feat(spmc): add FFA_PARTITION_INFO_GET handler

Enable the SPMC to handle calls to FFA_PARTITION_INFO_GET.
This allows the normal world to discover which partitions
are running in the secure world including logical partitions
in EL3.

This implementation supports both the v1.0 and v1.1
implementations of the Partition Info Get Descriptor.
The SPMC populates the appropriate descriptor in the
partitions RX buffer, if requested, according to the
version of FF-A that the caller is using.

Additionally rename the common/uuid UUID_H include guard
due to a conflict with another header file.

Signed-off-by: Marc Bonnici <marc.bonnici@arm.com>
Change-Id: I0a85f1dae50fae1fe47a3cafb765fbe9f40619e1
This commit is contained in:
Marc Bonnici 2021-08-17 18:00:07 +01:00
parent 1a752245ec
commit f74e27723b
5 changed files with 309 additions and 5 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@ -132,3 +132,27 @@ int read_uuid(uint8_t *dest, char *uuid)
return 0;
}
/*
* Helper function to check if 2 UUIDs match.
*/
bool uuid_match(uint32_t *uuid1, uint32_t *uuid2)
{
return !memcmp(uuid1, uuid2, sizeof(uint32_t) * 4);
}
/*
* Helper function to copy from one UUID struct to another.
*/
void copy_uuid(uint32_t *to_uuid, uint32_t *from_uuid)
{
to_uuid[0] = from_uuid[0];
to_uuid[1] = from_uuid[1];
to_uuid[2] = from_uuid[2];
to_uuid[3] = from_uuid[3];
}
bool is_null_uuid(uint32_t *uuid)
{
return (uuid[0] == 0 && uuid[1] == 0 &&
uuid[2] == 0 && uuid[3] == 0);
}

View File

@ -1,15 +1,18 @@
/*
* Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
* Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef UUID_H
#define UUID_H
#ifndef UUID_COMMON_H
#define UUID_COMMON_H
#define UUID_BYTES_LENGTH 16
#define UUID_STRING_LENGTH 36
int read_uuid(uint8_t *dest, char *uuid);
bool uuid_match(uint32_t *uuid1, uint32_t *uuid2);
void copy_uuid(uint32_t *to_uuid, uint32_t *from_uuid);
bool is_null_uuid(uint32_t *uuid);
#endif /* UUID_H */
#endif /* UUID_COMMON_H */

View File

@ -195,6 +195,11 @@
#define SPMC_SECURE_ID_MASK U(1)
#define SPMC_SECURE_ID_SHIFT U(15)
/*
* Partition Count Flag in FFA_PARTITION_INFO_GET.
*/
#define FFA_PARTITION_INFO_GET_COUNT_FLAG_MASK U(1 << 0)
/*
* Mask for source and destination endpoint id in
* a direct message request/response.

View File

@ -184,6 +184,24 @@ struct ns_endpoint_desc {
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];
};
/* Setup Function for different SP types. */
void spmc_sp_common_setup(struct secure_partition_desc *sp,
entry_point_info_t *ep_info);

View File

