refactor: maintain the boot order list with VMs
In the present implementation, the boot order is maintained through a
list of nodes contained by vCPU data structures.
Boot order protocol is a concept that naturally aligns with VMs. In
subsequent patches, we depend on the boot order for bootstrapping
execution contexts of MP endpoints on secondary CPUs. This motivates
us to maintain the boot order list with elements representing their
parent VM entries. This makes it simpler to obtain the vCPU to be
bootstrapped for initialization when a secondary CPU is turned on.
Change-Id: I333fa2b3188db99b2b52e7e43988a7a7b9fb7aef
Signed-off-by: Madhukar Pappireddy <madhukar.pappireddy@arm.com>
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 666141d..bf411f2 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -337,8 +337,7 @@
case SPMD_FRAMEWORK_MSG_PSCI_REQ: {
enum psci_return_code psci_msg_response =
PSCI_ERROR_NOT_SUPPORTED;
- struct vcpu *boot_vcpu = vcpu_get_boot_vcpu();
- struct vm *vm = boot_vcpu->vm;
+ struct vm *vm = vm_get_boot_vm();
struct vcpu_locked vcpu_locked;
/*
diff --git a/src/arch/aarch64/plat/psci/hypervisor.c b/src/arch/aarch64/plat/psci/hypervisor.c
index deae71c..342333c 100644
--- a/src/arch/aarch64/plat/psci/hypervisor.c
+++ b/src/arch/aarch64/plat/psci/hypervisor.c
@@ -60,9 +60,10 @@
struct vcpu *plat_psci_cpu_resume(struct cpu *c)
{
- struct vcpu *vcpu = vcpu_get_boot_vcpu();
+ struct vm *vm = vm_get_boot_vm();
+ struct vcpu *vcpu;
- vcpu = vm_get_vcpu(vcpu->vm, cpu_index(c));
+ vcpu = vm_get_vcpu(vm, cpu_index(c));
vcpu->cpu = c;
arch_cpu_init(c);
diff --git a/src/arch/aarch64/plat/psci/spmc.c b/src/arch/aarch64/plat/psci/spmc.c
index 6b5132f..632596e 100644
--- a/src/arch/aarch64/plat/psci/spmc.c
+++ b/src/arch/aarch64/plat/psci/spmc.c
@@ -58,15 +58,12 @@
{
struct vcpu_locked vcpu_locked;
struct vcpu_locked other_world_vcpu_locked;
- struct vcpu *vcpu = vcpu_get_boot_vcpu();
- struct vm *vm;
+ struct vcpu *vcpu;
+ struct vm *vm = vm_get_boot_vm();
struct vm *other_world_vm;
struct vcpu *other_world_vcpu;
struct two_vcpu_locked vcpus_locked;
- assert(vcpu != NULL);
-
- vm = vcpu->vm;
cpu_on(c);
arch_cpu_init(c);
@@ -75,6 +72,9 @@
ffa_notifications_sri_init(c);
vcpu = vm_get_vcpu(vm, vm_is_up(vm) ? 0 : cpu_index(c));
+
+ assert(vcpu != NULL);
+
vcpu_locked = vcpu_lock(vcpu);
if (vcpu->rt_model != RTM_SP_INIT &&
diff --git a/src/ffa/spmc.c b/src/ffa/spmc.c
index 1b31a1a..ba325d6 100644
--- a/src/ffa/spmc.c
+++ b/src/ffa/spmc.c
@@ -356,6 +356,8 @@
static bool spmc_booted = false;
struct vcpu *vcpu_next = NULL;
struct vcpu *current = current_locked.vcpu;
+ struct vm *next_vm;
+ size_t cpu_indx = cpu_index(current->cpu);
if (spmc_booted) {
return false;
@@ -373,12 +375,12 @@
current->state = VCPU_STATE_WAITING;
/*
- * Pick next vCPU to be booted. Once all SPs have booted
+ * Pick next SP's vCPU to be booted. Once all SPs have booted
* (next_boot is NULL), then return execution to NWd.
*/
- vcpu_next = vcpu_get_next_boot(current);
+ next_vm = vm_get_next_boot(current->vm);
- if (vcpu_next == NULL) {
+ if (next_vm == NULL) {
dlog_notice("Finished initializing all VMs.\n");
spmc_booted = true;
return false;
@@ -387,6 +389,7 @@
current->rt_model = RTM_NONE;
current->scheduling_mode = NONE;
+ vcpu_next = vm_get_vcpu(next_vm, cpu_indx);
CHECK(vcpu_next->rt_model == RTM_SP_INIT);
arch_regs_reset(vcpu_next);
vcpu_next->cpu = current->cpu;
diff --git a/src/load.c b/src/load.c
index 14692fc..ea38f25 100644
--- a/src/load.c
+++ b/src/load.c
@@ -257,7 +257,7 @@
}
/* Updating boot list according to boot_order */
- vcpu_update_boot(vm_get_vcpu(vm_locked.vm, 0));
+ vm_update_boot(vm_locked.vm);
if (vm_locked_are_notifications_enabled(vm_locked) &&
!ffa_notifications_bitmap_create_call(
@@ -408,8 +408,8 @@
dlog_info("Loaded primary VM with %u vCPUs, entry at %#lx.\n",
vm->vcpu_count, pa_addr(primary_begin));
- /* Mark the first VM vCPU to be the first booted vCPU. */
- vcpu_update_boot(vm_get_vcpu(vm, 0));
+ /* Mark the first VM to be the first booted VM. */
+ vm_update_boot(vm);
vcpu_locked = vcpu_lock(vm_get_vcpu(vm, 0));
vcpu_on(vcpu_locked, primary_entry, params->kernel_arg);
diff --git a/src/vcpu.c b/src/vcpu.c
index 6ff71a3..3375465 100644
--- a/src/vcpu.c
+++ b/src/vcpu.c
@@ -15,8 +15,6 @@
#include "hf/std.h"
#include "hf/vm.h"
-static struct list_entry boot_list = LIST_INIT(boot_list);
-
/** GP register to be used to pass the current vCPU ID, at core bring up. */
#define PHYS_CORE_IDX_GP_REG 4
@@ -69,7 +67,6 @@
vcpu->direct_request_origin.is_ffa_req2 = false;
vcpu->direct_request_origin.vm_id = HF_INVALID_VM_ID;
vcpu->rt_model = RTM_SP_INIT;
- list_init(&vcpu->boot_list_node);
list_init(&vcpu->timer_node);
list_init(&vcpu->ipi_list_node);
}
@@ -222,71 +219,6 @@
}
}
-/**
- * The 'boot_list' is used as the start and end of the list.
- * Start: the nodes it points to is the first vCPU to boot.
- * End: the last node's next points to the entry.
- */
-static bool vcpu_is_boot_list_end(struct vcpu *vcpu)
-{
- return vcpu->boot_list_node.next == &boot_list;
-}
-
-/**
- * Gets the first partition to boot, according to Boot Protocol from FFA spec.
- */
-struct vcpu *vcpu_get_boot_vcpu(void)
-{
- assert(!list_empty(&boot_list));
-
- return CONTAINER_OF(boot_list.next, struct vcpu, boot_list_node);
-}
-
-/**
- * Returns the next element in the boot order list, if there is one.
- */
-struct vcpu *vcpu_get_next_boot(struct vcpu *vcpu)
-{
- return vcpu_is_boot_list_end(vcpu)
- ? NULL
- : CONTAINER_OF(vcpu->boot_list_node.next, struct vcpu,
- boot_list_node);
-}
-
-/**
- * Insert in boot list, sorted by `boot_order` parameter in the vm structure
- * and rooted in `first_boot_vm`.
- */
-void vcpu_update_boot(struct vcpu *vcpu)
-{
- struct vcpu *current = NULL;
-
- if (list_empty(&boot_list)) {
- list_prepend(&boot_list, &vcpu->boot_list_node);
- return;
- }
-
- /*
- * When getting to this point the first insertion should have
- * been done.
- */
- current = vcpu_get_boot_vcpu();
- assert(current != NULL);
-
- /*
- * Iterate until the position is found according to boot order, or
- * until we reach end of the list.
- */
- while (!vcpu_is_boot_list_end(current) &&
- current->vm->boot_order <= vcpu->vm->boot_order) {
- current = vcpu_get_next_boot(current);
- }
-
- current->vm->boot_order > vcpu->vm->boot_order
- ? list_prepend(¤t->boot_list_node, &vcpu->boot_list_node)
- : list_append(¤t->boot_list_node, &vcpu->boot_list_node);
-}
-
void vcpu_interrupt_clear_decrement(struct vcpu_locked vcpu_locked,
uint32_t intid)
{
diff --git a/src/vm.c b/src/vm.c
index 8e4930c..e439a49 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -28,6 +28,12 @@
static ffa_vm_count_t vm_count;
/**
+ * The `boot_list` is a special entry in the circular linked list maintained by
+ * the partition manager and serves as both the start and end of the list.
+ */
+static struct list_entry boot_list = LIST_INIT(boot_list);
+
+/**
* Counters on the status of notifications in the system. It helps to improve
* the information retrieved by the receiver scheduler.
*/
@@ -94,6 +100,7 @@
}
vm_notifications_init(vm, vcpu_count, ppool);
+ list_init(&vm->boot_list_node);
return vm;
}
@@ -1148,3 +1155,68 @@
return int_desc;
}
+
+/**
+ * The 'boot_list' is used as the start and end of the list.
+ * Start: the nodes it points to is the first VM to boot.
+ * End: the last node's next points to the entry.
+ */
+static bool vm_is_boot_list_end(struct vm *vm)
+{
+ return vm->boot_list_node.next == &boot_list;
+}
+
+/**
+ * Gets the first partition to boot, according to Boot Protocol from FF-A spec.
+ */
+struct vm *vm_get_boot_vm(void)
+{
+ assert(!list_empty(&boot_list));
+
+ return CONTAINER_OF(boot_list.next, struct vm, boot_list_node);
+}
+
+/**
+ * Returns the next element in the boot order list, if there is one.
+ */
+struct vm *vm_get_next_boot(struct vm *vm)
+{
+ return vm_is_boot_list_end(vm)
+ ? NULL
+ : CONTAINER_OF(vm->boot_list_node.next, struct vm,
+ boot_list_node);
+}
+
+/**
+ * Insert in boot list, sorted by `boot_order` parameter in the vm structure
+ * and rooted in `first_boot_vm`.
+ */
+void vm_update_boot(struct vm *vm)
+{
+ struct vm *current_vm = NULL;
+
+ if (list_empty(&boot_list)) {
+ list_prepend(&boot_list, &vm->boot_list_node);
+ return;
+ }
+
+ /*
+ * When getting to this point the first insertion should have
+ * been done.
+ */
+ current_vm = vm_get_boot_vm();
+ assert(current_vm != NULL);
+
+ /*
+ * Iterate until the position is found according to boot order, or
+ * until we reach end of the list.
+ */
+ while (!vm_is_boot_list_end(current_vm) &&
+ current_vm->boot_order <= vm->boot_order) {
+ current_vm = vm_get_next_boot(current_vm);
+ }
+
+ current_vm->boot_order > vm->boot_order
+ ? list_prepend(¤t_vm->boot_list_node, &vm->boot_list_node)
+ : list_append(¤t_vm->boot_list_node, &vm->boot_list_node);
+}
diff --git a/src/vm_test.cc b/src/vm_test.cc
index 41db62f..aed2422 100644
--- a/src/vm_test.cc
+++ b/src/vm_test.cc
@@ -95,7 +95,7 @@
TEST_F(vm, vm_boot_order)
{
struct_vm *vm_cur;
- struct_vcpu *vcpu;
+ struct_vm *vm;
std::list<struct_vm *> expected_final_order;
/*
@@ -104,27 +104,24 @@
*/
EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur, false, 0));
vm_cur->boot_order = 3;
- vcpu = vm_get_vcpu(vm_cur, 0);
- vcpu_update_boot(vcpu);
+ vm_update_boot(vm_cur);
expected_final_order.push_back(vm_cur);
- EXPECT_EQ(vcpu_get_boot_vcpu()->vm->id, vm_cur->id);
+ EXPECT_EQ(vm_get_boot_vm()->id, vm_cur->id);
/* Insertion at the head of the boot list */
EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur, false, 0));
vm_cur->boot_order = 1;
- vcpu = vm_get_vcpu(vm_cur, 0);
- vcpu_update_boot(vcpu);
+ vm_update_boot(vm_cur);
expected_final_order.push_back(vm_cur);
- EXPECT_EQ(vcpu_get_boot_vcpu()->vm->id, vm_cur->id);
+ EXPECT_EQ(vm_get_boot_vm()->id, vm_cur->id);
/* Insertion of two in the middle of the boot list */
for (uint32_t i = 0; i < 2; i++) {
EXPECT_TRUE(vm_init_next(MAX_CPUS, &ppool, &vm_cur, false, 0));
vm_cur->boot_order = 2;
- vcpu = vm_get_vcpu(vm_cur, 0);
- vcpu_update_boot(vcpu);
+ vm_update_boot(vm_cur);
expected_final_order.push_back(vm_cur);
}
@@ -136,8 +133,7 @@
*/
vm_cur = vm_find(1);
EXPECT_FALSE(vm_cur == NULL);
- vcpu = vm_get_vcpu(vm_cur, 0);
- vcpu_update_boot(vcpu);
+ vm_update_boot(vm_cur);
expected_final_order.push_back(vm_cur);
/*
@@ -151,12 +147,12 @@
expected_final_order.sort(vm::BootOrderSmallerThan);
std::list<struct_vm *>::iterator it;
- vcpu = vcpu_get_boot_vcpu();
+ vm = vm_get_boot_vm();
for (it = expected_final_order.begin();
it != expected_final_order.end(); it++) {
- EXPECT_TRUE(vcpu != NULL);
- EXPECT_EQ((*it)->id, vcpu->vm->id);
- vcpu = vcpu_get_next_boot(vcpu);
+ EXPECT_TRUE(vm != NULL);
+ EXPECT_EQ((*it)->id, vm->id);
+ vm = vm_get_next_boot(vm);
}
}