| /* |
| * SPDX-License-Identifier: BSD-3-Clause |
| * SPDX-FileCopyrightText: Copyright TF-RMM Contributors. |
| */ |
| |
| #include <attestation.h> |
| #include <debug.h> |
| #include <granule.h> |
| #include <measurement.h> |
| #include <realm.h> |
| #include <rsi-handler.h> |
| #include <smc-rsi.h> |
| #include <smc.h> |
| #include <string.h> |
| #include <utils_def.h> |
| |
| #define MAX_EXTENDED_SIZE (64U) |
| |
| /* |
| * Return the Realm Personalization Value. |
| * |
| * Arguments: |
| * rd - The Realm descriptor. |
| * claim_ptr - The start address of the Realm Personalization Value claim |
| * claim_len - The length of the Realm Personalization Value claim |
| */ |
| static void get_rpv(struct rd *rd, void **claim_ptr, size_t *claim_len) |
| { |
| *claim_ptr = (uint8_t *)&(rd->rpv[0]); |
| *claim_len = RPV_SIZE; |
| } |
| |
| /* |
| * Save the input parameters in the context for later iterations to check for |
| * consistency. |
| */ |
| static void save_input_parameters(struct rec *rec) |
| { |
| struct rec_attest_data *attest_data = rec->aux_data.attest_data; |
| |
| attest_data->token_sign_ctx.token_ipa = rec->regs[1]; |
| (void)memcpy(attest_data->token_sign_ctx.challenge, &rec->regs[2], |
| ATTEST_CHALLENGE_SIZE); |
| } |
| |
| /* |
| * Verify that in all the iterations the input parameters are the same |
| * as in the initial call. |
| */ |
| static bool verify_input_parameters_consistency(struct rec *rec) |
| { |
| struct rec_attest_data *attest_data = rec->aux_data.attest_data; |
| |
| return attest_data->token_sign_ctx.token_ipa == rec->regs[1]; |
| } |
| |
| /* |
| * Function to continue with the sign operation |
| */ |
| static void attest_token_continue_sign_state( |
| struct rec_attest_data *attest_data, |
| struct rsi_result *res) |
| { |
| /* |
| * Sign and finish creating the token. |
| */ |
| enum attest_token_err_t ret = |
| attest_realm_token_sign(&(attest_data->token_sign_ctx.ctx), |
| &(attest_data->rmm_realm_token_len)); |
| |
| if ((ret == ATTEST_TOKEN_ERR_COSE_SIGN_IN_PROGRESS) || |
| (ret == ATTEST_TOKEN_ERR_SUCCESS)) { |
| /* |
| * Return to RSI handler function after each iteration |
| * to check is there anything else to do (pending IRQ) |
| * or next signing iteration can be executed. |
| */ |
| res->smc_res.x[0] = RSI_INCOMPLETE; |
| |
| /* If this was the last signing cycle */ |
| if (ret == ATTEST_TOKEN_ERR_SUCCESS) { |
| attest_data->token_sign_ctx.state = |
| ATTEST_SIGN_TOKEN_WRITE_IN_PROGRESS; |
| } |
| } else { |
| /* Accessible only in case of failure during token signing */ |
| ERROR("FATAL_ERROR: Realm token creation failed\n"); |
| panic(); |
| } |
| } |
| |
| /* |
| * Function to continue with the token write operation |
| */ |
| static void attest_token_continue_write_state(struct rec *rec, |
| struct rsi_result *res) |
| { |
| struct granule *gr; |
| uint8_t *realm_att_token; |
| unsigned long realm_att_token_ipa = rec->regs[1]; |
| enum s2_walk_status walk_status; |
| struct s2_walk_result walk_res = { 0UL }; |
| size_t attest_token_len; |
| struct rec_attest_data *attest_data = rec->aux_data.attest_data; |
| |
| /* |
| * Translate realm granule IPA to PA. If returns with |
| * WALK_SUCCESS then the last level page table (llt), |
| * which holds the realm_att_token_buf mapping, is locked. |
| */ |
| walk_status = realm_ipa_to_pa(rec, realm_att_token_ipa, &walk_res); |
| |
| /* Walk parameter validity was checked by RSI_ATTESTATION_TOKEN_INIT */ |
| assert(walk_status != WALK_INVALID_PARAMS); |
| |
| if (walk_status == WALK_FAIL) { |
| if (walk_res.ripas_val == RIPAS_EMPTY) { |
| res->smc_res.x[0] = RSI_ERROR_INPUT; |
| } else { |
| /* |
| * Translation failed, IPA is not mapped. |
| * Return to NS host to fix the issue. |
| */ |
| res->action = STAGE_2_TRANSLATION_FAULT; |
| res->rtt_level = walk_res.rtt_level; |
| } |
| return; |
| } |
| |
| /* Map realm data granule to RMM address space */ |
| gr = find_granule(walk_res.pa); |
| realm_att_token = granule_map(gr, SLOT_RSI_CALL); |
| |
| attest_token_len = attest_cca_token_create(realm_att_token, |
| ATTEST_TOKEN_BUFFER_SIZE, |
| &attest_data->rmm_realm_token_buf, |
| attest_data->rmm_realm_token_len); |
| |
| /* Unmap realm granule */ |
| buffer_unmap(realm_att_token); |
| |
| /* Unlock last level page table (walk_res.g_llt) */ |
| granule_unlock(walk_res.llt); |
| |
| /* Write output parameters */ |
| if (attest_token_len == 0) { |
| res->smc_res.x[0] = RSI_ERROR_INPUT; |
| } else { |
| res->smc_res.x[0] = RSI_SUCCESS; |
| res->smc_res.x[1] = attest_token_len; |
| } |
| |
| /* The signing has either succeeded or failed. Reset the state. */ |
| attest_data->token_sign_ctx.state = ATTEST_SIGN_NOT_STARTED; |
| } |
| |
| void handle_rsi_attest_token_init(struct rec *rec, struct rsi_result *res) |
| { |
| struct rd *rd = NULL; |
| unsigned long realm_buf_ipa; |
| struct rec_attest_data *attest_data; |
| void *rpv_ptr; |
| size_t rpv_len; |
| int att_ret; |
| |
| assert(rec != NULL); |
| |
| realm_buf_ipa = rec->regs[1]; |
| attest_data = rec->aux_data.attest_data; |
| |
| res->action = UPDATE_REC_RETURN_TO_REALM; |
| |
| /* |
| * Calling RSI_ATTESTATION_TOKEN_INIT any time aborts any ongoing |
| * operation. |
| * TODO: This can be moved to attestation lib |
| */ |
| if (attest_data->token_sign_ctx.state != ATTEST_SIGN_NOT_STARTED) { |
| int restart; |
| |
| attest_data->token_sign_ctx.state = ATTEST_SIGN_NOT_STARTED; |
| restart = attestation_heap_reinit_pe(rec->aux_data.attest_heap_buf, |
| REC_HEAP_SIZE); |
| if (restart != 0) { |
| /* There is no provision for this failure so panic */ |
| panic(); |
| } |
| } |
| |
| if (!GRANULE_ALIGNED(realm_buf_ipa)) { |
| res->smc_res.x[0] = RSI_ERROR_INPUT; |
| return; |
| } |
| |
| /* |
| * rd lock is acquired so that measurement cannot be updated |
| * simultaneously by another rec |
| */ |
| granule_lock(rec->realm_info.g_rd, GRANULE_STATE_RD); |
| rd = granule_map(rec->realm_info.g_rd, SLOT_RD); |
| if (!addr_in_par(rd, realm_buf_ipa)) { |
| res->smc_res.x[0] = RSI_ERROR_INPUT; |
| goto out_unmap_rd; |
| } |
| |
| /* |
| * Save the input parameters in the context for later iterations |
| * to check. |
| */ |
| save_input_parameters(rec); |
| |
| get_rpv(rd, &rpv_ptr, &rpv_len); |
| att_ret = attest_realm_token_create(rd->algorithm, rd->measurement, |
| MEASUREMENT_SLOT_NR, |
| rpv_ptr, |
| rpv_len, |
| &attest_data->token_sign_ctx, |
| attest_data->rmm_realm_token_buf, |
| sizeof(attest_data->rmm_realm_token_buf)); |
| if (att_ret != 0) { |
| ERROR("FATAL_ERROR: Realm token creation failed\n"); |
| panic(); |
| } |
| |
| attest_data->token_sign_ctx.state = ATTEST_SIGN_IN_PROGRESS; |
| res->smc_res.x[0] = RSI_SUCCESS; |
| |
| out_unmap_rd: |
| buffer_unmap(rd); |
| granule_unlock(rec->realm_info.g_rd); |
| } |
| |
| /* |
| * Return 'false' if no IRQ is pending, |
| * return 'true' if there is an IRQ pending, and need to return to Host. |
| */ |
| static bool check_pending_irq(void) |
| { |
| return (read_isr_el1() != 0UL); |
| } |
| |
| void handle_rsi_attest_token_continue(struct rec *rec, |
| struct rmi_rec_exit *rec_exit, |
| struct rsi_result *res) |
| { |
| struct rec_attest_data *attest_data; |
| |
| assert(rec != NULL); |
| assert(rec_exit != NULL); |
| |
| attest_data = rec->aux_data.attest_data; |
| res->action = UPDATE_REC_RETURN_TO_REALM; |
| |
| if (!verify_input_parameters_consistency(rec)) { |
| res->smc_res.x[0] = RSI_ERROR_INPUT; |
| return; |
| } |
| |
| while (true) { |
| switch (attest_data->token_sign_ctx.state) { |
| case ATTEST_SIGN_NOT_STARTED: |
| /* |
| * Before this call the initial attestation token call |
| * (SMC_RSI_ATTEST_TOKEN_INIT) must have been executed |
| * successfully. |
| */ |
| res->smc_res.x[0] = RSI_ERROR_STATE; |
| break; |
| case ATTEST_SIGN_IN_PROGRESS: |
| attest_token_continue_sign_state(attest_data, res); |
| break; |
| case ATTEST_SIGN_TOKEN_WRITE_IN_PROGRESS: |
| attest_token_continue_write_state(rec, res); |
| break; |
| default: |
| /* Any other state is considered an error */ |
| panic(); |
| } |
| |
| if (res->smc_res.x[0] == RSI_INCOMPLETE) { |
| if (check_pending_irq()) { |
| res->action = UPDATE_REC_EXIT_TO_HOST; |
| res->smc_res.x[0] = RSI_INCOMPLETE; |
| rec_exit->exit_reason = RMI_EXIT_IRQ; |
| break; |
| } |
| if ((res->action & FLAG_EXIT_TO_HOST) != 0) { |
| break; |
| } |
| } else { |
| break; |
| } |
| } |
| } |
| |
| void handle_rsi_measurement_extend(struct rec *rec, struct rsi_result *res) |
| { |
| struct granule *g_rd; |
| struct rd *rd; |
| unsigned long index; |
| unsigned long rd_addr; |
| size_t size; |
| void *extend_measurement; |
| unsigned char *current_measurement; |
| int __unused meas_ret; |
| |
| assert(rec != NULL); |
| |
| res->action = UPDATE_REC_RETURN_TO_REALM; |
| |
| /* |
| * rd lock is acquired so that measurement cannot be updated |
| * simultaneously by another rec |
| */ |
| rd_addr = granule_addr(rec->realm_info.g_rd); |
| g_rd = find_lock_granule(rd_addr, GRANULE_STATE_RD); |
| |
| assert(g_rd != NULL); |
| |
| rd = granule_map(rec->realm_info.g_rd, SLOT_RD); |
| |
| /* |
| * X1: index |
| * X2: size |
| * X3-X10: measurement value |
| */ |
| index = rec->regs[1]; |
| |
| if ((index == RIM_MEASUREMENT_SLOT) || |
| (index >= MEASUREMENT_SLOT_NR)) { |
| res->smc_res.x[0] = RSI_ERROR_INPUT; |
| goto out_unmap_rd; |
| } |
| |
| size = rec->regs[2]; |
| |
| if (size > MAX_EXTENDED_SIZE) { |
| res->smc_res.x[0] = RSI_ERROR_INPUT; |
| goto out_unmap_rd; |
| } |
| |
| extend_measurement = &rec->regs[3]; |
| current_measurement = rd->measurement[index]; |
| |
| measurement_extend(rd->algorithm, |
| current_measurement, |
| extend_measurement, |
| size, |
| current_measurement); |
| |
| res->smc_res.x[0] = RSI_SUCCESS; |
| |
| out_unmap_rd: |
| buffer_unmap(rd); |
| granule_unlock(g_rd); |
| } |
| |
| void handle_rsi_measurement_read(struct rec *rec, struct rsi_result *res) |
| { |
| struct rd *rd; |
| unsigned long idx; |
| size_t measurement_size; |
| |
| assert(rec != NULL); |
| |
| res->action = UPDATE_REC_RETURN_TO_REALM; |
| |
| /* X1: Index */ |
| idx = rec->regs[1]; |
| |
| if (idx >= MEASUREMENT_SLOT_NR) { |
| res->smc_res.x[0] = RSI_ERROR_INPUT; |
| return; |
| } |
| |
| /* |
| * rd lock is acquired so that measurement cannot be updated |
| * simultaneously by another rec |
| */ |
| granule_lock(rec->realm_info.g_rd, GRANULE_STATE_RD); |
| rd = granule_map(rec->realm_info.g_rd, SLOT_RD); |
| |
| measurement_size = measurement_get_size(rd->algorithm); |
| |
| (void)memcpy(&rec->regs[1], rd->measurement[idx], measurement_size); |
| |
| /* Zero-initialize the unused area */ |
| if (measurement_size < MAX_MEASUREMENT_SIZE) { |
| (void)memset((char *)(&rec->regs[1]) + measurement_size, |
| 0, MAX_MEASUREMENT_SIZE - measurement_size); |
| } |
| |
| buffer_unmap(rd); |
| granule_unlock(rec->realm_info.g_rd); |
| |
| res->smc_res.x[0] = RSI_SUCCESS; |
| } |