aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--inc/hf/api.h2
-rw-r--r--inc/hf/manifest.h2
-rw-r--r--inc/hf/vm.h2
-rw-r--r--inc/vmapi/hf/call.h25
-rw-r--r--inc/vmapi/hf/ffa.h65
-rw-r--r--src/api.c139
-rw-r--r--src/arch/aarch64/hypervisor/handler.c8
-rw-r--r--src/load.c1
-rw-r--r--src/manifest.c2
-rw-r--r--src/manifest_test.cc2
-rw-r--r--src/vm.c11
-rw-r--r--test/vmapi/primary_only/BUILD.gn1
-rw-r--r--test/vmapi/primary_only/primary_only.c63
-rw-r--r--test/vmapi/primary_with_secondaries/ffa.c38
14 files changed, 326 insertions, 35 deletions
diff --git a/inc/hf/api.h b/inc/hf/api.h
index 2c092ec35..f9f8ccae9 100644
--- a/inc/hf/api.h
+++ b/inc/hf/api.h
@@ -48,6 +48,8 @@ struct ffa_value api_ffa_rxtx_map(ipaddr_t send, ipaddr_t recv,
struct vcpu **next);
void api_yield(struct vcpu *current, struct vcpu **next);
struct ffa_value api_ffa_version(uint32_t requested_version);
+struct ffa_value api_ffa_partition_info_get(struct vcpu *current,
+ const struct ffa_uuid *uuid);
struct ffa_value api_ffa_id_get(const struct vcpu *current);
struct ffa_value api_ffa_features(uint32_t function_id);
struct ffa_value api_ffa_run(ffa_vm_id_t vm_id, ffa_vcpu_index_t vcpu_idx,
diff --git a/inc/hf/manifest.h b/inc/hf/manifest.h
index 9e571b142..88f3b6e8f 100644
--- a/inc/hf/manifest.h
+++ b/inc/hf/manifest.h
@@ -47,7 +47,7 @@ struct sp_manifest {
/** PSA-FF-A expected version - mandatory */
uint32_t ffa_version;
/** UUID - mandatory */
- uint32_t uuid[4];
+ struct ffa_uuid uuid;
/** Partition id - optional */
ffa_vm_id_t id;
/** Aux ids for mem transactions - optional */
diff --git a/inc/hf/vm.h b/inc/hf/vm.h
index d45b47674..cf03b0ce1 100644
--- a/inc/hf/vm.h
+++ b/inc/hf/vm.h
@@ -100,6 +100,7 @@ struct smc_whitelist {
struct vm {
ffa_vm_id_t id;
+ struct ffa_uuid uuid;
struct smc_whitelist smc_whitelist;
/** See api.c for the partial ordering on locks. */
@@ -140,6 +141,7 @@ bool vm_init_next(ffa_vcpu_count_t vcpu_count, struct mpool *ppool,
struct vm **new_vm);
ffa_vm_count_t vm_get_count(void);
struct vm *vm_find(ffa_vm_id_t id);
+struct vm *vm_find_index(uint16_t index);
struct vm_locked vm_lock(struct vm *vm);
struct two_vm_locked vm_lock_both(struct vm *vm1, struct vm *vm2);
void vm_unlock(struct vm_locked *locked);
diff --git a/inc/vmapi/hf/call.h b/inc/vmapi/hf/call.h
index 719da6207..8a2993341 100644
--- a/inc/vmapi/hf/call.h
+++ b/inc/vmapi/hf/call.h
@@ -28,6 +28,31 @@ static inline struct ffa_value ffa_id_get(void)
}
/**
+ * Requests information for partitions instantiated in the system. The
+ * information is returned in the RX buffer of the caller as an array of
+ * partition information descriptors (struct ffa_partition_info).
+ *
+ * A Null UUID (UUID that is all zeros) returns information for all partitions,
+ * whereas a non-Null UUID returns information only for partitions that match.
+ *
+ * Returns:
+ * - FFA_SUCCESS on success. The count of partition information descriptors
+ * populated in the RX buffer is returned in arg2 (register w2).
+ * - FFA_BUSY if the caller's RX buffer is not free.
+ * - FFA_NO_MEMORY if the results do not fit in the callers RX buffer.
+ * - FFA_INVALID_PARAMETERS for an unrecognized UUID.
+ */
+static inline struct ffa_value ffa_partition_info_get(
+ const struct ffa_uuid *uuid)
+{
+ return ffa_call((struct ffa_value){.func = FFA_PARTITION_INFO_GET_32,
+ .arg1 = uuid->uuid[0],
+ .arg2 = uuid->uuid[1],
+ .arg3 = uuid->uuid[2],
+ .arg4 = uuid->uuid[3]});
+}
+
+/**
* Returns the VM's own ID.
*/
static inline ffa_vm_id_t hf_vm_get_id(void)
diff --git a/inc/vmapi/hf/ffa.h b/inc/vmapi/hf/ffa.h
index 02c07778b..bc85337d0 100644
--- a/inc/vmapi/hf/ffa.h
+++ b/inc/vmapi/hf/ffa.h
@@ -290,6 +290,71 @@ static inline ffa_vm_id_t ffa_frag_sender(struct ffa_value args)
}
/**
+ * Holds the UUID in a struct that is mappable directly to the SMCC calling
+ * convention, which is used for FF-A calls.
+ *
+ * Refer to table 84 of the FF-A 1.0 EAC specification as well as section 5.3
+ * of the SMCC Spec 1.2.
+ */
+struct ffa_uuid {
+ uint32_t uuid[4];
+};
+
+static inline void ffa_uuid_init(uint32_t w0, uint32_t w1, uint32_t w2,
+ uint32_t w3, struct ffa_uuid *uuid)
+{
+ uuid->uuid[0] = w0;
+ uuid->uuid[1] = w1;
+ uuid->uuid[2] = w2;
+ uuid->uuid[3] = w3;
+}
+
+static inline bool ffa_uuid_equal(const struct ffa_uuid *uuid1,
+ const struct ffa_uuid *uuid2)
+{
+ return (uuid1->uuid[0] == uuid2->uuid[0]) &&
+ (uuid1->uuid[1] == uuid2->uuid[1]) &&
+ (uuid1->uuid[2] == uuid2->uuid[2]) &&
+ (uuid1->uuid[3] == uuid2->uuid[3]);
+}
+
+static inline bool ffa_uuid_is_null(const struct ffa_uuid *uuid)
+{
+ return (uuid->uuid[0] == 0) && (uuid->uuid[1] == 0) &&
+ (uuid->uuid[2] == 0) && (uuid->uuid[3] == 0);
+}
+
+/**
+ * Flags to determine the partition properties, as required by
+ * FFA_PARTITION_INFO_GET.
+ *
+ * The values of the flags are specified in table 82 of the FF-A 1.0 EAC
+ * specification, "Partition information descriptor, partition properties".
+ */
+typedef uint32_t ffa_partition_properties_t;
+
+/** Partition property: partition supports receipt of direct requests. */
+#define FFA_PARTITION_DIRECT_RECV 0x1
+
+/** Partition property: partition can send direct requests. */
+#define FFA_PARTITION_DIRECT_SEND 0x2
+
+/** Partition property: partition can send and receive indirect messages. */
+#define FFA_PARTITION_INDIRECT_MSG 0x4
+
+/**
+ * Holds information returned for each partition by the FFA_PARTITION_INFO_GET
+ * interface.
+ * This corresponds to table 82 of the FF-A 1.0 EAC specification, "Partition
+ * information descriptor".
+ */
+struct ffa_partition_info {
+ ffa_vm_id_t vm_id;
+ ffa_vcpu_count_t vcpu_count;
+ ffa_partition_properties_t properties;
+};
+
+/**
* A set of contiguous pages which is part of a memory region. This corresponds
* to table 40 of the FF-A 1.0 EAC specification, "Constituent memory region
* descriptor".
diff --git a/src/api.c b/src/api.c
index 418ce50cc..3370bccbf 100644
--- a/src/api.c
+++ b/src/api.c
@@ -26,6 +26,10 @@
#include "vmapi/hf/call.h"
#include "vmapi/hf/ffa.h"
+static_assert(sizeof(struct ffa_partition_info) == 8,
+ "Partition information descriptor size doesn't match the one in "
+ "the FF-A 1.0 EAC specification, Table 82.");
+
/*
* To eliminate the risk of deadlocks, we define a partial order for the
* acquisition of locks held concurrently by the same physical CPU. Our current
@@ -118,6 +122,35 @@ static struct vcpu *api_switch_to_primary(struct vcpu *current,
}
/**
+ * Checks whether the given `to` VM's mailbox is currently busy, and optionally
+ * registers the `from` VM to be notified when it becomes available.
+ */
+static bool msg_receiver_busy(struct vm_locked to, struct vm *from, bool notify)
+{
+ if (to.vm->mailbox.state != MAILBOX_STATE_EMPTY ||
+ to.vm->mailbox.recv == NULL) {
+ /*
+ * Fail if the receiver isn't currently ready to receive data,
+ * setting up for notification if requested.
+ */
+ if (notify) {
+ struct wait_entry *entry =
+ vm_get_wait_entry(from, to.vm->id);
+
+ /* Append waiter only if it's not there yet. */
+ if (list_empty(&entry->wait_links)) {
+ list_append(&to.vm->mailbox.waiter_list,
+ &entry->wait_links);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/**
* Returns to the primary VM and signals that the vCPU still has work to do so.
*/
struct vcpu *api_preempt(struct vcpu *current)
@@ -223,6 +256,82 @@ struct vcpu *api_abort(struct vcpu *current)
return api_switch_to_primary(current, ret, VCPU_STATE_ABORTED);
}
+struct ffa_value api_ffa_partition_info_get(struct vcpu *current,
+ const struct ffa_uuid *uuid)
+{
+ struct vm *current_vm = current->vm;
+ struct vm_locked current_vm_locked;
+ ffa_vm_count_t vm_count = 0;
+ bool uuid_is_null = ffa_uuid_is_null(uuid);
+ struct ffa_value ret;
+ uint32_t size;
+ struct ffa_partition_info partitions[MAX_VMS];
+
+ /*
+ * Iterate through the VMs to find the ones with a matching UUID.
+ * A Null UUID retrieves information for all VMs.
+ */
+ for (uint16_t index = 0; index < vm_get_count(); ++index) {
+ const struct vm *vm = vm_find_index(index);
+
+ if (uuid_is_null || ffa_uuid_equal(uuid, &vm->uuid)) {
+ partitions[vm_count].vm_id = vm->id;
+ partitions[vm_count].vcpu_count = vm->vcpu_count;
+
+ /* Hafnium only supports indirect messaging. */
+ partitions[vm_count].properties =
+ FFA_PARTITION_INDIRECT_MSG;
+
+ ++vm_count;
+ }
+ }
+
+ /* Unrecognized UUID: does not match any of the VMs and is not Null. */
+ if (vm_count == 0) {
+ return ffa_error(FFA_INVALID_PARAMETERS);
+ }
+
+ size = vm_count * sizeof(partitions[0]);
+ if (size > FFA_MSG_PAYLOAD_MAX) {
+ dlog_error(
+ "Partition information does not fit in the VM's RX "
+ "buffer.\n");
+ return ffa_error(FFA_NO_MEMORY);
+ }
+
+ /*
+ * Partition information is returned in the VM's RX buffer, which is why
+ * the lock is needed.
+ */
+ current_vm_locked = vm_lock(current_vm);
+
+ if (msg_receiver_busy(current_vm_locked, NULL, false)) {
+ /*
+ * Can't retrieve memory information if the mailbox is not
+ * available.
+ */
+ dlog_verbose("RX buffer not ready.\n");
+ ret = ffa_error(FFA_BUSY);
+ goto out_unlock;
+ }
+
+ /* Populate the VM's RX buffer with the partition information. */
+ memcpy_s(current_vm->mailbox.recv, FFA_MSG_PAYLOAD_MAX, partitions,
+ size);
+ current_vm->mailbox.recv_size = size;
+ current_vm->mailbox.recv_sender = HF_HYPERVISOR_VM_ID;
+ current_vm->mailbox.recv_func = FFA_PARTITION_INFO_GET_32;
+ current_vm->mailbox.state = MAILBOX_STATE_READ;
+
+ /* Return the count of partition information descriptors in w2. */
+ ret = (struct ffa_value){.func = FFA_SUCCESS_32, .arg2 = vm_count};
+
+out_unlock:
+ vm_unlock(&current_vm_locked);
+
+ return ret;
+}
+
/**
* Returns the ID of the VM.
*/
@@ -870,35 +979,6 @@ exit:
}
/**
- * Checks whether the given `to` VM's mailbox is currently busy, and optionally
- * registers the `from` VM to be notified when it becomes available.
- */
-static bool msg_receiver_busy(struct vm_locked to, struct vm *from, bool notify)
-{
- if (to.vm->mailbox.state != MAILBOX_STATE_EMPTY ||
- to.vm->mailbox.recv == NULL) {
- /*
- * Fail if the receiver isn't currently ready to receive data,
- * setting up for notification if requested.
- */
- if (notify) {
- struct wait_entry *entry =
- vm_get_wait_entry(from, to.vm->id);
-
- /* Append waiter only if it's not there yet. */
- if (list_empty(&entry->wait_links)) {
- list_append(&to.vm->mailbox.waiter_list,
- &entry->wait_links);
- }
- }
-
- return true;
- }
-
- return false;
-}
-
-/**
* Notifies the `to` VM about the message currently in its mailbox, possibly
* with the help of the primary VM.
*/
@@ -1428,6 +1508,7 @@ struct ffa_value api_ffa_features(uint32_t function_id)
case FFA_FEATURES_32:
case FFA_RX_RELEASE_32:
case FFA_RXTX_MAP_64:
+ case FFA_PARTITION_INFO_GET_32:
case FFA_ID_GET_32:
case FFA_MSG_POLL_32:
case FFA_MSG_WAIT_32:
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 280fb71e9..ddc5116b8 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -317,6 +317,14 @@ static bool ffa_handler(struct ffa_value *args, struct vcpu **next)
case FFA_VERSION_32:
*args = api_ffa_version(args->arg1);
return true;
+ case FFA_PARTITION_INFO_GET_32: {
+ struct ffa_uuid uuid;
+
+ ffa_uuid_init(args->arg1, args->arg2, args->arg3, args->arg4,
+ &uuid);
+ *args = api_ffa_partition_info_get(current(), &uuid);
+ return true;
+ }
case FFA_ID_GET_32:
*args = api_ffa_id_get(current());
return true;
diff --git a/src/load.c b/src/load.c
index 83eed5322..8962d653f 100644
--- a/src/load.c
+++ b/src/load.c
@@ -104,6 +104,7 @@ static bool load_kernel(struct mm_stage1_locked stage1_locked, paddr_t begin,
static bool load_common(const struct manifest_vm *manifest_vm, struct vm *vm)
{
vm->smc_whitelist = manifest_vm->smc_whitelist;
+ vm->uuid = manifest_vm->sp.uuid;
/* Initialize architecture-specific features. */
arch_vm_features_set(vm);
diff --git a/src/manifest.c b/src/manifest.c
index 003a1b921..947b106b5 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -317,7 +317,7 @@ static enum manifest_return_code parse_ffa_manifest(struct fdt *fdt,
while (uint32list_has_next(&uuid) && i < 4) {
TRY(uint32list_get_next(&uuid, &uuid_word));
- vm->sp.uuid[i] = uuid_word;
+ vm->sp.uuid.uuid[i] = uuid_word;
i++;
}
dlog_verbose(" SP UUID %#x-%x-%x_%x\n", vm->sp.uuid[0], vm->sp.uuid[1],
diff --git a/src/manifest_test.cc b/src/manifest_test.cc
index b89002d6c..2b8be8bd4 100644
--- a/src/manifest_test.cc
+++ b/src/manifest_test.cc
@@ -819,7 +819,7 @@ TEST(manifest, ffa_valid)
ASSERT_EQ(m.vm[0].sp.ffa_version, 0x10000);
ASSERT_THAT(
- std::span(m.vm[0].sp.uuid, 4),
+ std::span(m.vm[0].sp.uuid.uuid, 4),
ElementsAre(0xb4b5671e, 0x4a904fe1, 0xb81ffb13, 0xdae1dacb));
ASSERT_EQ(m.vm[0].sp.execution_ctx_count, 1);
ASSERT_EQ(m.vm[0].sp.run_time_el, S_EL1);
diff --git a/src/vm.c b/src/vm.c
index af3ecc050..a021d7713 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -90,6 +90,9 @@ ffa_vm_count_t vm_get_count(void)
return vm_count;
}
+/**
+ * Returns a pointer to the VM with the corresponding id.
+ */
struct vm *vm_find(ffa_vm_id_t id)
{
uint16_t index;
@@ -108,6 +111,14 @@ struct vm *vm_find(ffa_vm_id_t id)
index = id - HF_VM_ID_OFFSET;
+ return vm_find_index(index);
+}
+
+/**
+ * Returns a pointer to the VM at the specified index.
+ */
+struct vm *vm_find_index(uint16_t index)
+{
/* Ensure the VM is initialized. */
if (index >= vm_count) {
return NULL;
diff --git a/test/vmapi/primary_only/BUILD.gn b/test/vmapi/primary_only/BUILD.gn
index e16ae25ae..ae26501cb 100644
--- a/test/vmapi/primary_only/BUILD.gn
+++ b/test/vmapi/primary_only/BUILD.gn
@@ -18,6 +18,7 @@ vm_kernel("primary_only_test_vm") {
deps = [
"//test/hftest:hftest_primary_vm",
+ "//test/vmapi/common",
]
}
diff --git a/test/vmapi/primary_only/primary_only.c b/test/vmapi/primary_only/primary_only.c
index 310974443..8af5fa4e0 100644
--- a/test/vmapi/primary_only/primary_only.c
+++ b/test/vmapi/primary_only/primary_only.c
@@ -241,6 +241,9 @@ TEST(ffa, ffa_features)
ret = ffa_features(FFA_RXTX_MAP_64);
EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+ ret = ffa_features(FFA_PARTITION_INFO_GET_32);
+ EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
ret = ffa_features(FFA_ID_GET_32);
EXPECT_EQ(ret.func, FFA_SUCCESS_32);
@@ -298,9 +301,6 @@ TEST(ffa, ffa_features_not_supported)
ret = ffa_features(FFA_RXTX_UNMAP_32);
EXPECT_FFA_ERROR(ret, FFA_NOT_SUPPORTED);
- ret = ffa_features(FFA_PARTITION_INFO_GET_32);
- EXPECT_FFA_ERROR(ret, FFA_NOT_SUPPORTED);
-
ret = ffa_features(FFA_MSG_SEND_DIRECT_RESP_32);
EXPECT_FFA_ERROR(ret, FFA_NOT_SUPPORTED);
@@ -315,6 +315,63 @@ TEST(ffa, ffa_features_not_supported)
}
/**
+ * Verify that partition discovery via the FFA_PARTITION_INFO interface
+ * returns the expected information on the VMs in the system, which in this
+ * case is only one primary VM.
+ *
+ * Verify also that calls to the FFA_PARTITION_INFO interface fail when
+ * expected, e.g., if the mailbox isn't setup or the RX buffer is busy.
+ */
+TEST(ffa, ffa_partition_info)
+{
+ struct mailbox_buffers mb;
+ struct ffa_value ret;
+ const struct ffa_partition_info *partitions;
+ struct ffa_uuid uuid;
+
+ /* A Null UUID requests information for all partitions. */
+ ffa_uuid_init(0, 0, 0, 0, &uuid);
+
+ /* Try to get partition information before the RX buffer is setup. */
+ ret = ffa_partition_info_get(&uuid);
+ EXPECT_FFA_ERROR(ret, FFA_BUSY);
+
+ /* Setup the mailbox (which holds the RX buffer). */
+ mb = set_up_mailbox();
+ partitions = mb.recv;
+
+ /* Check that the expected partition information is returned. */
+ ret = ffa_partition_info_get(&uuid);
+ EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+ EXPECT_EQ(ret.arg2, hf_vm_get_count());
+ EXPECT_EQ(partitions[0].vm_id, hf_vm_get_id());
+ EXPECT_EQ(partitions[0].vcpu_count, hf_vcpu_get_count(hf_vm_get_id()));
+
+ /*
+ * Check that the partition information cannot be requested if the RX
+ * buffer is busy.
+ */
+ ret = ffa_partition_info_get(&uuid);
+ EXPECT_FFA_ERROR(ret, FFA_BUSY);
+
+ /* Release the buffer and try again. */
+ ret = ffa_rx_release();
+ EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+ ret = ffa_partition_info_get(&uuid);
+ EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+ ret = ffa_rx_release();
+ EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+
+ /* Try to get partition information for an unrecognized UUID. */
+ ffa_uuid_init(0, 0, 0, 1, &uuid);
+
+ ret = ffa_partition_info_get(&uuid);
+ EXPECT_FFA_ERROR(ret, FFA_INVALID_PARAMETERS);
+}
+
+/**
* Test that floating-point operations work in the primary VM.
*/
TEST(fp, fp)
diff --git a/test/vmapi/primary_with_secondaries/ffa.c b/test/vmapi/primary_with_secondaries/ffa.c
index 8eebbac6a..d13794794 100644
--- a/test/vmapi/primary_with_secondaries/ffa.c
+++ b/test/vmapi/primary_with_secondaries/ffa.c
@@ -121,3 +121,41 @@ TEST(ffa, ffa_recv_non_blocking)
run_res = ffa_run(SERVICE_VM1, 0);
EXPECT_EQ(run_res.func, FFA_YIELD_32);
}
+
+/**
+ * Verify that partition discovery via the FFA_PARTITION_INFO interface
+ * returns the expected information on the VMs in the system.
+ */
+TEST(ffa, ffa_partition_info)
+{
+ struct mailbox_buffers mb = set_up_mailbox();
+ struct ffa_value ret;
+ const struct ffa_partition_info *partitions = mb.recv;
+ struct ffa_uuid uuid;
+
+ /* A Null UUID requests information for all partitions. */
+ ffa_uuid_init(0, 0, 0, 0, &uuid);
+
+ ret = ffa_partition_info_get(&uuid);
+ EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+ EXPECT_EQ(ret.arg2, hf_vm_get_count());
+
+ for (uint16_t index = 0; index < hf_vm_get_count(); ++index) {
+ ffa_vm_id_t vm_id = partitions[index].vm_id;
+ EXPECT_GE(vm_id, (ffa_vm_id_t)HF_PRIMARY_VM_ID);
+ EXPECT_LE(vm_id, (ffa_vm_id_t)SERVICE_VM3);
+
+ /*
+ * NOTE: The ordering is NOT specified by the spec, but is an
+ * artifact of how it's implemented in Hafnium. If that changes
+ * the following EXPECT could fail.
+ */
+ EXPECT_EQ(vm_id, index + 1);
+
+ EXPECT_EQ(partitions[index].vcpu_count,
+ hf_vcpu_get_count(vm_id));
+ }
+
+ ret = ffa_rx_release();
+ EXPECT_EQ(ret.func, FFA_SUCCESS_32);
+}