diff options
-rw-r--r-- | inc/hf/api.h | 2 | ||||
-rw-r--r-- | inc/hf/manifest.h | 2 | ||||
-rw-r--r-- | inc/hf/vm.h | 2 | ||||
-rw-r--r-- | inc/vmapi/hf/call.h | 25 | ||||
-rw-r--r-- | inc/vmapi/hf/ffa.h | 65 | ||||
-rw-r--r-- | src/api.c | 139 | ||||
-rw-r--r-- | src/arch/aarch64/hypervisor/handler.c | 8 | ||||
-rw-r--r-- | src/load.c | 1 | ||||
-rw-r--r-- | src/manifest.c | 2 | ||||
-rw-r--r-- | src/manifest_test.cc | 2 | ||||
-rw-r--r-- | src/vm.c | 11 | ||||
-rw-r--r-- | test/vmapi/primary_only/BUILD.gn | 1 | ||||
-rw-r--r-- | test/vmapi/primary_only/primary_only.c | 63 | ||||
-rw-r--r-- | test/vmapi/primary_with_secondaries/ffa.c | 38 |
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". @@ -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(¤t_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); @@ -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); +} |