feat: forward VM availability messages from SPMC to SP
* Add `plat_ffa_handle_framework_msg` and use it to check framework
messages are valid on SPMC.
* Modify `api_ffa_dir_msg_value` to preserve the value of `args.arg2` if
`args` is a framework message.
Change-Id: I9c66d38b9b3a4fdeb39381289418188350d7d410
Signed-off-by: Karl Meakin <karl.meakin@arm.com>
diff --git a/src/api.c b/src/api.c
index 0803a3d..4728f31 100644
--- a/src/api.c
+++ b/src/api.c
@@ -2727,7 +2727,7 @@
return (struct ffa_value){
.func = (uint32_t)args.func,
.arg1 = (uint32_t)args.arg1,
- .arg2 = (uint32_t)0,
+ .arg2 = (uint32_t)args.arg2,
.arg3 = (uint32_t)args.arg3,
.arg4 = (uint32_t)args.arg4,
.arg5 = (uint32_t)args.arg5,
@@ -2738,27 +2738,38 @@
/**
* Helper to copy direct message payload, depending on SMC used, direct
- * messaging interface used, and expected registers size.
+ * messaging interface used, and expected registers size. Can be used for both
+ * framework messages and standard messages.
*/
static struct ffa_value api_ffa_dir_msg_value(struct ffa_value args)
{
- if (args.func == FFA_MSG_SEND_DIRECT_REQ_32 ||
- args.func == FFA_MSG_SEND_DIRECT_RESP_32) {
- return api_ffa_value_copy32(args);
- }
+ switch (args.func) {
+ case FFA_MSG_SEND_DIRECT_REQ_32:
+ case FFA_MSG_SEND_DIRECT_RESP_32:
+ args = api_ffa_value_copy32(args);
+ if (!ffa_is_framework_msg(args)) {
+ args.arg2 = 0;
+ }
+ break;
- if (args.func == FFA_MSG_SEND_DIRECT_REQ_64 ||
- args.func == FFA_MSG_SEND_DIRECT_RESP_64) {
- args.arg2 = 0;
- }
+ case FFA_MSG_SEND_DIRECT_REQ_64:
+ case FFA_MSG_SEND_DIRECT_RESP_64:
+ if (!ffa_is_framework_msg(args)) {
+ args.arg2 = 0;
+ }
+ break;
- if (args.func == FFA_MSG_SEND_DIRECT_REQ2_64) {
+ case FFA_MSG_SEND_DIRECT_REQ2_64:
args.extended_val.valid = true;
- }
+ break;
- if (args.func == FFA_MSG_SEND_DIRECT_RESP2_64) {
+ case FFA_MSG_SEND_DIRECT_RESP2_64:
args.arg2 = 0;
args.arg3 = 0;
+ break;
+ default:
+ panic("Invalid direct message function %#x\n", args.func);
+ break;
}
return args;
@@ -2827,10 +2838,18 @@
if ((args.func == FFA_MSG_SEND_DIRECT_REQ_32 ||
args.func == FFA_MSG_SEND_DIRECT_REQ_64) &&
+ !ffa_is_framework_msg(args) &&
!api_ffa_dir_msg_is_arg2_zero(args)) {
+ dlog_verbose("Direct messaging: w2 must be zero (w2 = %#lx)\n",
+ args.arg2);
return ffa_error(FFA_INVALID_PARAMETERS);
}
+ if (ffa_is_framework_msg(args) &&
+ plat_ffa_handle_framework_msg(args, &ret)) {
+ return ret;
+ }
+
if (!plat_ffa_is_direct_request_valid(current, sender_vm_id,
receiver_vm_id)) {
dlog_verbose("Invalid direct message request.\n");
@@ -2838,6 +2857,7 @@
}
if (plat_ffa_direct_request_forward(receiver_vm_id, args, &ret)) {
+ dlog_verbose("Direct message request forwarded\n");
return ret;
}
@@ -2861,6 +2881,7 @@
*/
if (!plat_ffa_is_direct_request_supported(current->vm, receiver_vm,
args.func)) {
+ dlog_verbose("Direct message request not supported\n");
return ffa_error(FFA_DENIED);
}
@@ -2930,8 +2951,6 @@
case VCPU_STATE_BLOCKED_INTERRUPT:
case VCPU_STATE_BLOCKED:
case VCPU_STATE_PREEMPTED:
- dlog_verbose("Receiver's vCPU can't receive request (%u)!\n",
- vcpu_index(receiver_vcpu));
ret = ffa_error(FFA_BUSY);
goto out;
case VCPU_STATE_WAITING:
@@ -2958,6 +2977,8 @@
receiver_vcpu->direct_request_origin.is_ffa_req2 =
(args.func == FFA_MSG_SEND_DIRECT_REQ2_64);
receiver_vcpu->direct_request_origin.vm_id = sender_vm_id;
+ receiver_vcpu->direct_request_origin.is_framework =
+ ffa_is_framework_msg(args);
arch_regs_set_retval(&receiver_vcpu->regs, api_ffa_dir_msg_value(args));
@@ -3057,6 +3078,8 @@
struct ffa_value to_ret = api_ffa_dir_msg_value(args);
struct two_vcpu_locked vcpus_locked;
bool received_req2;
+ bool req_framework;
+ bool resp_framework;
/*
* If using FFA_MSG_SEND_DIRECT_RESP, the caller's
@@ -3064,7 +3087,10 @@
* - x8-x17 SBZ if caller's FF-A version >= FF-A v1.2
*/
if (args.func != FFA_MSG_SEND_DIRECT_RESP2_64) {
- if (!api_ffa_dir_msg_is_arg2_zero(args)) {
+ if (!ffa_is_framework_msg(args) &&
+ !api_ffa_dir_msg_is_arg2_zero(args)) {
+ dlog_verbose("%s: w2 Must Be Zero",
+ ffa_func_name(args.func));
return ffa_error(FFA_INVALID_PARAMETERS);
}
@@ -3105,6 +3131,19 @@
goto out;
}
+ req_framework = current_locked.vcpu->direct_request_origin.is_framework;
+ resp_framework = ffa_is_framework_msg(args);
+
+ if (req_framework && !resp_framework) {
+ ret = ffa_error(FFA_INVALID_PARAMETERS);
+ dlog_verbose(
+ "Mismatch in framework message bit: request was a %s "
+ "message, but response is a %s message\n",
+ req_framework ? "framework" : "non-framework",
+ resp_framework ? "framework" : "non-framework");
+ goto out;
+ }
+
received_req2 = current->direct_request_origin.is_ffa_req2;
if (args.func != FFA_MSG_SEND_DIRECT_RESP2_64 && received_req2) {
diff --git a/src/arch/aarch64/plat/ffa/absent.c b/src/arch/aarch64/plat/ffa/absent.c
index e5209fd..66e258e 100644
--- a/src/arch/aarch64/plat/ffa/absent.c
+++ b/src/arch/aarch64/plat/ffa/absent.c
@@ -596,3 +596,11 @@
{
return api_interrupt_get(current_locked);
}
+
+bool plat_ffa_handle_framework_msg(struct ffa_value args, struct ffa_value *ret)
+{
+ (void)args;
+ (void)ret;
+
+ return false;
+}
diff --git a/src/arch/aarch64/plat/ffa/hypervisor.c b/src/arch/aarch64/plat/ffa/hypervisor.c
index bec5cf6..d59b54b 100644
--- a/src/arch/aarch64/plat/ffa/hypervisor.c
+++ b/src/arch/aarch64/plat/ffa/hypervisor.c
@@ -264,26 +264,35 @@
struct ffa_value *ret)
{
if (!ffa_tee_enabled) {
+ dlog_verbose("Not forwarding: ffa_tee_enabled is false\n");
return false;
}
/*
* VM's requests should be forwarded to the SPMC, if receiver is an SP.
*/
- if (!vm_id_is_current_world(receiver_vm_id)) {
- dlog_verbose("%s calling SPMC %#lx %#lx %#lx %#lx %#lx\n",
- __func__, args.func, args.arg1, args.arg2,
- args.arg3, args.arg4);
- if (args.func == FFA_MSG_SEND_DIRECT_REQ_64 ||
- args.func == FFA_MSG_SEND_DIRECT_REQ_32) {
- *ret = arch_other_world_call(args);
- } else if (args.func == FFA_MSG_SEND_DIRECT_REQ2_64) {
- *ret = arch_other_world_call_ext(args);
- }
- return true;
+ if (vm_id_is_current_world(receiver_vm_id)) {
+ dlog_verbose(
+ "Not forwarding: receiver VM %#x is in the same "
+ "world\n",
+ receiver_vm_id);
+ return false;
}
- return false;
+ switch (args.func) {
+ case FFA_MSG_SEND_DIRECT_REQ_32:
+ case FFA_MSG_SEND_DIRECT_REQ_64:
+ *ret = arch_other_world_call(args);
+ break;
+ case FFA_MSG_SEND_DIRECT_REQ2_64:
+ *ret = arch_other_world_call_ext(args);
+ break;
+ default:
+ panic("Invalid direct message function %#x\n", args.func);
+ break;
+ }
+
+ return true;
}
bool plat_ffa_rx_release_forward(struct vm_locked vm_locked,
@@ -2047,3 +2056,11 @@
{
return api_interrupt_get(current_locked);
}
+
+bool plat_ffa_handle_framework_msg(struct ffa_value args, struct ffa_value *ret)
+{
+ (void)args;
+ (void)ret;
+
+ return false;
+}
diff --git a/src/arch/aarch64/plat/ffa/spmc.c b/src/arch/aarch64/plat/ffa/spmc.c
index 6eeb82b..af22eb7 100644
--- a/src/arch/aarch64/plat/ffa/spmc.c
+++ b/src/arch/aarch64/plat/ffa/spmc.c
@@ -15,6 +15,7 @@
#include "hf/arch/vmid_base.h"
#include "hf/api.h"
+#include "hf/bits.h"
#include "hf/check.h"
#include "hf/dlog.h"
#include "hf/ffa.h"
@@ -2964,3 +2965,97 @@
return api_interrupt_get(current_locked);
}
+
+/**
+ * Check that the arguments to a VM availability message are correct.
+ * Returns `FFA_SUCCESS_32` if the arguments are correct.
+ * Returns `FFA_INVALID_PARAMETERS` if:
+ * - the receiver is not a valid VM
+ * - the receiver has not subscribed to the message type
+ */
+static struct ffa_value check_vm_availability_message(struct ffa_value args)
+{
+ struct ffa_value ret = ffa_error(FFA_INVALID_PARAMETERS);
+ enum ffa_framework_msg_func func = ffa_framework_msg_func(args);
+ ffa_id_t receiver_id = ffa_receiver(args);
+ struct vm_locked receiver = vm_find_locked(receiver_id);
+
+ if (receiver.vm == NULL) {
+ dlog_verbose(
+ "VM availability messaging: could not find SP %#x\n",
+ receiver_id);
+ return ret;
+ }
+
+ /* only valid if receiver has subscribed */
+ if (func == FFA_FRAMEWORK_MSG_VM_CREATION_REQ &&
+ !receiver.vm->vm_availability_messages.vm_created) {
+ dlog_verbose(
+ "VM availability messaging: SP %#x is not subscribed "
+ "to VM creation messages\n",
+ receiver_id);
+ goto out;
+ }
+
+ if (func == FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ &&
+ !receiver.vm->vm_availability_messages.vm_destroyed) {
+ dlog_verbose(
+ "VM availability messaging: SP %#x is not subscribed "
+ "to VM destruction messages\n",
+ receiver_id);
+ goto out;
+ }
+
+ if (ANY_BITS_SET(args.arg5, FFA_VM_AVAILABILITY_MESSAGE_SBZ_HI,
+ FFA_VM_AVAILABILITY_MESSAGE_SBZ_LO)) {
+ dlog_warning(
+ "VM availability messaging: bits[%u:%u] of w5 are "
+ "reserved and should be zero (w5=%#lx)\n",
+ FFA_VM_AVAILABILITY_MESSAGE_SBZ_HI,
+ FFA_VM_AVAILABILITY_MESSAGE_SBZ_LO, args.arg5);
+ }
+
+ if (args.arg6 != 0) {
+ dlog_warning(
+ "VM availability messaging: w6 is reserved and should "
+ "be zero (w6=%#lx)\n",
+ args.arg6);
+ }
+
+ if (args.arg7 != 0) {
+ dlog_warning(
+ "VM availability messaging: w7 is reserved and should "
+ "be zero (w7=%#lx)\n",
+ args.arg7);
+ }
+
+ ret = (struct ffa_value){.func = FFA_SUCCESS_32};
+
+out:
+
+ vm_unlock(&receiver);
+ return ret;
+}
+
+/**
+ * Handle framework messages: in particular, check VM availability messages are
+ * valid.
+ */
+bool plat_ffa_handle_framework_msg(struct ffa_value args, struct ffa_value *ret)
+{
+ enum ffa_framework_msg_func func = ffa_framework_msg_func(args);
+
+ switch (func) {
+ case FFA_FRAMEWORK_MSG_VM_CREATION_REQ:
+ case FFA_FRAMEWORK_MSG_VM_DESTRUCTION_REQ:
+ *ret = check_vm_availability_message(args);
+ if (ret->func != FFA_SUCCESS_32) {
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return false;
+}
diff --git a/src/arch/fake/hypervisor/ffa.c b/src/arch/fake/hypervisor/ffa.c
index ce9e72f..2399351 100644
--- a/src/arch/fake/hypervisor/ffa.c
+++ b/src/arch/fake/hypervisor/ffa.c
@@ -675,3 +675,11 @@
return 0;
}
+
+bool plat_ffa_handle_framework_msg(struct ffa_value args, struct ffa_value *ret)
+{
+ (void)args;
+ (void)ret;
+
+ return false;
+}