blob: 1cf65ae6358d0dc04f4b43f5fda4bdafc9be1532 [file] [log] [blame]
/*
* 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(&current_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(&current_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;
}