GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
+107
@@ -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
|
||||
+1995
File diff suppressed because it is too large
Load Diff
+283
@@ -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
|
||||
};
|
||||
+278
@@ -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);
|
||||
}
|
||||
+1861
File diff suppressed because it is too large
Load Diff
+115
@@ -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 */
|
||||
Reference in New Issue
Block a user