| /* |
| * Copyright (c) 2025, Arm Limited. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <errno.h> |
| |
| #include <plat/common/platform.h> |
| #include <services/bl31_lfa.h> |
| #include <services/lfa_svc.h> |
| #include <services/rmmd_rmm_lfa.h> |
| #include <smccc_helpers.h> |
| |
| static uint32_t lfa_component_count; |
| static plat_lfa_component_info_t *lfa_components; |
| static struct lfa_component_status current_activation; |
| static bool is_lfa_initialized; |
| |
| void lfa_reset_activation(void) |
| { |
| current_activation.component_id = LFA_INVALID_COMPONENT; |
| current_activation.prime_status = PRIME_NONE; |
| current_activation.cpu_rendezvous_required = false; |
| } |
| |
| static int convert_to_lfa_error(int ret) |
| { |
| switch (ret) { |
| case 0: |
| return LFA_SUCCESS; |
| case -EAUTH: |
| return LFA_AUTH_ERROR; |
| case -ENOMEM: |
| return LFA_NO_MEMORY; |
| default: |
| return LFA_DEVICE_ERROR; |
| } |
| } |
| |
| static bool lfa_initialize_components(void) |
| { |
| lfa_component_count = plat_lfa_get_components(&lfa_components); |
| |
| if (lfa_component_count == 0U || lfa_components == NULL) { |
| /* unlikely to reach here */ |
| ERROR("Invalid LFA component setup: count = 0 or components are NULL"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static uint64_t get_fw_activation_flags(uint32_t fw_seq_id) |
| { |
| const plat_lfa_component_info_t *comp = |
| &lfa_components[fw_seq_id]; |
| uint64_t flags = 0ULL; |
| |
| flags |= ((comp->activator == NULL ? 0ULL : 1ULL) |
| << LFA_ACTIVATION_CAPABLE_SHIFT); |
| flags |= (uint64_t)(comp->activation_pending) |
| << LFA_ACTIVATION_PENDING_SHIFT; |
| |
| if (comp->activator != NULL) { |
| flags |= ((comp->activator->may_reset_cpu ? 1ULL : 0ULL) |
| << LFA_MAY_RESET_CPU_SHIFT); |
| flags |= ((comp->activator->cpu_rendezvous_required ? 0ULL : 1ULL) |
| << LFA_CPU_RENDEZVOUS_OPTIONAL_SHIFT); |
| } |
| |
| return flags; |
| } |
| |
| static int lfa_cancel(uint32_t component_id) |
| { |
| int ret = LFA_SUCCESS; |
| |
| if (lfa_component_count == 0U) { |
| return LFA_WRONG_STATE; |
| } |
| |
| /* Check if component ID is in range. */ |
| if ((component_id >= lfa_component_count) || |
| (component_id != current_activation.component_id)) { |
| return LFA_INVALID_PARAMETERS; |
| } |
| |
| ret = plat_lfa_cancel(component_id); |
| if (ret != LFA_SUCCESS) { |
| return LFA_BUSY; |
| } |
| |
| /* TODO: add proper termination prime and activate phases */ |
| lfa_reset_activation(); |
| |
| return ret; |
| } |
| |
| static int lfa_activate(uint32_t component_id, uint64_t flags, |
| uint64_t ep_address, uint64_t context_id) |
| { |
| int ret = LFA_ACTIVATION_FAILED; |
| struct lfa_component_ops *activator; |
| |
| if ((lfa_component_count == 0U) || |
| (!lfa_components[component_id].activation_pending) || |
| (current_activation.prime_status != PRIME_COMPLETE)) { |
| return LFA_COMPONENT_WRONG_STATE; |
| } |
| |
| /* Check if fw_seq_id is in range. */ |
| if ((component_id >= lfa_component_count) || |
| (current_activation.component_id != component_id)) { |
| return LFA_INVALID_PARAMETERS; |
| } |
| |
| if (lfa_components[component_id].activator == NULL) { |
| return LFA_NOT_SUPPORTED; |
| } |
| |
| activator = lfa_components[component_id].activator; |
| if (activator->activate != NULL) { |
| /* |
| * Pass skip_cpu_rendezvous (flag[0]) only if flag[0]==1 |
| * & CPU_RENDEZVOUS is not required. |
| */ |
| if (flags & LFA_SKIP_CPU_RENDEZVOUS_BIT) { |
| if (!activator->cpu_rendezvous_required) { |
| INFO("Skipping rendezvous requested by caller.\n"); |
| current_activation.cpu_rendezvous_required = false; |
| } |
| /* |
| * Return error if caller tries to skip rendezvous when |
| * it is required. |
| */ |
| else { |
| ERROR("CPU Rendezvous is required, can't skip.\n"); |
| return LFA_INVALID_PARAMETERS; |
| } |
| } |
| |
| ret = activator->activate(¤t_activation, ep_address, |
| context_id); |
| } |
| |
| lfa_components[component_id].activation_pending = false; |
| |
| return ret; |
| } |
| |
| static int lfa_prime(uint32_t component_id, uint64_t *flags) |
| { |
| int ret = LFA_SUCCESS; |
| struct lfa_component_ops *activator; |
| |
| if (lfa_component_count == 0U || |
| !lfa_components[component_id].activation_pending) { |
| return LFA_WRONG_STATE; |
| } |
| |
| /* Check if fw_seq_id is in range. */ |
| if (component_id >= lfa_component_count) { |
| return LFA_INVALID_PARAMETERS; |
| } |
| |
| if (lfa_components[component_id].activator == NULL) { |
| return LFA_NOT_SUPPORTED; |
| } |
| |
| switch (current_activation.prime_status) { |
| case PRIME_NONE: |
| current_activation.component_id = component_id; |
| current_activation.prime_status = PRIME_STARTED; |
| break; |
| |
| case PRIME_STARTED: |
| if (current_activation.component_id != component_id) { |
| /* Mismatched component trying to continue PRIME - error */ |
| return LFA_WRONG_STATE; |
| } |
| break; |
| |
| case PRIME_COMPLETE: |
| default: |
| break; |
| } |
| |
| ret = plat_lfa_load_auth_image(component_id); |
| ret = convert_to_lfa_error(ret); |
| |
| activator = lfa_components[component_id].activator; |
| if (activator->prime != NULL) { |
| ret = activator->prime(¤t_activation); |
| if (ret != LFA_SUCCESS) { |
| /* |
| * TODO: it should be LFA_PRIME_FAILED but specification |
| * has not define this error yet |
| */ |
| return ret; |
| } |
| } |
| |
| current_activation.prime_status = PRIME_COMPLETE; |
| |
| /* TODO: split this into multiple PRIME calls */ |
| *flags = 0ULL; |
| |
| return ret; |
| } |
| |
| int lfa_setup(void) |
| { |
| is_lfa_initialized = lfa_initialize_components(); |
| if (!is_lfa_initialized) { |
| return -1; |
| } |
| |
| lfa_reset_activation(); |
| |
| return 0; |
| } |
| |
| uint64_t lfa_smc_handler(uint32_t 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) |
| { |
| uint64_t retx1, retx2; |
| uint64_t lfa_flags; |
| uint8_t *uuid_p; |
| uint32_t fw_seq_id = (uint32_t)x1; |
| int ret; |
| |
| /** |
| * TODO: Acquire serialization lock. |
| */ |
| |
| if (!is_lfa_initialized) { |
| return LFA_NOT_SUPPORTED; |
| } |
| |
| switch (smc_fid) { |
| case LFA_VERSION: |
| SMC_RET1(handle, LFA_VERSION_VAL); |
| break; |
| |
| case LFA_FEATURES: |
| SMC_RET1(handle, is_lfa_fid(x1) ? LFA_SUCCESS : LFA_NOT_SUPPORTED); |
| break; |
| |
| case LFA_GET_INFO: |
| /** |
| * The current specification limits this input parameter to be zero for |
| * version 1.0 of LFA |
| */ |
| if (x1 == 0ULL) { |
| SMC_RET3(handle, LFA_SUCCESS, lfa_component_count, 0); |
| } else { |
| SMC_RET1(handle, LFA_INVALID_PARAMETERS); |
| } |
| break; |
| |
| case LFA_GET_INVENTORY: |
| if (lfa_component_count == 0U) { |
| SMC_RET1(handle, LFA_WRONG_STATE); |
| } |
| |
| /* |
| * Check if fw_seq_id is in range. LFA_GET_INFO must be called first to scan |
| * platform firmware and create a valid number of firmware components. |
| */ |
| if (fw_seq_id >= lfa_component_count) { |
| SMC_RET1(handle, LFA_INVALID_PARAMETERS); |
| } |
| |
| /* |
| * grab the UUID of asked fw_seq_id and set the return UUID |
| * variables |
| */ |
| uuid_p = (uint8_t *)&lfa_components[fw_seq_id].uuid; |
| memcpy(&retx1, uuid_p, sizeof(uint64_t)); |
| memcpy(&retx2, uuid_p + sizeof(uint64_t), sizeof(uint64_t)); |
| |
| /* |
| * check the given fw_seq_id's update available |
| * and accordingly set the active_pending flag |
| */ |
| lfa_components[fw_seq_id].activation_pending = |
| is_plat_lfa_activation_pending(fw_seq_id); |
| |
| INFO("Component %lu %s live activation:\n", x1, |
| lfa_components[fw_seq_id].activator ? "supports" : |
| "does not support"); |
| |
| if (lfa_components[fw_seq_id].activator != NULL) { |
| INFO("Activation pending: %s\n", |
| lfa_components[fw_seq_id].activation_pending ? "true" : "false"); |
| } |
| |
| INFO("x1 = 0x%016lx, x2 = 0x%016lx\n", retx1, retx2); |
| |
| SMC_RET4(handle, LFA_SUCCESS, retx1, retx2, get_fw_activation_flags(fw_seq_id)); |
| |
| break; |
| |
| case LFA_PRIME: |
| ret = lfa_prime(x1, &lfa_flags); |
| if (ret != LFA_SUCCESS) { |
| SMC_RET1(handle, ret); |
| } else { |
| SMC_RET2(handle, ret, lfa_flags); |
| } |
| break; |
| |
| case LFA_ACTIVATE: |
| ret = lfa_activate(fw_seq_id, x2, x3, x4); |
| /* TODO: implement activate again */ |
| SMC_RET2(handle, ret, 0ULL); |
| |
| break; |
| |
| case LFA_CANCEL: |
| ret = lfa_cancel(x1); |
| SMC_RET1(handle, ret); |
| break; |
| |
| default: |
| WARN("Unimplemented LFA Service Call: 0x%x\n", smc_fid); |
| SMC_RET1(handle, SMC_UNK); |
| break; /* unreachable */ |
| |
| } |
| |
| SMC_RET1(handle, SMC_UNK); |
| |
| return 0; |
| } |