VHE: Add support for a VM to be EL0 partition
Added a boolean and updated VM initialization APIs to take a parameter
that indicates that a given VM is really an EL0 FF-A partition.
Note that it is odd to associate EL0 partitions with a VM, however, all
of hafnium code base deals with VMs and vCPUs and the path of least
resistance to support EL0 partitions (S-EL0 SPs mainly) is to model an
EL0 partition as a "lightweight" VM with 1 VCPU (since all S-EL0
partitions are UP migratable per the FF-A 1.0 spec).
While the FF-A spec really only calls out S-EL0 partitions, there is no
technical reason it cannot be supported in the normal world too.
However, the main expected use case for EL0 partitions in the normal
world would be for testing/verification.
Change-Id: I67cb85bed1f324704e0cc0766563839a07a949af
Signed-off-by: Raghu Krishnamurthy <raghu.ncstate@gmail.com>
diff --git a/src/load.c b/src/load.c
index 4521e1a..b700c59 100644
--- a/src/load.c
+++ b/src/load.c
@@ -200,6 +200,9 @@
paddr_t primary_end = pa_add(primary_begin, RSIZE_MAX);
+ /* Primary VM must be a VM */
+ CHECK(manifest_vm->sp.run_time_el == EL1);
+
/*
* Load the kernel if a filename is specified in the VM manifest.
* For an FF-A partition, kernel_filename is undefined indicating
@@ -214,7 +217,7 @@
}
}
- if (!vm_init_next(MAX_CPUS, ppool, &vm)) {
+ if (!vm_init_next(MAX_CPUS, ppool, &vm, false)) {
dlog_error("Unable to initialise primary VM.\n");
return false;
}
@@ -421,8 +424,15 @@
return false;
}
}
+ /*
+ * An S-EL0 partition must contain only 1 vCPU (UP migratable) per the
+ * FF-A 1.0 spec.
+ */
+ CHECK(manifest_vm->sp.run_time_el != S_EL0 ||
+ manifest_vm->secondary.vcpu_count == 1);
- if (!vm_init_next(manifest_vm->secondary.vcpu_count, ppool, &vm)) {
+ if (!vm_init_next(manifest_vm->secondary.vcpu_count, ppool, &vm,
+ (manifest_vm->sp.run_time_el == S_EL0))) {
dlog_error("Unable to initialise VM.\n");
return false;
}
@@ -683,7 +693,7 @@
* -TrustZone (or the SPMC) when running the Hypervisor
* -the Hypervisor when running TZ/SPMC
*/
- other_world_vm = vm_init(HF_OTHER_WORLD_ID, MAX_CPUS, ppool);
+ other_world_vm = vm_init(HF_OTHER_WORLD_ID, MAX_CPUS, ppool, false);
CHECK(other_world_vm != NULL);
for (i = 0; i < MAX_CPUS; i++) {
diff --git a/src/manifest.c b/src/manifest.c
index 8883659..155a22d 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -676,6 +676,15 @@
ret_code = MANIFEST_ERROR_NOT_COMPATIBLE;
}
+ if (vm->sp.run_time_el == S_EL0 && vm->sp.execution_ctx_count != 1) {
+ dlog_error(
+ "Exception level and execution context count %s: %d "
+ "%d\n",
+ error_string, vm->sp.run_time_el,
+ vm->sp.execution_ctx_count);
+ ret_code = MANIFEST_ERROR_NOT_COMPATIBLE;
+ }
+
return ret_code;
}
diff --git a/src/vm.c b/src/vm.c
index 116de09..cb7873f 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -24,12 +24,13 @@
static struct vm *first_boot_vm;
struct vm *vm_init(ffa_vm_id_t id, ffa_vcpu_count_t vcpu_count,
- struct mpool *ppool)
+ struct mpool *ppool, bool el0_partition)
{
uint32_t i;
struct vm *vm;
if (id == HF_OTHER_WORLD_ID) {
+ CHECK(el0_partition == false);
vm = &other_world;
} else {
uint16_t vm_index = id - HF_VM_ID_OFFSET;
@@ -49,6 +50,7 @@
vm->vcpu_count = vcpu_count;
vm->mailbox.state = MAILBOX_STATE_EMPTY;
atomic_init(&vm->aborting, false);
+ vm->el0_partition = el0_partition;
if (!mm_vm_init(&vm->ptable, ppool)) {
return NULL;
@@ -70,14 +72,15 @@
}
bool vm_init_next(ffa_vcpu_count_t vcpu_count, struct mpool *ppool,
- struct vm **new_vm)
+ struct vm **new_vm, bool el0_partition)
{
if (vm_count >= MAX_VMS) {
return false;
}
/* Generate IDs based on an offset, as low IDs e.g., 0, are reserved */
- *new_vm = vm_init(vm_count + HF_VM_ID_OFFSET, vcpu_count, ppool);
+ *new_vm = vm_init(vm_count + HF_VM_ID_OFFSET, vcpu_count, ppool,
+ el0_partition);
if (*new_vm == NULL) {
return false;
}
diff --git a/src/vm_test.cc b/src/vm_test.cc
index 4a0c119..6a9e109 100644
--- a/src/vm_test.cc
+++ b/src/vm_test.cc
@@ -66,7 +66,7 @@
struct_vm *vm;
struct vm_locked vm_locked;
- EXPECT_TRUE(vm_init_next(1, &ppool, &vm));
+ EXPECT_TRUE(vm_init_next(1, &ppool, &vm, false));
vm_locked = vm_lock(vm);
ASSERT_TRUE(mm_vm_init(&vm->ptable, &ppool));
EXPECT_TRUE(vm_unmap_hypervisor(vm_locked, &ppool));
@@ -92,7 +92,7 @@
* Insertion when no call to "vm_update_boot" has been made yet.
* The "boot_list" is expected to be empty.
*/
- EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur));
+ EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur, false));
vm_cur->boot_order = 1;
vm_update_boot(vm_cur);
expected_final_order.push_back(vm_cur);
@@ -100,7 +100,7 @@
EXPECT_EQ(vm_get_first_boot()->id, vm_cur->id);
/* Insertion at the head of the boot list */
- EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur));
+ EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur, false));
vm_cur->boot_order = 3;
vm_update_boot(vm_cur);
expected_final_order.push_back(vm_cur);
@@ -109,7 +109,7 @@
/* Insertion of two in the middle of the boot list */
for (int i = 0; i < 2; i++) {
- EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur));
+ EXPECT_TRUE(vm_init_next(1, &ppool, &vm_cur, false));
vm_cur->boot_order = 2;
vm_update_boot(vm_cur);
expected_final_order.push_back(vm_cur);