GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -0,0 +1,107 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <common/debug.h>
#include <services/el3_spmc_logical_sp.h>
#include <services/ffa_svc.h>
#include "spmc.h"
/*******************************************************************************
* Validate any logical partition descriptors before we initialise.
* Initialization of said partitions will be taken care of during SPMC boot.
******************************************************************************/
int el3_sp_desc_validate(void)
{
struct el3_lp_desc *lp_array;
/*
* Assert the number of descriptors is less than maximum allowed.
* This constant should be define on a per platform basis.
*/
assert(EL3_LP_DESCS_COUNT <= MAX_EL3_LP_DESCS_COUNT);
/* Check the array bounds are valid. */
assert(EL3_LP_DESCS_END >= EL3_LP_DESCS_START);
/* If no logical partitions are implemented then simply bail out. */
if (EL3_LP_DESCS_COUNT == 0U) {
return 0;
}
lp_array = get_el3_lp_array();
for (unsigned int index = 0; index < EL3_LP_DESCS_COUNT; index++) {
struct el3_lp_desc *lp_desc = &lp_array[index];
/* Validate our logical partition descriptors. */
if (lp_desc == NULL) {
ERROR("Invalid Logical SP Descriptor\n");
return -EINVAL;
}
/*
* Ensure the ID follows the convention to indidate it resides
* in the secure world.
*/
if (!ffa_is_secure_world_id(lp_desc->sp_id)) {
ERROR("Invalid Logical SP ID (0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure we don't conflict with the SPMC partition ID. */
if (lp_desc->sp_id == FFA_SPMC_ID) {
ERROR("Logical SP ID clashes with SPMC ID(0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure the UUID is not the NULL UUID. */
if (lp_desc->uuid[0] == 0 && lp_desc->uuid[1] == 0 &&
lp_desc->uuid[2] == 0 && lp_desc->uuid[3] == 0) {
ERROR("Invalid UUID for Logical SP (0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure init function callback is registered. */
if (lp_desc->init == NULL) {
ERROR("Missing init function for Logical SP(0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure that LP only supports receiving direct requests. */
if (lp_desc->properties &
~(FFA_PARTITION_DIRECT_REQ_RECV)) {
ERROR("Invalid partition properties (0x%x)\n",
lp_desc->properties);
return -EINVAL;
}
/* Ensure direct request function callback is registered. */
if (lp_desc->direct_req == NULL) {
ERROR("No Direct Req handler for Logical SP (0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
/* Ensure that all partition IDs are unique. */
for (unsigned int inner_idx = index + 1;
inner_idx < EL3_LP_DESCS_COUNT; inner_idx++) {
if (lp_desc->sp_id == lp_array[inner_idx].sp_id) {
ERROR("Duplicate SP ID Detected (0x%x)\n",
lp_desc->sp_id);
return -EINVAL;
}
}
}
return 0;
}
@@ -0,0 +1,296 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SPMC_H
#define SPMC_H
#include <stdint.h>
#include <common/bl_common.h>
#include <lib/psci/psci.h>
#include <lib/spinlock.h>
#include <services/el3_spmc_logical_sp.h>
#include "spm_common.h"
/*
* Ranges of FF-A IDs for Normal world and Secure world components. The
* convention matches that used by other SPMCs i.e. Hafnium and OP-TEE.
*/
#define FFA_NWD_ID_BASE 0x0
#define FFA_NWD_ID_LIMIT 0x7FFF
#define FFA_SWD_ID_BASE 0x8000
#define FFA_SWD_ID_LIMIT SPMD_DIRECT_MSG_ENDPOINT_ID - 1
#define FFA_SWD_ID_MASK 0x8000
/* ID 0 is reserved for the normal world entity, (Hypervisor or OS Kernel). */
#define FFA_NWD_ID U(0)
/* First ID is reserved for the SPMC */
#define FFA_SPMC_ID U(FFA_SWD_ID_BASE)
/* SP IDs are allocated after the SPMC ID */
#define FFA_SP_ID_BASE (FFA_SPMC_ID + 1)
/* Align with Hafnium implementation */
#define INV_SP_ID 0x7FFF
/* FF-A Related helper macros. */
#define FFA_ID_MASK U(0xFFFF)
#define FFA_PARTITION_ID_SHIFT U(16)
#define FFA_FEATURES_BIT31_MASK U(0x1u << 31)
#define FFA_FEATURES_RET_REQ_NS_BIT U(0x1 << 1)
#define FFA_RUN_EP_ID(ep_vcpu_ids) \
((ep_vcpu_ids >> FFA_PARTITION_ID_SHIFT) & FFA_ID_MASK)
#define FFA_RUN_VCPU_ID(ep_vcpu_ids) \
(ep_vcpu_ids & FFA_ID_MASK)
#define FFA_PAGE_SIZE (4096)
#define FFA_RXTX_PAGE_COUNT_MASK 0x1F
/* Ensure that the page size used by TF-A is 4k aligned. */
CASSERT((PAGE_SIZE % FFA_PAGE_SIZE) == 0, assert_aligned_page_size);
/*
* Defines to allow an SP to subscribe for power management messages
*/
#define FFA_PM_MSG_SUB_CPU_OFF U(1 << 0)
#define FFA_PM_MSG_SUB_CPU_SUSPEND U(1 << 1)
#define FFA_PM_MSG_SUB_CPU_SUSPEND_RESUME U(1 << 2)
/*
* Runtime states of an execution context as per the FF-A v1.1 specification.
*/
enum sp_runtime_states {
RT_STATE_WAITING,
RT_STATE_RUNNING,
RT_STATE_PREEMPTED,
RT_STATE_BLOCKED
};
/*
* Runtime model of an execution context as per the FF-A v1.1 specification. Its
* value is valid only if the execution context is not in the waiting state.
*/
enum sp_runtime_model {
RT_MODEL_DIR_REQ,
RT_MODEL_RUN,
RT_MODEL_INIT,
RT_MODEL_INTR
};
enum sp_runtime_el {
EL1 = 0,
S_EL0,
S_EL1
};
enum sp_execution_state {
SP_STATE_AARCH64 = 0,
SP_STATE_AARCH32
};
enum mailbox_state {
/* There is no message in the mailbox. */
MAILBOX_STATE_EMPTY,
/* There is a message that has been populated in the mailbox. */
MAILBOX_STATE_FULL,
};
struct mailbox {
enum mailbox_state state;
/* RX/TX Buffers. */
void *rx_buffer;
const void *tx_buffer;
/* Size of RX/TX Buffer. */
uint32_t rxtx_page_count;
/* Lock access to mailbox. */
spinlock_t lock;
};
/*
* Execution context members for an SP. This is a bit like struct
* vcpu in a hypervisor.
*/
struct sp_exec_ctx {
/*
* Store the stack address to restore C runtime context from after
* returning from a synchronous entry into the SP.
*/
uint64_t c_rt_ctx;
/* Space to maintain the architectural state of an SP. */
cpu_context_t cpu_ctx;
/* Track the current runtime state of the SP. */
enum sp_runtime_states rt_state;
/* Track the current runtime model of the SP. */
enum sp_runtime_model rt_model;
};
/*
* Structure to describe the cumulative properties of an SP.
*/
struct secure_partition_desc {
/*
* Execution contexts allocated to this endpoint. Ideally,
* we need as many contexts as there are physical cpus only
* for a S-EL1 SP which is MP-pinned.
*/
struct sp_exec_ctx ec[PLATFORM_CORE_COUNT];
/* ID of the Secure Partition. */
uint16_t sp_id;
/* Runtime EL. */
enum sp_runtime_el runtime_el;
/* Partition UUID. */
uint32_t uuid[4];
/* Partition Properties. */
uint32_t properties;
/* Supported FF-A Version. */
uint32_t ffa_version;
/* Execution State. */
enum sp_execution_state execution_state;
/* Mailbox tracking. */
struct mailbox mailbox;
/* Secondary entrypoint. Only valid for a S-EL1 SP. */
uintptr_t secondary_ep;
/*
* Store whether the SP has subscribed to any power management messages.
*/
uint16_t pwr_mgmt_msgs;
/*
* Store whether the SP has requested the use of the NS bit for memory
* management transactions if it is using FF-A v1.0.
*/
bool ns_bit_requested;
};
/*
* This define identifies the only SP that will be initialised and participate
* in FF-A communication. The implementation leaves the door open for more SPs
* to be managed in future but for now it is reasonable to assume that either a
* single S-EL0 or a single S-EL1 SP will be supported. This define will be used
* to identify which SP descriptor to initialise and manage during SP runtime.
*/
#define ACTIVE_SP_DESC_INDEX 0
/*
* Structure to describe the cumulative properties of the Hypervisor and
* NS-Endpoints.
*/
struct ns_endpoint_desc {
/*
* ID of the NS-Endpoint or Hypervisor.
*/
uint16_t ns_ep_id;
/*
* Mailbox tracking.
*/
struct mailbox mailbox;
/*
* Supported FF-A Version
*/
uint32_t ffa_version;
};
/**
* Holds information returned for each partition by the FFA_PARTITION_INFO_GET
* interface.
*/
struct ffa_partition_info_v1_0 {
uint16_t ep_id;
uint16_t execution_ctx_count;
uint32_t properties;
};
/* Extended structure for v1.1. */
struct ffa_partition_info_v1_1 {
uint16_t ep_id;
uint16_t execution_ctx_count;
uint32_t properties;
uint32_t uuid[4];
};
/* Reference to power management hooks */
extern const spd_pm_ops_t spmc_pm;
/* Setup Function for different SP types. */
void spmc_sp_common_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info,
int32_t boot_info_reg);
void spmc_el1_sp_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info);
void spmc_sp_common_ep_commit(struct secure_partition_desc *sp,
entry_point_info_t *ep_info);
/*
* Helper function to perform a synchronous entry into a SP.
*/
uint64_t spmc_sp_synchronous_entry(struct sp_exec_ctx *ec);
/*
* Helper function to obtain the descriptor of the current SP on a physical cpu.
*/
struct secure_partition_desc *spmc_get_current_sp_ctx(void);
/*
* Helper function to obtain the execution context of an SP on a
* physical cpu.
*/
struct sp_exec_ctx *spmc_get_sp_ec(struct secure_partition_desc *sp);
/*
* Helper function to obtain the index of the execution context of an SP on a
* physical cpu.
*/
unsigned int get_ec_index(struct secure_partition_desc *sp);
uint64_t spmc_ffa_error_return(void *handle, int error_code);
/*
* Ensure a partition ID does not clash and follows the secure world convention.
*/
bool is_ffa_secure_id_valid(uint16_t partition_id);
/*
* Helper function to obtain the array storing the EL3
* Logical Partition descriptors.
*/
struct el3_lp_desc *get_el3_lp_array(void);
/*
* Helper function to obtain the RX/TX buffer pair descriptor of the Hypervisor
* or OS kernel in the normal world or the last SP that was run.
*/
struct mailbox *spmc_get_mbox_desc(bool secure_origin);
/*
* Helper function to obtain the context of an SP with a given partition ID.
*/
struct secure_partition_desc *spmc_get_sp_ctx(uint16_t id);
/*
* Add helper function to obtain the FF-A version of the calling
* partition.
*/
uint32_t get_partition_ffa_version(bool secure_origin);
#endif /* SPMC_H */
@@ -0,0 +1,44 @@
#
# Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
ifneq (${ARCH},aarch64)
$(error "Error: SPMC is only supported on aarch64.")
endif
SPMC_SOURCES := $(addprefix services/std_svc/spm/el3_spmc/, \
spmc_main.c \
spmc_setup.c \
logical_sp.c \
spmc_pm.c \
spmc_shared_mem.c)
# Specify platform specific logical partition implementation.
SPMC_LP_SOURCES := $(addprefix ${PLAT_DIR}/, \
${PLAT}_el3_spmc_logical_sp.c)
SPMC_SOURCES += $(SPMC_LP_SOURCES)
# Let the top-level Makefile know that we intend to include a BL32 image
NEED_BL32 := yes
ifndef BL32
# The SPMC is paired with a Test Secure Payload source and we intend to
# build the Test Secure Payload if no other image has been provided
# for BL32.
#
# In cases where an associated Secure Payload lies outside this build
# system/source tree, the dispatcher Makefile can either invoke an external
# build command or assume it is pre-built.
BL32_ROOT := bl32/tsp
# Conditionally include SP's Makefile. The assumption is that the TSP's build
# system is compatible with that of Trusted Firmware, and it'll add and populate
# necessary build targets and variables.
include ${BL32_ROOT}/tsp.mk
endif
@@ -0,0 +1,283 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <errno.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/spinlock.h>
#include <plat/common/common_def.h>
#include <plat/common/platform.h>
#include <services/ffa_svc.h>
#include "spmc.h"
#include <platform_def.h>
/*******************************************************************************
* spmc_build_pm_message
*
* Builds an SPMC to SP direct message request.
******************************************************************************/
static void spmc_build_pm_message(gp_regs_t *gpregs,
unsigned long long message,
uint8_t pm_msg_type,
uint16_t sp_id)
{
write_ctx_reg(gpregs, CTX_GPREG_X0, FFA_MSG_SEND_DIRECT_REQ_SMC32);
write_ctx_reg(gpregs, CTX_GPREG_X1,
(FFA_SPMC_ID << FFA_DIRECT_MSG_SOURCE_SHIFT) |
sp_id);
write_ctx_reg(gpregs, CTX_GPREG_X2, FFA_FWK_MSG_BIT |
(pm_msg_type & FFA_FWK_MSG_MASK));
write_ctx_reg(gpregs, CTX_GPREG_X3, message);
}
/*******************************************************************************
* This CPU has been turned on. Enter the SP to initialise S-EL1.
******************************************************************************/
static void spmc_cpu_on_finish_handler(u_register_t unused)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
struct sp_exec_ctx *ec;
unsigned int linear_id = plat_my_core_pos();
entry_point_info_t sec_ec_ep_info = {0};
uint64_t rc;
/* Sanity check for a NULL pointer dereference. */
assert(sp != NULL);
/* Initialize entry point information for the SP. */
SET_PARAM_HEAD(&sec_ec_ep_info, PARAM_EP, VERSION_1,
SECURE | EP_ST_ENABLE);
/*
* Check if the primary execution context registered an entry point else
* bail out early.
* TODO: Add support for boot reason in manifest to allow jumping to
* entrypoint into the primary execution context.
*/
if (sp->secondary_ep == 0) {
WARN("%s: No secondary ep on core%u\n", __func__, linear_id);
return;
}
sec_ec_ep_info.pc = sp->secondary_ep;
/*
* Setup and initialise the SP execution context on this physical cpu.
*/
spmc_el1_sp_setup(sp, &sec_ec_ep_info);
spmc_sp_common_ep_commit(sp, &sec_ec_ep_info);
/* Obtain a reference to the SP execution context. */
ec = spmc_get_sp_ec(sp);
/*
* TODO: Should we do some PM related state tracking of the SP execution
* context here?
*/
/* Update the runtime model and state of the partition. */
ec->rt_model = RT_MODEL_INIT;
ec->rt_state = RT_STATE_RUNNING;
INFO("SP (0x%x) init start on core%u.\n", sp->sp_id, linear_id);
rc = spmc_sp_synchronous_entry(ec);
if (rc != 0ULL) {
ERROR("%s failed (%lu) on CPU%u\n", __func__, rc, linear_id);
}
/* Update the runtime state of the partition. */
ec->rt_state = RT_STATE_WAITING;
VERBOSE("CPU %u on!\n", linear_id);
}
/*******************************************************************************
* Helper function to send a FF-A power management message to an SP.
******************************************************************************/
static int32_t spmc_send_pm_msg(uint8_t pm_msg_type,
unsigned long long psci_event)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
struct sp_exec_ctx *ec;
gp_regs_t *gpregs_ctx;
unsigned int linear_id = plat_my_core_pos();
u_register_t resp;
uint64_t rc;
/* Obtain a reference to the SP execution context. */
ec = spmc_get_sp_ec(sp);
/*
* TODO: Should we do some PM related state tracking of the SP execution
* context here?
*/
/*
* Build an SPMC to SP direct message request.
* Note that x4-x6 should be populated with the original PSCI arguments.
*/
spmc_build_pm_message(get_gpregs_ctx(&ec->cpu_ctx),
psci_event,
pm_msg_type,
sp->sp_id);
/* Sanity check partition state. */
assert(ec->rt_state == RT_STATE_WAITING);
/* Update the runtime model and state of the partition. */
ec->rt_model = RT_MODEL_DIR_REQ;
ec->rt_state = RT_STATE_RUNNING;
rc = spmc_sp_synchronous_entry(ec);
if (rc != 0ULL) {
ERROR("%s failed (%lu) on CPU%u.\n", __func__, rc, linear_id);
assert(false);
return -EINVAL;
}
/*
* Validate we receive an expected response from the SP.
* TODO: We don't currently support aborting an SP in the scenario
* where it is misbehaving so assert these conditions are not
* met for now.
*/
gpregs_ctx = get_gpregs_ctx(&ec->cpu_ctx);
/* Expect a direct message response from the SP. */
resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X0);
if (resp != FFA_MSG_SEND_DIRECT_RESP_SMC32) {
ERROR("%s invalid SP response (%lx).\n", __func__, resp);
assert(false);
return -EINVAL;
}
/* Ensure the sender and receiver are populated correctly. */
resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X1);
if (!(ffa_endpoint_source(resp) == sp->sp_id &&
ffa_endpoint_destination(resp) == FFA_SPMC_ID)) {
ERROR("%s invalid src/dst response (%lx).\n", __func__, resp);
assert(false);
return -EINVAL;
}
/* Expect a PM message response from the SP. */
resp = read_ctx_reg(gpregs_ctx, CTX_GPREG_X2);
if ((resp & FFA_FWK_MSG_BIT) == 0U ||
((resp & FFA_FWK_MSG_MASK) != FFA_PM_MSG_PM_RESP)) {
ERROR("%s invalid PM response (%lx).\n", __func__, resp);
assert(false);
return -EINVAL;
}
/* Update the runtime state of the partition. */
ec->rt_state = RT_STATE_WAITING;
/* Return the status code returned by the SP */
return read_ctx_reg(gpregs_ctx, CTX_GPREG_X3);
}
/*******************************************************************************
* spmc_cpu_suspend_finish_handler
******************************************************************************/
static void spmc_cpu_suspend_finish_handler(u_register_t unused)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
unsigned int linear_id = plat_my_core_pos();
int32_t rc;
/* Sanity check for a NULL pointer dereference. */
assert(sp != NULL);
/*
* Check if the SP has subscribed for this power management message.
* If not then we don't have anything else to do here.
*/
if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_SUSPEND_RESUME) == 0U) {
goto exit;
}
rc = spmc_send_pm_msg(FFA_PM_MSG_WB_REQ, FFA_WB_TYPE_NOTS2RAM);
if (rc < 0) {
ERROR("%s failed (%d) on CPU%u\n", __func__, rc, linear_id);
return;
}
exit:
VERBOSE("CPU %u resumed!\n", linear_id);
}
/*******************************************************************************
* spmc_cpu_suspend_handler
******************************************************************************/
static void spmc_cpu_suspend_handler(u_register_t unused)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
unsigned int linear_id = plat_my_core_pos();
int32_t rc;
/* Sanity check for a NULL pointer dereference. */
assert(sp != NULL);
/*
* Check if the SP has subscribed for this power management message.
* If not then we don't have anything else to do here.
*/
if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_SUSPEND) == 0U) {
goto exit;
}
rc = spmc_send_pm_msg(FFA_FWK_MSG_PSCI, PSCI_CPU_SUSPEND_AARCH64);
if (rc < 0) {
ERROR("%s failed (%d) on CPU%u\n", __func__, rc, linear_id);
return;
}
exit:
VERBOSE("CPU %u suspend!\n", linear_id);
}
/*******************************************************************************
* spmc_cpu_off_handler
******************************************************************************/
static int32_t spmc_cpu_off_handler(u_register_t unused)
{
struct secure_partition_desc *sp = spmc_get_current_sp_ctx();
unsigned int linear_id = plat_my_core_pos();
int32_t ret = 0;
/* Sanity check for a NULL pointer dereference. */
assert(sp != NULL);
/*
* Check if the SP has subscribed for this power management message.
* If not then we don't have anything else to do here.
*/
if ((sp->pwr_mgmt_msgs & FFA_PM_MSG_SUB_CPU_OFF) == 0U) {
goto exit;
}
ret = spmc_send_pm_msg(FFA_FWK_MSG_PSCI, PSCI_CPU_OFF);
if (ret < 0) {
ERROR("%s failed (%d) on CPU%u\n", __func__, ret, linear_id);
return ret;
}
exit:
VERBOSE("CPU %u off!\n", linear_id);
return ret;
}
/*******************************************************************************
* Structure populated by the SPM Core to perform any bookkeeping before
* PSCI executes a power mgmt. operation.
******************************************************************************/
const spd_pm_ops_t spmc_pm = {
.svc_on_finish = spmc_cpu_on_finish_handler,
.svc_off = spmc_cpu_off_handler,
.svc_suspend = spmc_cpu_suspend_handler,
.svc_suspend_finish = spmc_cpu_suspend_finish_handler
};
@@ -0,0 +1,278 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <string.h>
#include <arch.h>
#include <arch_helpers.h>
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <context.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/utils.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <libfdt.h>
#include <plat/common/common_def.h>
#include <plat/common/platform.h>
#include <services/ffa_svc.h>
#include "spm_common.h"
#include "spmc.h"
#include <tools_share/firmware_image_package.h>
#include <platform_def.h>
/*
* Statically allocate a page of memory for passing boot information to an SP.
*/
static uint8_t ffa_boot_info_mem[PAGE_SIZE] __aligned(PAGE_SIZE);
/*
* This function creates a initialization descriptor in the memory reserved
* for passing boot information to an SP. It then copies the partition manifest
* into this region and ensures that its reference in the initialization
* descriptor is updated.
*/
static void spmc_create_boot_info(entry_point_info_t *ep_info,
struct secure_partition_desc *sp)
{
struct ffa_boot_info_header *boot_header;
struct ffa_boot_info_desc *boot_descriptor;
uintptr_t manifest_addr;
/*
* Calculate the maximum size of the manifest that can be accommodated
* in the boot information memory region.
*/
const unsigned int
max_manifest_sz = sizeof(ffa_boot_info_mem) -
(sizeof(struct ffa_boot_info_header) +
sizeof(struct ffa_boot_info_desc));
/*
* The current implementation only supports the FF-A v1.1
* implementation of the boot protocol, therefore check
* that a v1.0 SP has not requested use of the protocol.
*/
if (sp->ffa_version == MAKE_FFA_VERSION(1, 0)) {
ERROR("FF-A boot protocol not supported for v1.0 clients\n");
return;
}
/*
* Check if the manifest will fit into the boot info memory region else
* bail.
*/
if (ep_info->args.arg1 > max_manifest_sz) {
WARN("Unable to copy manifest into boot information. ");
WARN("Max sz = %u bytes. Manifest sz = %lu bytes\n",
max_manifest_sz, ep_info->args.arg1);
return;
}
/* Zero the memory region before populating. */
memset(ffa_boot_info_mem, 0, PAGE_SIZE);
/*
* Populate the ffa_boot_info_header at the start of the boot info
* region.
*/
boot_header = (struct ffa_boot_info_header *) ffa_boot_info_mem;
/* Position the ffa_boot_info_desc after the ffa_boot_info_header. */
boot_header->offset_boot_info_desc =
sizeof(struct ffa_boot_info_header);
boot_descriptor = (struct ffa_boot_info_desc *)
(ffa_boot_info_mem +
boot_header->offset_boot_info_desc);
/*
* We must use the FF-A version coresponding to the version implemented
* by the SP. Currently this can only be v1.1.
*/
boot_header->version = sp->ffa_version;
/* Populate the boot information header. */
boot_header->size_boot_info_desc = sizeof(struct ffa_boot_info_desc);
/* Set the signature "0xFFA". */
boot_header->signature = FFA_INIT_DESC_SIGNATURE;
/* Set the count. Currently 1 since only the manifest is specified. */
boot_header->count_boot_info_desc = 1;
/* Populate the boot information descriptor for the manifest. */
boot_descriptor->type =
FFA_BOOT_INFO_TYPE(FFA_BOOT_INFO_TYPE_STD) |
FFA_BOOT_INFO_TYPE_ID(FFA_BOOT_INFO_TYPE_ID_FDT);
boot_descriptor->flags =
FFA_BOOT_INFO_FLAG_NAME(FFA_BOOT_INFO_FLAG_NAME_UUID) |
FFA_BOOT_INFO_FLAG_CONTENT(FFA_BOOT_INFO_FLAG_CONTENT_ADR);
/*
* Copy the manifest into boot info region after the boot information
* descriptor.
*/
boot_descriptor->size_boot_info = (uint32_t) ep_info->args.arg1;
manifest_addr = (uintptr_t) (ffa_boot_info_mem +
boot_header->offset_boot_info_desc +
boot_header->size_boot_info_desc);
memcpy((void *) manifest_addr, (void *) ep_info->args.arg0,
boot_descriptor->size_boot_info);
boot_descriptor->content = manifest_addr;
/* Calculate the size of the total boot info blob. */
boot_header->size_boot_info_blob = boot_header->offset_boot_info_desc +
boot_descriptor->size_boot_info +
(boot_header->count_boot_info_desc *
boot_header->size_boot_info_desc);
INFO("SP boot info @ 0x%lx, size: %u bytes.\n",
(uintptr_t) ffa_boot_info_mem,
boot_header->size_boot_info_blob);
INFO("SP manifest @ 0x%lx, size: %u bytes.\n",
boot_descriptor->content,
boot_descriptor->size_boot_info);
}
/*
* We are assuming that the index of the execution
* context used is the linear index of the current physical cpu.
*/
unsigned int get_ec_index(struct secure_partition_desc *sp)
{
return plat_my_core_pos();
}
/* S-EL1 partition specific initialisation. */
void spmc_el1_sp_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info)
{
/* Sanity check input arguments. */
assert(sp != NULL);
assert(ep_info != NULL);
/* Initialise the SPSR for S-EL1 SPs. */
ep_info->spsr = SPSR_64(MODE_EL1, MODE_SP_ELX,
DISABLE_ALL_EXCEPTIONS);
/*
* TF-A Implementation defined behaviour to provide the linear
* core ID in the x4 register.
*/
ep_info->args.arg4 = (uintptr_t) plat_my_core_pos();
/*
* Check whether setup is being performed for the primary or a secondary
* execution context. In the latter case, indicate to the SP that this
* is a warm boot.
* TODO: This check would need to be reworked if the same entry point is
* used for both primary and secondary initialisation.
*/
if (sp->secondary_ep != 0U) {
/*
* Sanity check that the secondary entry point is still what was
* originally set.
*/
assert(sp->secondary_ep == ep_info->pc);
ep_info->args.arg0 = FFA_WB_TYPE_S2RAM;
}
}
/* Common initialisation for all SPs. */
void spmc_sp_common_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info,
int32_t boot_info_reg)
{
uint16_t sp_id;
/* Assign FF-A Partition ID if not already assigned. */
if (sp->sp_id == INV_SP_ID) {
sp_id = FFA_SP_ID_BASE + ACTIVE_SP_DESC_INDEX;
/*
* Ensure we don't clash with previously assigned partition
* IDs.
*/
while (!is_ffa_secure_id_valid(sp_id)) {
sp_id++;
if (sp_id == FFA_SWD_ID_LIMIT) {
ERROR("Unable to determine valid SP ID.\n");
panic();
}
}
sp->sp_id = sp_id;
}
/*
* We currently only support S-EL1 partitions so ensure this is the
* case.
*/
assert(sp->runtime_el == S_EL1);
/* Check if the SP wants to use the FF-A boot protocol. */
if (boot_info_reg >= 0) {
/*
* Create a boot information descriptor and copy the partition
* manifest into the reserved memory region for consumption by
* the SP.
*/
spmc_create_boot_info(ep_info, sp);
/*
* We have consumed what we need from ep args so we can now
* zero them before we start populating with new information
* specifically for the SP.
*/
zeromem(&ep_info->args, sizeof(ep_info->args));
/*
* Pass the address of the boot information in the
* boot_info_reg.
*/
switch (boot_info_reg) {
case 0:
ep_info->args.arg0 = (uintptr_t) ffa_boot_info_mem;
break;
case 1:
ep_info->args.arg1 = (uintptr_t) ffa_boot_info_mem;
break;
case 2:
ep_info->args.arg2 = (uintptr_t) ffa_boot_info_mem;
break;
case 3:
ep_info->args.arg3 = (uintptr_t) ffa_boot_info_mem;
break;
default:
ERROR("Invalid value for \"gp-register-num\" %d.\n",
boot_info_reg);
}
} else {
/*
* We don't need any of the information that was populated
* in ep_args so we can clear them.
*/
zeromem(&ep_info->args, sizeof(ep_info->args));
}
}
/*
* Initialise the SP context now we have populated the common and EL specific
* entrypoint information.
*/
void spmc_sp_common_ep_commit(struct secure_partition_desc *sp,
entry_point_info_t *ep_info)
{
cpu_context_t *cpu_ctx;
cpu_ctx = &(spmc_get_sp_ec(sp)->cpu_ctx);
print_entry_point_info(ep_info);
cm_setup_context(cpu_ctx, ep_info);
}
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2022, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SPMC_SHARED_MEM_H
#define SPMC_SHARED_MEM_H
#include <services/el3_spmc_ffa_memory.h>
/**
* struct ffa_mem_relinquish_descriptor - Relinquish request descriptor.
* @handle:
* Id of shared memory object to relinquish.
* @flags:
* If bit 0 is set clear memory after unmapping from borrower. Must be 0
* for share. Bit[1]: Time slicing. Not supported, must be 0. All other
* bits are reserved 0.
* @endpoint_count:
* Number of entries in @endpoint_array.
* @endpoint_array:
* Array of endpoint ids.
*/
struct ffa_mem_relinquish_descriptor {
uint64_t handle;
uint32_t flags;
uint32_t endpoint_count;
ffa_endpoint_id16_t endpoint_array[];
};
CASSERT(sizeof(struct ffa_mem_relinquish_descriptor) == 16,
assert_ffa_mem_relinquish_descriptor_size_mismatch);
/**
* struct spmc_shmem_obj_state - Global state.
* @data: Backing store for spmc_shmem_obj objects.
* @data_size: The size allocated for the backing store.
* @allocated: Number of bytes allocated in @data.
* @next_handle: Handle used for next allocated object.
* @lock: Lock protecting all state in this file.
*/
struct spmc_shmem_obj_state {
uint8_t *data;
size_t data_size;
size_t allocated;
uint64_t next_handle;
spinlock_t lock;
};
extern struct spmc_shmem_obj_state spmc_shmem_obj_state;
extern int plat_spmc_shmem_begin(struct ffa_mtd *desc);
extern int plat_spmc_shmem_reclaim(struct ffa_mtd *desc);
long spmc_ffa_mem_send(uint32_t smc_fid,
bool secure_origin,
uint64_t total_length,
uint32_t fragment_length,
uint64_t address,
uint32_t page_count,
void *cookie,
void *handle,
uint64_t flags);
long spmc_ffa_mem_frag_tx(uint32_t smc_fid,
bool secure_origin,
uint64_t handle_low,
uint64_t handle_high,
uint32_t fragment_length,
uint32_t sender_id,
void *cookie,
void *handle,
uint64_t flags);
long spmc_ffa_mem_retrieve_req(uint32_t smc_fid,
bool secure_origin,
uint32_t total_length,
uint32_t fragment_length,
uint64_t address,
uint32_t page_count,
void *cookie,
void *handle,
uint64_t flags);
long spmc_ffa_mem_frag_rx(uint32_t smc_fid,
bool secure_origin,
uint32_t handle_low,
uint32_t handle_high,
uint32_t fragment_offset,
uint32_t sender_id,
void *cookie,
void *handle,
uint64_t flags);
int spmc_ffa_mem_relinquish(uint32_t smc_fid,
bool secure_origin,
uint32_t handle_low,
uint32_t handle_high,
uint32_t fragment_offset,
uint32_t sender_id,
void *cookie,
void *handle,
uint64_t flags);
int spmc_ffa_mem_reclaim(uint32_t smc_fid,
bool secure_origin,
uint32_t handle_low,
uint32_t handle_high,
uint32_t mem_flags,
uint64_t x4,
void *cookie,
void *handle,
uint64_t flags);
#endif /* SPMC_SHARED_MEM_H */