@ -13,6 +13,7 @@
#include <common/debug.h>
#include <common/fdt_wrappers.h>
#include <common/runtime_svc.h>
#include <common/uuid.h>
#include <lib/el3_runtime/context_mgmt.h>
#include <lib/smccc.h>
#include <lib/utils.h>
@ -27,6 +28,9 @@
#include <platform_def.h>
/* Declare the maximum number of SPs and El3 LPs. */
#define MAX_SP_LP_PARTITIONS SECURE_PARTITION_COUNT + MAX_EL3_LP_DESCS_COUNT
/*
* Allocate a secure partition descriptor to describe each SP in the system that
* does not reside at EL3.
@ -727,6 +731,251 @@ static uint64_t rxtx_unmap_handler(uint32_t smc_fid,
SMC_RET1(handle, FFA_SUCCESS_SMC32);
}
/*
* Collate the partition information in a v1.1 partition information
* descriptor format, this will be converter later if required.
*/
static int partition_info_get_handler_v1_1(uint32_t *uuid,
struct ffa_partition_info_v1_1
*partitions,
uint32_t max_partitions,
uint32_t *partition_count)
{
uint32_t index;
struct ffa_partition_info_v1_1 *desc;
bool null_uuid = is_null_uuid(uuid);
struct el3_lp_desc *el3_lp_descs = get_el3_lp_array();
/* Deal with Logical Partitions. */
for (index = 0U; index < EL3_LP_DESCS_COUNT; index++) {
if (null_uuid || uuid_match(uuid, el3_lp_descs[index].uuid)) {
/* Found a matching UUID, populate appropriately. */
if (*partition_count >= max_partitions) {
return FFA_ERROR_NO_MEMORY;
}
desc = &partitions[*partition_count];
desc->ep_id = el3_lp_descs[index].sp_id;
desc->execution_ctx_count = PLATFORM_CORE_COUNT;
desc->properties = el3_lp_descs[index].properties;
if (null_uuid) {
copy_uuid(desc->uuid, el3_lp_descs[index].uuid);
}
(*partition_count)++;
}
}
/* Deal with physical SP's. */
for (index = 0U; index < SECURE_PARTITION_COUNT; index++) {
if (null_uuid || uuid_match(uuid, sp_desc[index].uuid)) {
/* Found a matching UUID, populate appropriately. */
if (*partition_count >= max_partitions) {
return FFA_ERROR_NO_MEMORY;
}
desc = &partitions[*partition_count];
desc->ep_id = sp_desc[index].sp_id;
/*
* Execution context count must match No. cores for
* S-EL1 SPs.
*/
desc->execution_ctx_count = PLATFORM_CORE_COUNT;
desc->properties = sp_desc[index].properties;
if (null_uuid) {
copy_uuid(desc->uuid, sp_desc[index].uuid);
}
(*partition_count)++;
}
}
return 0;
}
/*
* Handle the case where that caller only wants the count of partitions
* matching a given UUID and does not want the corresponding descriptors
* populated.
*/
static uint32_t partition_info_get_handler_count_only(uint32_t *uuid)
{
uint32_t index = 0;
uint32_t partition_count = 0;
bool null_uuid = is_null_uuid(uuid);
struct el3_lp_desc *el3_lp_descs = get_el3_lp_array();
/* Deal with Logical Partitions. */
for (index = 0U; index < EL3_LP_DESCS_COUNT; index++) {
if (null_uuid ||
uuid_match(uuid, el3_lp_descs[index].uuid)) {
(partition_count)++;
}
}
/* Deal with physical SP's. */
for (index = 0U; index < SECURE_PARTITION_COUNT; index++) {
if (null_uuid || uuid_match(uuid, sp_desc[index].uuid)) {
(partition_count)++;
}
}
return partition_count;
}
/*
* If the caller of the PARTITION_INFO_GET ABI was a v1.0 caller, populate
* the coresponding descriptor format from the v1.1 descriptor array.
*/
static uint64_t partition_info_populate_v1_0(struct ffa_partition_info_v1_1
*partitions,
struct mailbox *mbox,
int partition_count)
{
uint32_t index;
uint32_t buf_size;
uint32_t descriptor_size;
struct ffa_partition_info_v1_0 *v1_0_partitions =
(struct ffa_partition_info_v1_0 *) mbox->rx_buffer;
buf_size = mbox->rxtx_page_count * FFA_PAGE_SIZE;
descriptor_size = partition_count *
sizeof(struct ffa_partition_info_v1_0);
if (descriptor_size > buf_size) {
return FFA_ERROR_NO_MEMORY;
}
for (index = 0U; index < partition_count; index++) {
v1_0_partitions[index].ep_id = partitions[index].ep_id;
v1_0_partitions[index].execution_ctx_count =
partitions[index].execution_ctx_count;
v1_0_partitions[index].properties =
partitions[index].properties;
}
return 0;
}
/*
* Main handler for FFA_PARTITION_INFO_GET which supports both FF-A v1.1 and
* v1.0 implementations.
*/
static uint64_t partition_info_get_handler(uint32_t smc_fid,
bool secure_origin,
uint64_t x1,
uint64_t x2,
uint64_t x3,
uint64_t x4,
void *cookie,
void *handle,
uint64_t flags)
{
int ret;
uint32_t partition_count = 0;
uint32_t size = 0;
uint32_t ffa_version = get_partition_ffa_version(secure_origin);
struct mailbox *mbox;
uint64_t info_get_flags;
bool count_only;
uint32_t uuid[4];
uuid[0] = x1;
uuid[1] = x2;
uuid[2] = x3;
uuid[3] = x4;
/* Determine if the Partition descriptors should be populated. */
info_get_flags = SMC_GET_GP(handle, CTX_GPREG_X5);
count_only = (info_get_flags & FFA_PARTITION_INFO_GET_COUNT_FLAG_MASK);
/* Handle the case where we don't need to populate the descriptors. */
if (count_only) {
partition_count = partition_info_get_handler_count_only(uuid);
if (partition_count == 0) {
return spmc_ffa_error_return(handle,
FFA_ERROR_INVALID_PARAMETER);
}
} else {
struct ffa_partition_info_v1_1 partitions[MAX_SP_LP_PARTITIONS];
/*
* Handle the case where the partition descriptors are required,
* check we have the buffers available and populate the
* appropriate structure version.
*/
/* Obtain the v1.1 format of the descriptors. */
ret = partition_info_get_handler_v1_1(uuid, partitions,
MAX_SP_LP_PARTITIONS,
&partition_count);
/* Check if an error occurred during discovery. */
if (ret != 0) {
goto err;
}
/* If we didn't find any matches the UUID is unknown. */
if (partition_count == 0) {
ret = FFA_ERROR_INVALID_PARAMETER;
goto err;
}
/* Obtain the partition mailbox RX/TX buffer pair descriptor. */
mbox = spmc_get_mbox_desc(secure_origin);
/*
* If the caller has not bothered registering its RX/TX pair
* then return an error code.
*/
spin_lock(&mbox->lock);
if (mbox->rx_buffer == NULL) {
ret = FFA_ERROR_BUSY;
goto err_unlock;
}
/* Ensure the RX buffer is currently free. */
if (mbox->state != MAILBOX_STATE_EMPTY) {
ret = FFA_ERROR_BUSY;
goto err_unlock;
}
/* Zero the RX buffer before populating. */
(void)memset(mbox->rx_buffer, 0,
mbox->rxtx_page_count * FFA_PAGE_SIZE);
/*
* Depending on the FF-A version of the requesting partition
* we may need to convert to a v1.0 format otherwise we can copy
* directly.
*/
if (ffa_version == MAKE_FFA_VERSION(U(1), U(0))) {
ret = partition_info_populate_v1_0(partitions,
mbox,
partition_count);
if (ret != 0) {
goto err_unlock;
}
} else {
uint32_t buf_size = mbox->rxtx_page_count *
FFA_PAGE_SIZE;
/* Ensure the descriptor will fit in the buffer. */
size = sizeof(struct ffa_partition_info_v1_1);
if (partition_count * size > buf_size) {
ret = FFA_ERROR_NO_MEMORY;
goto err_unlock;
}
memcpy(mbox->rx_buffer, partitions,
partition_count * size);
}
mbox->state = MAILBOX_STATE_FULL;
spin_unlock(&mbox->lock);
}
SMC_RET4(handle, FFA_SUCCESS_SMC32, 0, partition_count, size);
err_unlock:
spin_unlock(&mbox->lock);
err:
return spmc_ffa_error_return(handle, ret);
}
/*******************************************************************************
* This function will parse the Secure Partition Manifest. From manifest, it
* will fetch details for preparing Secure partition image context and secure
@ -1142,6 +1391,11 @@ uint64_t spmc_smc_handler(uint32_t smc_fid,
return rxtx_unmap_handler(smc_fid, secure_origin, x1, x2, x3,
x4, cookie, handle, flags);
case FFA_PARTITION_INFO_GET:
return partition_info_get_handler(smc_fid, secure_origin, x1,
x2, x3, x4, cookie, handle,
flags);
case FFA_MSG_WAIT:
return msg_wait_handler(smc_fid, secure_origin, x1, x2, x3, x4,
cookie, handle, flags);