feat: perform checks for partition runtime model
Perform state transition checks across invocations of various
FF-A ABIs based on the partition runtime model.
Partition manager must return DENIED as the error code if an
invalid transition is attempted by an endpoint execution context.
Change-Id: I7bb9c23f7c50288c8ccfcc9c8928045c0168276f
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/api.c b/src/api.c
index 8ae4e4a..ceb7934 100644
--- a/src/api.c
+++ b/src/api.c
@@ -270,7 +270,8 @@
{
struct ffa_value ret = (struct ffa_value){.func = FFA_SUCCESS_32};
struct vcpu_locked current_locked;
- bool is_direct_request_ongoing;
+ bool transition_allowed;
+ enum vcpu_state next_state = VCPU_STATE_BLOCKED;
if (current->vm->id == HF_PRIMARY_VM_ID) {
/* NOOP on the primary as it makes the scheduling decisions. */
@@ -278,20 +279,23 @@
}
current_locked = vcpu_lock(current);
- is_direct_request_ongoing =
- is_ffa_direct_msg_request_ongoing(current_locked);
+ transition_allowed = plat_ffa_check_runtime_state_transition(
+ current, current->vm->id, HF_INVALID_VM_ID, NULL, FFA_YIELD_32,
+ &next_state);
vcpu_unlock(¤t_locked);
- if (is_direct_request_ongoing) {
+ if (!transition_allowed) {
return ffa_error(FFA_DENIED);
}
+ assert(next_state == VCPU_STATE_BLOCKED);
+
*next = api_switch_to_primary(
current,
(struct ffa_value){.func = FFA_YIELD_32,
.arg1 = ffa_vm_vcpu(current->vm->id,
vcpu_index(current))},
- VCPU_STATE_BLOCKED);
+ next_state);
return ret;
}
@@ -689,6 +693,7 @@
struct ffa_value *args)
{
struct ffa_value ret;
+ enum vcpu_state next_state = VCPU_STATE_WAITING;
if (args->arg1 != 0U || args->arg2 != 0U || args->arg3 != 0U ||
args->arg4 != 0U || args->arg5 != 0U || args->arg6 != 0U ||
@@ -696,6 +701,14 @@
return ffa_error(FFA_INVALID_PARAMETERS);
}
+ if (!plat_ffa_check_runtime_state_transition(
+ current, current->vm->id, HF_INVALID_VM_ID, NULL,
+ FFA_MSG_WAIT_32, &next_state)) {
+ return ffa_error(FFA_DENIED);
+ }
+
+ assert(next_state == VCPU_STATE_WAITING);
+
if (plat_ffa_msg_wait_prepare(current, next, &ret)) {
return ret;
}
@@ -941,6 +954,8 @@
struct vm *vm;
struct vcpu *vcpu;
struct ffa_value ret = ffa_error(FFA_INVALID_PARAMETERS);
+ enum vcpu_state next_state = VCPU_STATE_BLOCKED;
+ struct vcpu_locked current_locked;
if (!plat_ffa_run_checks(current, vm_id, vcpu_idx, &ret, next)) {
return ret;
@@ -961,8 +976,23 @@
goto out;
}
- /* Update state if allowed. */
- vcpu = vm_get_vcpu(vm, vcpu_idx);
+ /*
+ * Refer Figure 8.13 Scenario 1 of the FF-A v1.1 EAC spec. SPMC
+ * bypasses the intermediate execution contexts and resumes the
+ * SP execution context that was originally preempted.
+ */
+ if (*next != NULL) {
+ vcpu = *next;
+ } else {
+ vcpu = vm_get_vcpu(vm, vcpu_idx);
+ }
+
+ if (!plat_ffa_check_runtime_state_transition(current, current->vm->id,
+ HF_INVALID_VM_ID, vcpu,
+ FFA_RUN_32, &next_state)) {
+ return ffa_error(FFA_DENIED);
+ }
+
if (!api_vcpu_prepare_run(current, vcpu, &ret)) {
goto out;
}
@@ -991,6 +1021,11 @@
/* Switch to the vCPU. */
*next = vcpu;
+ assert(next_state == VCPU_STATE_BLOCKED);
+ current_locked = vcpu_lock(current);
+ current->state = VCPU_STATE_BLOCKED;
+ vcpu_unlock(¤t_locked);
+
/*
* Set a placeholder return code to the scheduler. This will be
* overwritten when the switch back to the primary occurs.
@@ -2445,6 +2480,7 @@
struct vm_locked receiver_locked;
struct vcpu *receiver_vcpu;
struct two_vcpu_locked vcpus_locked;
+ enum vcpu_state next_state = VCPU_STATE_BLOCKED;
if (!api_ffa_dir_msg_is_arg2_zero(args)) {
return ffa_error(FFA_INVALID_PARAMETERS);
@@ -2487,6 +2523,12 @@
return ffa_error(FFA_INVALID_PARAMETERS);
}
+ if (!plat_ffa_check_runtime_state_transition(
+ current, sender_vm_id, HF_INVALID_VM_ID, receiver_vcpu,
+ args.func, &next_state)) {
+ return ffa_error(FFA_DENIED);
+ }
+
receiver_locked = vm_lock(receiver_vm);
vcpus_locked = vcpu_lock_both(receiver_vcpu, current);
@@ -2554,6 +2596,7 @@
arch_regs_set_retval(&receiver_vcpu->regs, api_ffa_dir_msg_value(args));
+ assert(next_state == VCPU_STATE_BLOCKED);
current->state = VCPU_STATE_BLOCKED;
/* Switch to receiver vCPU targeted to by direct msg request */
@@ -2596,6 +2639,7 @@
struct vcpu **next)
{
struct vcpu_locked current_locked;
+ enum vcpu_state next_state = VCPU_STATE_WAITING;
if (!api_ffa_dir_msg_is_arg2_zero(args)) {
return ffa_error(FFA_INVALID_PARAMETERS);
@@ -2608,6 +2652,13 @@
return ffa_error(FFA_INVALID_PARAMETERS);
}
+ if (!plat_ffa_check_runtime_state_transition(current, sender_vm_id,
+ receiver_vm_id, NULL,
+ args.func, &next_state)) {
+ return ffa_error(FFA_DENIED);
+ }
+
+ assert(next_state == VCPU_STATE_WAITING);
current_locked = vcpu_lock(current);
if (api_ffa_is_managed_exit_ongoing(current_locked)) {
/*
diff --git a/test/vmapi/ffa_secure_partitions/dir_msg.c b/test/vmapi/ffa_secure_partitions/dir_msg.c
index 7610849..dce67b8 100644
--- a/test/vmapi/ffa_secure_partitions/dir_msg.c
+++ b/test/vmapi/ffa_secure_partitions/dir_msg.c
@@ -56,7 +56,7 @@
/**
* Test that if a direct message request is sent to an SP that is already
- * waiting for a direct message response an BUSY error code is returned.
+ * waiting for a direct message response an DENIED error code is returned.
*/
TEST(ffa_msg_send_direct_req, fails_direct_req_to_waiting_sp)
{
@@ -64,7 +64,7 @@
struct ffa_value res;
ffa_vm_id_t own_id = hf_vm_get_id();
- res = sp_req_echo_busy_cmd_send(own_id, receiver_id);
+ res = sp_req_echo_denied_cmd_send(own_id, receiver_id);
EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
EXPECT_EQ(sp_resp(res), SP_SUCCESS);
diff --git a/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h b/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h
index 941e31e..0d91fd3 100644
--- a/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h
+++ b/test/vmapi/ffa_secure_partitions/services/inc/partition_services.h
@@ -69,18 +69,18 @@
uint32_t val2, uint32_t val3, uint32_t val4);
/**
- * Command to request SP to run echo busy test with second SP.
+ * Command to request SP to run echo denied test with second SP.
*/
-#define SP_REQ_ECHO_BUSY_CMD 0x65636871
+#define SP_REQ_ECHO_DENIED_CMD 0x65636871
-static inline struct ffa_value sp_req_echo_busy_cmd_send(ffa_vm_id_t sender,
- ffa_vm_id_t receiver)
+static inline struct ffa_value sp_req_echo_denied_cmd_send(ffa_vm_id_t sender,
+ ffa_vm_id_t receiver)
{
- return ffa_msg_send_direct_req(sender, receiver, SP_REQ_ECHO_BUSY_CMD,
+ return ffa_msg_send_direct_req(sender, receiver, SP_REQ_ECHO_DENIED_CMD,
0, 0, 0, 0);
}
-struct ffa_value sp_req_echo_busy_cmd(ffa_vm_id_t test_source);
+struct ffa_value sp_req_echo_denied_cmd(ffa_vm_id_t test_source);
/**
* Command to request SP to set notifications.
diff --git a/test/vmapi/ffa_secure_partitions/services/message_loop.c b/test/vmapi/ffa_secure_partitions/services/message_loop.c
index 8b5bf37..d907b90 100644
--- a/test/vmapi/ffa_secure_partitions/services/message_loop.c
+++ b/test/vmapi/ffa_secure_partitions/services/message_loop.c
@@ -37,8 +37,8 @@
res = sp_req_echo_cmd(ffa_sender(res), res.arg4,
res.arg5, res.arg6, res.arg7);
break;
- case SP_REQ_ECHO_BUSY_CMD:
- res = sp_req_echo_busy_cmd(ffa_sender(res));
+ case SP_REQ_ECHO_DENIED_CMD:
+ res = sp_req_echo_denied_cmd(ffa_sender(res));
break;
case SP_NOTIF_SET_CMD:
res = sp_notif_set_cmd(
diff --git a/test/vmapi/ffa_secure_partitions/services/partition_services.c b/test/vmapi/ffa_secure_partitions/services/partition_services.c
index 9a3262b..3fa669a 100644
--- a/test/vmapi/ffa_secure_partitions/services/partition_services.c
+++ b/test/vmapi/ffa_secure_partitions/services/partition_services.c
@@ -46,7 +46,7 @@
return sp_success(own_id, test_source, 0);
}
-struct ffa_value sp_req_echo_busy_cmd(ffa_vm_id_t test_source)
+struct ffa_value sp_req_echo_denied_cmd(ffa_vm_id_t test_source)
{
ffa_vm_id_t own_id = hf_vm_get_id();
struct ffa_value res;
@@ -54,9 +54,9 @@
if (IS_SP_ID(test_source)) {
res = ffa_msg_send_direct_req(own_id, test_source, 0, 0, 0, 0,
0);
- EXPECT_FFA_ERROR(res, FFA_BUSY);
+ EXPECT_FFA_ERROR(res, FFA_DENIED);
} else {
- res = sp_req_echo_busy_cmd_send(own_id, own_id + 1);
+ res = sp_req_echo_denied_cmd_send(own_id, own_id + 1);
EXPECT_EQ(res.func, FFA_MSG_SEND_DIRECT_RESP_32);
EXPECT_EQ(sp_resp(res), SP_SUCCESS);