aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFuad Tabba <tabba@google.com>2020-06-30 15:14:28 +0100
committerFuad Tabba <tabba@google.com>2020-07-07 08:16:02 +0000
commit50469e018d38f5baa15306f5fb964695f1ca1507 (patch)
tree5e77a92636456a86d876acc9448d397d0e79d4ea
parentdd8248f39e4907c525a55b13e009d46e06662be3 (diff)
downloadhafnium-50469e018d38f5baa15306f5fb964695f1ca1507.tar.gz
Pass an FDT to the unit test VMs
Instead of passing the assigned memory size, optionally pass the FDT to the secondary VMs. The VMs can extract the memory size, as well as other potentially useful information from the FDT. This is done to facilitate using the same unit test framework with kvm-unit-tests, which expects an FDT, as does the Linux kernel. Change-Id: I436d86b3f4d1540c1995e01e48289f39e2c23490 Signed-off-by: Fuad Tabba <tabba@google.com>
-rw-r--r--build/image/image.gni4
-rw-r--r--inc/hf/addr.h11
-rw-r--r--inc/hf/fdt_handler.h5
-rw-r--r--inc/hf/fdt_patch.h5
-rw-r--r--inc/hf/manifest.h1
-rwxr-xr-xkokoro/test.sh1
-rw-r--r--src/fdt_handler.c46
-rw-r--r--src/fdt_patch.c86
-rw-r--r--src/load.c119
-rw-r--r--src/manifest.c2
-rw-r--r--test/hftest/BUILD.gn25
-rw-r--r--test/hftest/secondary_no_fdt.c63
-rw-r--r--test/hftest/service.c24
-rw-r--r--test/linux/BUILD.gn1
-rw-r--r--test/vmapi/BUILD.gn1
-rw-r--r--test/vmapi/arch/aarch64/BUILD.gn1
-rw-r--r--test/vmapi/arch/aarch64/gicv3/BUILD.gn11
-rw-r--r--test/vmapi/arch/aarch64/gicv3/manifest.dts1
-rw-r--r--test/vmapi/arch/aarch64/gicv3/secondary.dts40
-rw-r--r--test/vmapi/primary_only/BUILD.gn1
-rw-r--r--test/vmapi/primary_with_secondaries/BUILD.gn70
-rw-r--r--test/vmapi/primary_with_secondaries/manifest.dts3
-rw-r--r--test/vmapi/primary_with_secondaries/manifest_no_fdt.dts26
-rw-r--r--test/vmapi/primary_with_secondaries/no_fdt.c38
-rw-r--r--test/vmapi/primary_with_secondaries/no_fdt_secondary.c38
-rw-r--r--test/vmapi/primary_with_secondaries/secondary.dts40
26 files changed, 645 insertions, 18 deletions
diff --git a/build/image/image.gni b/build/image/image.gni
index f9ef4f1..8f5d26d 100644
--- a/build/image/image.gni
+++ b/build/image/image.gni
@@ -303,7 +303,7 @@ template("manifest") {
fdt_overlay(target_name) {
base = base_file
overlay = overlay_file
- output = "${target_out_dir}/manifest.dtb"
+ output = "${target_out_dir}" + "/" + invoker.output
deps = [
":$base_target",
":$overlay_target",
@@ -313,7 +313,7 @@ template("manifest") {
# No overlay specified. Compile the manifest to DTB.
device_tree(target_name) {
source = invoker.source
- output = "${target_out_dir}/manifest.dtb"
+ output = "${target_out_dir}" + "/" + invoker.output
}
}
}
diff --git a/inc/hf/addr.h b/inc/hf/addr.h
index 00e77c1..efb1844 100644
--- a/inc/hf/addr.h
+++ b/inc/hf/addr.h
@@ -13,6 +13,8 @@
#include "hf/arch/types.h"
+#include "hf/check.h"
+
/** An opaque type for a physical address. */
typedef struct {
uintpaddr_t pa;
@@ -69,6 +71,15 @@ static inline ipaddr_t ipa_init(uintpaddr_t ipa)
}
/**
+ * Subtract from a physical address.
+ */
+static inline paddr_t pa_sub(paddr_t pa, size_t n)
+{
+ CHECK((uintptr_t)pa_addr(pa) >= n);
+ return pa_init(pa_addr(pa) - n);
+}
+
+/**
* Extracts the absolute intermediate physical address.
*/
static inline uintpaddr_t ipa_addr(ipaddr_t ipa)
diff --git a/inc/hf/fdt_handler.h b/inc/hf/fdt_handler.h
index 8fe0831..0a5e341 100644
--- a/inc/hf/fdt_handler.h
+++ b/inc/hf/fdt_handler.h
@@ -17,12 +17,15 @@
#define FDT_PROP_INITRD_START "linux,initrd-start"
#define FDT_PROP_INITRD_END "linux,initrd-end"
+bool fdt_struct_from_ptr(const void *fdt_ptr, struct fdt *fdt);
bool fdt_map(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
paddr_t fdt_addr, struct mpool *ppool);
bool fdt_unmap(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
struct mpool *ppool);
bool fdt_find_cpus(const struct fdt *fdt, cpu_id_t *cpu_ids, size_t *cpu_count);
-bool fdt_find_memory_ranges(const struct fdt *fdt, struct string *device_type,
+bool fdt_find_memory_ranges(const struct fdt *fdt,
+ const struct string *device_type,
struct mem_range *mem_ranges,
size_t *mem_ranges_count, size_t mem_range_limit);
bool fdt_find_initrd(const struct fdt *fdt, paddr_t *begin, paddr_t *end);
+bool fdt_get_memory_size(const struct fdt *fdt, size_t *size);
diff --git a/inc/hf/fdt_patch.h b/inc/hf/fdt_patch.h
index 4ba571c..8a5d3a0 100644
--- a/inc/hf/fdt_patch.h
+++ b/inc/hf/fdt_patch.h
@@ -15,3 +15,8 @@
/** Apply an update to the FDT. */
bool fdt_patch(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
struct boot_params_update *p, struct mpool *ppool);
+
+/** Patches a secondary VM's FDT with the location of its memory range. */
+bool fdt_patch_mem(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
+ size_t fdt_max_size, paddr_t mem_begin, paddr_t mem_end,
+ struct mpool *ppool);
diff --git a/inc/hf/manifest.h b/inc/hf/manifest.h
index a5ce7cb..9e571b1 100644
--- a/inc/hf/manifest.h
+++ b/inc/hf/manifest.h
@@ -134,6 +134,7 @@ struct manifest_vm {
struct {
uint64_t mem_size;
ffa_vcpu_count_t vcpu_count;
+ struct string fdt_filename;
} secondary;
};
};
diff --git a/kokoro/test.sh b/kokoro/test.sh
index eee457c..7951b0b 100755
--- a/kokoro/test.sh
+++ b/kokoro/test.sh
@@ -101,5 +101,6 @@ do
"${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/arch/aarch64/gicv3/gicv3_test
"${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/primary_only/primary_only_test
"${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/primary_with_secondaries/primary_with_secondaries_test
+ "${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/primary_with_secondaries/primary_with_secondaries_no_fdt
"${HFTEST_CPU[@]}" hafnium --initrd test/linux/linux_test --force-long-running --vm_args "rdinit=/test_binary --"
done
diff --git a/src/fdt_handler.c b/src/fdt_handler.c
index fc62c5c..4d0496f 100644
--- a/src/fdt_handler.c
+++ b/src/fdt_handler.c
@@ -16,6 +16,22 @@
#include "hf/std.h"
/**
+ * Initializes the FDT struct with the pointer to the FDT data (header) in
+ * fdt_ptr.
+ */
+bool fdt_struct_from_ptr(const void *fdt_ptr, struct fdt *fdt)
+{
+ size_t fdt_size;
+
+ if (!fdt_ptr || !fdt) {
+ return false;
+ }
+
+ return fdt_size_from_header(fdt_ptr, &fdt_size) &&
+ fdt_init_from_ptr(fdt, fdt_ptr, fdt_size);
+}
+
+/**
* Finds the memory region where initrd is stored.
*/
bool fdt_find_initrd(const struct fdt *fdt, paddr_t *begin, paddr_t *end)
@@ -96,7 +112,8 @@ bool fdt_find_cpus(const struct fdt *fdt, cpu_id_t *cpu_ids, size_t *cpu_count)
return true;
}
-bool fdt_find_memory_ranges(const struct fdt *fdt, struct string *device_type,
+bool fdt_find_memory_ranges(const struct fdt *fdt,
+ const struct string *device_type,
struct mem_range *mem_ranges,
size_t *mem_ranges_count, size_t mem_range_limit)
{
@@ -212,3 +229,30 @@ bool fdt_unmap(struct fdt *fdt, struct mm_stage1_locked stage1_locked,
fdt_fini(fdt);
return true;
}
+
+/**
+ * Gets the size of the first memory range from the FDT into size.
+ *
+ * The test framework expects the address space to be contiguous, therefore
+ * gets the size of the first memory range, if there is more than one range.
+ */
+bool fdt_get_memory_size(const struct fdt *fdt, size_t *size)
+{
+ const struct string memory_device_type = STRING_INIT("memory");
+ struct mem_range mem_range;
+ size_t mem_ranges_count;
+
+ if (!fdt || !size ||
+ !fdt_find_memory_ranges(fdt, &memory_device_type, &mem_range,
+ &mem_ranges_count, 1)) {
+ return false;
+ }
+
+ if (mem_ranges_count < 1) {
+ return false;
+ }
+
+ *size = pa_difference(mem_range.begin, mem_range.end);
+
+ return true;
+}
diff --git a/src/fdt_patch.c b/src/fdt_patch.c
index 439a79f..0a4a045 100644
--- a/src/fdt_patch.c
+++ b/src/fdt_patch.c
@@ -156,3 +156,89 @@ err_unmap_fdt_header:
ppool);
return false;
}
+
+bool fdt_patch_mem(struct mm_stage1_locked stage1_locked, paddr_t fdt_addr,
+ size_t fdt_max_size, paddr_t mem_begin, paddr_t mem_end,
+ struct mpool *ppool)
+{
+ int ret = 0;
+ uint64_t mem_start_addr = pa_addr(mem_begin);
+ size_t mem_size = pa_difference(mem_begin, mem_end);
+ struct fdt_header *fdt;
+ int fdt_memory_node;
+ int root_offset;
+
+ /* Map the fdt in r/w mode in preparation for updating it. */
+ fdt = mm_identity_map(stage1_locked, fdt_addr,
+ pa_add(fdt_addr, fdt_max_size),
+ MM_MODE_R | MM_MODE_W, ppool);
+
+ if (!fdt) {
+ dlog_error("Unable to map FDT in r/w mode.\n");
+ return false;
+ }
+
+ ret = fdt_check_full(fdt, fdt_max_size);
+ if (ret != 0) {
+ dlog_error("FDT failed validation. Error: %d\n", ret);
+ goto out_unmap_fdt;
+ }
+
+ /* Allow some extra room for patches to the FDT. */
+ ret = fdt_open_into(fdt, fdt, fdt_max_size);
+ if (ret != 0) {
+ dlog_error("FDT failed to open_into. Error: %d\n", ret);
+ goto out_unmap_fdt;
+ }
+
+ root_offset = fdt_path_offset(fdt, "/");
+ if (ret < 0) {
+ dlog_error("FDT cannot find root offset. Error: %d\n", ret);
+ goto out_unmap_fdt;
+ }
+
+ /* Add a node to hold the memory information. */
+ fdt_memory_node = fdt_add_subnode(fdt, root_offset, "memory");
+ if (fdt_memory_node < 0) {
+ ret = fdt_memory_node;
+ dlog_error("FDT cannot add memory node. Error: %d\n", ret);
+ goto out_unmap_fdt;
+ }
+
+ /* Set the values for the VM's memory in the FDT. */
+ ret = fdt_appendprop_addrrange(fdt, root_offset, fdt_memory_node, "reg",
+ mem_start_addr, mem_size);
+ if (ret != 0) {
+ dlog_error(
+ "FDT failed to append address range property for "
+ "memory. Error: %d\n",
+ ret);
+ goto out_unmap_fdt;
+ }
+
+ ret = fdt_appendprop_string(fdt, fdt_memory_node, "device_type",
+ "memory");
+ if (ret != 0) {
+ dlog_error(
+ "FDT failed to append device_type property for memory. "
+ "Error: %d\n",
+ ret);
+ goto out_unmap_fdt;
+ }
+
+ ret = fdt_pack(fdt);
+ if (ret != 0) {
+ dlog_error("Failed to pack FDT. Error: %d\n", ret);
+ goto out_unmap_fdt;
+ }
+
+out_unmap_fdt:
+ /* Unmap FDT. */
+ if (!mm_unmap(stage1_locked, fdt_addr, pa_add(fdt_addr, fdt_max_size),
+ ppool)) {
+ dlog_error("Unable to unmap writable FDT.\n");
+ return false;
+ }
+
+ return ret == 0;
+}
diff --git a/src/load.c b/src/load.c
index f855a56..83eed53 100644
--- a/src/load.c
+++ b/src/load.c
@@ -16,6 +16,7 @@
#include "hf/boot_params.h"
#include "hf/check.h"
#include "hf/dlog.h"
+#include "hf/fdt_patch.h"
#include "hf/layout.h"
#include "hf/memiter.h"
#include "hf/mm.h"
@@ -59,11 +60,18 @@ static bool copy_to_unmapped(struct mm_stage1_locked stage1_locked, paddr_t to,
return true;
}
+/**
+ * Loads the secondary VM's kernel.
+ * Stores the kernel size in kernel_size (if kernel_size is not NULL).
+ * Returns false if it cannot load the kernel.
+ */
static bool load_kernel(struct mm_stage1_locked stage1_locked, paddr_t begin,
paddr_t end, const struct manifest_vm *manifest_vm,
- const struct memiter *cpio, struct mpool *ppool)
+ const struct memiter *cpio, struct mpool *ppool,
+ size_t *kernel_size)
{
struct memiter kernel;
+ size_t size;
if (!cpio_get_file(cpio, &manifest_vm->kernel_filename, &kernel)) {
dlog_error("Could not find kernel file \"%s\".\n",
@@ -71,7 +79,8 @@ static bool load_kernel(struct mm_stage1_locked stage1_locked, paddr_t begin,
return false;
}
- if (pa_difference(begin, end) < memiter_size(&kernel)) {
+ size = memiter_size(&kernel);
+ if (pa_difference(begin, end) < size) {
dlog_error("Kernel is larger than available memory.\n");
return false;
}
@@ -81,6 +90,10 @@ static bool load_kernel(struct mm_stage1_locked stage1_locked, paddr_t begin,
return false;
}
+ if (kernel_size) {
+ *kernel_size = size;
+ }
+
return true;
}
@@ -137,7 +150,7 @@ static bool load_primary(struct mm_stage1_locked stage1_locked,
*/
if (!string_is_empty(&manifest_vm->kernel_filename)) {
if (!load_kernel(stage1_locked, primary_begin, primary_end,
- manifest_vm, cpio, ppool)) {
+ manifest_vm, cpio, ppool, NULL)) {
dlog_error("Unable to load primary kernel.\n");
return false;
}
@@ -235,6 +248,60 @@ out:
return ret;
}
+/**
+ * Loads the secondary VM's FDT.
+ * Stores the total allocated size for the FDT in fdt_allocated_size (if
+ * fdt_allocated_size is not NULL). The allocated size includes additional space
+ * for potential patching.
+ */
+static bool load_secondary_fdt(struct mm_stage1_locked stage1_locked,
+ paddr_t end, size_t fdt_max_size,
+ const struct manifest_vm *manifest_vm,
+ const struct memiter *cpio, struct mpool *ppool,
+ paddr_t *fdt_addr, size_t *fdt_allocated_size)
+{
+ struct memiter fdt;
+ size_t allocated_size;
+
+ CHECK(!string_is_empty(&manifest_vm->secondary.fdt_filename));
+
+ if (!cpio_get_file(cpio, &manifest_vm->secondary.fdt_filename, &fdt)) {
+ dlog_error("Cannot open the secondary VM's FDT.\n");
+ return false;
+ }
+
+ /*
+ * Ensure the FDT has one additional page at the end for patching, and
+ * and align it to the page boundary.
+ */
+ allocated_size = align_up(memiter_size(&fdt), PAGE_SIZE) + PAGE_SIZE;
+
+ if (allocated_size > fdt_max_size) {
+ dlog_error(
+ "FDT allocated space (%u) is more than the specified "
+ "maximum to use (%u).\n",
+ allocated_size, fdt_max_size);
+ return false;
+ }
+
+ /* Load the FDT to the end of the VM's allocated memory space. */
+ *fdt_addr = pa_init(pa_addr(pa_sub(end, allocated_size)));
+
+ dlog_info("Loading secondary FDT of allocated size %u at 0x%x.\n",
+ allocated_size, pa_addr(*fdt_addr));
+
+ if (!copy_to_unmapped(stage1_locked, *fdt_addr, &fdt, ppool)) {
+ dlog_error("Unable to copy FDT.\n");
+ return false;
+ }
+
+ if (fdt_allocated_size) {
+ *fdt_allocated_size = allocated_size;
+ }
+
+ return true;
+}
+
/*
* Loads a secondary VM.
*/
@@ -248,6 +315,10 @@ static bool load_secondary(struct mm_stage1_locked stage1_locked,
struct vcpu *vcpu;
ipaddr_t secondary_entry;
bool ret;
+ paddr_t fdt_addr;
+ bool has_fdt;
+ size_t kernel_size = 0;
+ const size_t mem_size = pa_difference(mem_begin, mem_end);
/*
* Load the kernel if a filename is specified in the VM manifest.
@@ -257,12 +328,37 @@ static bool load_secondary(struct mm_stage1_locked stage1_locked,
*/
if (!string_is_empty(&manifest_vm->kernel_filename)) {
if (!load_kernel(stage1_locked, mem_begin, mem_end, manifest_vm,
- cpio, ppool)) {
+ cpio, ppool, &kernel_size)) {
dlog_error("Unable to load kernel.\n");
return false;
}
}
+ has_fdt = !string_is_empty(&manifest_vm->secondary.fdt_filename);
+ if (has_fdt) {
+ /*
+ * Ensure that the FDT does not overwrite the kernel or overlap
+ * its page, for the FDT to start at a page boundary.
+ */
+ const size_t fdt_max_size =
+ mem_size - align_up(kernel_size, PAGE_SIZE);
+
+ size_t fdt_allocated_size;
+
+ if (!load_secondary_fdt(stage1_locked, mem_end, fdt_max_size,
+ manifest_vm, cpio, ppool, &fdt_addr,
+ &fdt_allocated_size)) {
+ dlog_error("Unable to load FDT.\n");
+ return false;
+ }
+
+ if (!fdt_patch_mem(stage1_locked, fdt_addr, fdt_allocated_size,
+ mem_begin, mem_end, ppool)) {
+ dlog_error("Unable to patch FDT.\n");
+ return false;
+ }
+ }
+
if (!vm_init_next(manifest_vm->secondary.vcpu_count, ppool, &vm)) {
dlog_error("Unable to initialise VM.\n");
return false;
@@ -292,8 +388,19 @@ static bool load_secondary(struct mm_stage1_locked stage1_locked,
}
vcpu = vm_get_vcpu(vm, 0);
- vcpu_secondary_reset_and_start(vcpu, secondary_entry,
- pa_difference(mem_begin, mem_end));
+
+ if (has_fdt) {
+ vcpu_secondary_reset_and_start(vcpu, secondary_entry,
+ pa_addr(fdt_addr));
+ } else {
+ /*
+ * Without an FDT, secondary VMs expect the memory size to be
+ * passed in register x0, which is what
+ * vcpu_secondary_reset_and_start does in this case.
+ */
+ vcpu_secondary_reset_and_start(vcpu, secondary_entry, mem_size);
+ }
+
ret = true;
out:
diff --git a/src/manifest.c b/src/manifest.c
index 5b586f1..003a1b9 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -265,6 +265,8 @@ static enum manifest_return_code parse_vm_common(const struct fdt_node *node,
if (vm_id != HF_PRIMARY_VM_ID) {
TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
+ TRY(read_optional_string(node, "fdt_filename",
+ &vm->secondary.fdt_filename));
}
return MANIFEST_SUCCESS;
diff --git a/test/hftest/BUILD.gn b/test/hftest/BUILD.gn
index 2e0b837..8f42d9c 100644
--- a/test/hftest/BUILD.gn
+++ b/test/hftest/BUILD.gn
@@ -42,6 +42,7 @@ source_set("hftest_secondary_vm") {
":mm",
":power_mgmt",
"//src:dlog",
+ "//src:fdt_handler",
"//src:memiter",
"//src:panic",
"//src:std",
@@ -52,6 +53,30 @@ source_set("hftest_secondary_vm") {
]
}
+# Testing framework for a secondary VM. This framework doesn't provide services
+# and expects that secondary VMs will be passed the memory size in register x0
+# instead of a pointer to the FDT.
+source_set("hftest_secondary_vm_no_fdt") {
+ testonly = true
+
+ public_configs = [ ":hftest_config" ]
+
+ sources = [
+ "secondary_no_fdt.c",
+ ]
+
+ deps = [
+ ":mm",
+ "//src:dlog",
+ "//src:panic",
+ "//src:std",
+ "//src/arch/${plat_arch}:entry",
+ "//src/arch/${plat_arch}/hftest:entry",
+ "//src/arch/${plat_arch}/hftest:power_mgmt",
+ "//vmlib/${plat_arch}:call",
+ ]
+}
+
# Testing framework for a hypervisor.
source_set("hftest_hypervisor") {
testonly = true
diff --git a/test/hftest/secondary_no_fdt.c b/test/hftest/secondary_no_fdt.c
new file mode 100644
index 0000000..c02dc5f
--- /dev/null
+++ b/test/hftest/secondary_no_fdt.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+#include <stdalign.h>
+#include <stdint.h>
+
+#include "hf/ffa.h"
+#include "hf/mm.h"
+#include "hf/std.h"
+
+#include "vmapi/hf/call.h"
+
+#include "test/hftest.h"
+
+alignas(4096) uint8_t kstack[4096];
+
+HFTEST_ENABLE();
+
+extern struct hftest_test hftest_begin[];
+extern struct hftest_test hftest_end[];
+
+static struct hftest_context global_context;
+
+void test_main_secondary(size_t mem_size);
+
+struct hftest_context *hftest_get_context(void)
+{
+ return &global_context;
+}
+
+noreturn void abort(void)
+{
+ HFTEST_LOG("Service contained failures.");
+ /* Cause a fault, as a secondary can't power down the machine. */
+ *((volatile uint8_t *)1) = 1;
+
+ /* This should never be reached, but to make the compiler happy... */
+ for (;;) {
+ }
+}
+
+noreturn void kmain(size_t mem_size)
+{
+ /*
+ * Initialize the stage-1 MMU and identity-map the entire address space.
+ */
+ if (!hftest_mm_init()) {
+ HFTEST_LOG_FAILURE();
+ HFTEST_LOG(HFTEST_LOG_INDENT "Memory initialization failed");
+ abort();
+ }
+
+ /* Run tests. */
+ test_main_secondary(mem_size);
+
+ /* Do not expect to be run again, so abort. */
+ abort();
+}
diff --git a/test/hftest/service.c b/test/hftest/service.c
index ca16206..7af6a62 100644
--- a/test/hftest/service.c
+++ b/test/hftest/service.c
@@ -9,6 +9,7 @@
#include <stdalign.h>
#include <stdint.h>
+#include "hf/fdt_handler.h"
#include "hf/ffa.h"
#include "hf/memiter.h"
#include "hf/mm.h"
@@ -69,12 +70,13 @@ noreturn void abort(void)
}
}
-noreturn void kmain(size_t memory_size)
+noreturn void kmain(const void *fdt_ptr)
{
struct memiter args;
hftest_test_fn service;
struct hftest_context *ctx;
struct ffa_value ret;
+ struct fdt fdt;
/*
* Initialize the stage-1 MMU and identity-map the entire address space.
@@ -82,9 +84,7 @@ noreturn void kmain(size_t memory_size)
if (!hftest_mm_init()) {
HFTEST_LOG_FAILURE();
HFTEST_LOG(HFTEST_LOG_INDENT "Memory initialization failed");
- for (;;) {
- /* Hang if memory init failed. */
- }
+ abort();
}
/* Prepare the context. */
@@ -104,9 +104,12 @@ noreturn void kmain(size_t memory_size)
HFTEST_LOG_FAILURE();
HFTEST_LOG(HFTEST_LOG_INDENT
"Unable to find requested service");
- for (;;) {
- /* Hang if the service was unknown. */
- }
+ abort();
+ }
+
+ if (!fdt_struct_from_ptr(fdt_ptr, &fdt)) {
+ HFTEST_LOG(HFTEST_LOG_INDENT "Unable to access the FDT");
+ abort();
}
/* Clean the context. */
@@ -115,7 +118,12 @@ noreturn void kmain(size_t memory_size)
ctx->abort = abort;
ctx->send = send;
ctx->recv = recv;
- ctx->memory_size = memory_size;
+ if (!fdt_get_memory_size(&fdt, &ctx->memory_size)) {
+ HFTEST_LOG_FAILURE();
+ HFTEST_LOG(HFTEST_LOG_INDENT
+ "No entry in the FDT on memory size details");
+ abort();
+ }
/* Pause so the next time cycles are given the service will be run. */
ffa_yield();
diff --git a/test/linux/BUILD.gn b/test/linux/BUILD.gn
index 8ef7381..e0c5c85 100644
--- a/test/linux/BUILD.gn
+++ b/test/linux/BUILD.gn
@@ -52,6 +52,7 @@ linux_initrd("linux_test_initrd") {
manifest("linux_test_manifest") {
source = "manifest.dts"
+ output = "manifest.dtb"
overlay = hftest_manifest_overlay
}
diff --git a/test/vmapi/BUILD.gn b/test/vmapi/BUILD.gn
index c8e64f5..3c81159 100644
--- a/test/vmapi/BUILD.gn
+++ b/test/vmapi/BUILD.gn
@@ -14,6 +14,7 @@ group("vmapi") {
"arch/${plat_arch}:arch",
"common:common",
"primary_only:primary_only_test",
+ "primary_with_secondaries:primary_with_secondaries_no_fdt",
"primary_with_secondaries:primary_with_secondaries_test",
]
}
diff --git a/test/vmapi/arch/aarch64/BUILD.gn b/test/vmapi/arch/aarch64/BUILD.gn
index 3ddbb2b..106d020 100644
--- a/test/vmapi/arch/aarch64/BUILD.gn
+++ b/test/vmapi/arch/aarch64/BUILD.gn
@@ -34,6 +34,7 @@ vm_kernel("aarch64_test_vm") {
manifest("aarch64_test_manifest") {
source = "manifest.dts"
+ output = "manifest.dtb"
overlay = hftest_manifest_overlay
}
diff --git a/test/vmapi/arch/aarch64/gicv3/BUILD.gn b/test/vmapi/arch/aarch64/gicv3/BUILD.gn
index 965a70f..a6f628c 100644
--- a/test/vmapi/arch/aarch64/gicv3/BUILD.gn
+++ b/test/vmapi/arch/aarch64/gicv3/BUILD.gn
@@ -34,9 +34,15 @@ vm_kernel("gicv3_test_vm") {
manifest("gicv3_test_manifest") {
source = "manifest.dts"
+ output = "manifest.dtb"
overlay = hftest_manifest_overlay
}
+device_tree("secondary_dtb") {
+ source = "secondary.dts"
+ output = "$target_out_dir/secondary.dtb"
+}
+
initrd("gicv3_test") {
testonly = true
@@ -56,5 +62,10 @@ initrd("gicv3_test") {
"services:gicv3_service_vm1",
"gicv3_service_vm1.bin",
],
+ [
+ "secondary.dtb",
+ ":secondary_dtb",
+ "secondary.dtb",
+ ],
]
}
diff --git a/test/vmapi/arch/aarch64/gicv3/manifest.dts b/test/vmapi/arch/aarch64/gicv3/manifest.dts
index 98e4288..4f05f47 100644
--- a/test/vmapi/arch/aarch64/gicv3/manifest.dts
+++ b/test/vmapi/arch/aarch64/gicv3/manifest.dts
@@ -21,6 +21,7 @@
vcpu_count = <1>;
mem_size = <0x100000>;
kernel_filename = "services1";
+ fdt_filename = "secondary.dtb";
};
};
};
diff --git a/test/vmapi/arch/aarch64/gicv3/secondary.dts b/test/vmapi/arch/aarch64/gicv3/secondary.dts
new file mode 100644
index 0000000..12a762e
--- /dev/null
+++ b/test/vmapi/arch/aarch64/gicv3/secondary.dts
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+/* A minimal FDT for the unit tests to work. */
+
+/dts-v1/;
+
+/ {
+ #size-cells = <0x02>;
+ #address-cells = <0x02>;
+ compatible = "linux,dummy-virt";
+
+ psci {
+ #size-cells = <0x01>;
+ #address-cells = <0x01>;
+ migrate = <0xc4000005>;
+ cpu_on = <0xc4000003>;
+ cpu_off = <0x84000002>;
+ cpu_suspend = <0xc4000001>;
+ method = "smc";
+ compatible = "arm,psci-1.1\0arm,psci";
+ };
+
+ cpus {
+ #size-cells = <0>;
+ #address-cells = <1>;
+
+ cpu@0 {
+ reg = <0>;
+ enable-method = "psci";
+ compatible = "arm,cortex-a57";
+ device_type = "cpu";
+ };
+ };
+};
diff --git a/test/vmapi/primary_only/BUILD.gn b/test/vmapi/primary_only/BUILD.gn
index bd50f7e..e16ae25 100644
--- a/test/vmapi/primary_only/BUILD.gn
+++ b/test/vmapi/primary_only/BUILD.gn
@@ -23,6 +23,7 @@ vm_kernel("primary_only_test_vm") {
manifest("primary_only_test_manifest") {
source = "manifest.dts"
+ output = "manifest.dtb"
overlay = hftest_manifest_overlay
}
diff --git a/test/vmapi/primary_with_secondaries/BUILD.gn b/test/vmapi/primary_with_secondaries/BUILD.gn
index 18c5cf1..3bd00c9 100644
--- a/test/vmapi/primary_with_secondaries/BUILD.gn
+++ b/test/vmapi/primary_with_secondaries/BUILD.gn
@@ -12,6 +12,65 @@ config("config") {
}
# Tests with secondary VMs.
+vm_kernel("primary_with_secondaries_no_fdt_vm") {
+ testonly = true
+ public_configs = [
+ ":config",
+ "//src/arch/aarch64:config",
+ ]
+
+ sources = [
+ "no_fdt.c",
+ ]
+
+ deps = [
+ "//src/arch/aarch64/hftest:registers",
+ "//test/hftest:hftest_primary_vm",
+ "//test/vmapi/common",
+ "//vmlib",
+ ]
+}
+
+vm_kernel("secondary_vm_no_fdt") {
+ testonly = true
+
+ sources = [
+ "no_fdt_secondary.c",
+ ]
+
+ deps = [
+ "//test/hftest:hftest_secondary_vm_no_fdt",
+ ]
+}
+
+manifest("primary_with_secondaries_no_fdt_manifest") {
+ source = "manifest_no_fdt.dts"
+ output = "manifest_no_fdt.dtb"
+ overlay = hftest_manifest_overlay
+}
+
+initrd("primary_with_secondaries_no_fdt") {
+ testonly = true
+
+ files = [
+ [
+ "manifest.dtb",
+ ":primary_with_secondaries_no_fdt_manifest",
+ "manifest_no_fdt.dtb",
+ ],
+ [
+ "primary_with_secondaries_no_fdt",
+ ":primary_with_secondaries_no_fdt_vm",
+ "primary_with_secondaries_no_fdt_vm.bin",
+ ],
+ [
+ "secondary_vm_no_fdt",
+ ":secondary_vm_no_fdt",
+ "secondary_vm_no_fdt.bin",
+ ],
+ ]
+}
+
vm_kernel("primary_with_secondaries_test_vm") {
testonly = true
public_configs = [
@@ -45,9 +104,15 @@ vm_kernel("primary_with_secondaries_test_vm") {
manifest("primary_with_secondaries_test_manifest") {
source = "manifest.dts"
+ output = "manifest.dtb"
overlay = hftest_manifest_overlay
}
+device_tree("secondary_dtb") {
+ source = "secondary.dts"
+ output = "$target_out_dir/secondary.dtb"
+}
+
initrd("primary_with_secondaries_test") {
testonly = true
@@ -77,5 +142,10 @@ initrd("primary_with_secondaries_test") {
"services:service_vm3",
"service_vm3.bin",
],
+ [
+ "secondary.dtb",
+ ":secondary_dtb",
+ "secondary.dtb",
+ ],
]
}
diff --git a/test/vmapi/primary_with_secondaries/manifest.dts b/test/vmapi/primary_with_secondaries/manifest.dts
index 373f3ec..bd07edd 100644
--- a/test/vmapi/primary_with_secondaries/manifest.dts
+++ b/test/vmapi/primary_with_secondaries/manifest.dts
@@ -21,6 +21,7 @@
vcpu_count = <1>;
mem_size = <0x100000>;
kernel_filename = "services1";
+ fdt_filename = "secondary.dtb";
};
vm3 {
@@ -28,6 +29,7 @@
vcpu_count = <1>;
mem_size = <0x100000>;
kernel_filename = "services2";
+ fdt_filename = "secondary.dtb";
};
vm4 {
@@ -35,6 +37,7 @@
vcpu_count = <2>;
mem_size = <0x100000>;
kernel_filename = "services3";
+ fdt_filename = "secondary.dtb";
};
};
};
diff --git a/test/vmapi/primary_with_secondaries/manifest_no_fdt.dts b/test/vmapi/primary_with_secondaries/manifest_no_fdt.dts
new file mode 100644
index 0000000..88c1f19
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/manifest_no_fdt.dts
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+/dts-v1/;
+
+/ {
+ hypervisor {
+ compatible = "hafnium,hafnium";
+ vm1 {
+ debug_name = "primary_with_secondaries_no_fdt";
+ kernel_filename = "primary_with_secondaries_no_fdt";
+ };
+
+ vm2 {
+ debug_name = "secondary_vm_no_fdt";
+ vcpu_count = <1>;
+ mem_size = <0x100000>;
+ kernel_filename = "secondary_vm_no_fdt";
+ };
+ };
+};
diff --git a/test/vmapi/primary_with_secondaries/no_fdt.c b/test/vmapi/primary_with_secondaries/no_fdt.c
new file mode 100644
index 0000000..0bd026d
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/no_fdt.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+/*
+ * Run tests where secondary VMs are passed the memory size directly, rather
+ * than a pointer to the FDT.
+ */
+
+#include <stdalign.h>
+#include <stdint.h>
+
+#include "hf/mm.h"
+#include "hf/static_assert.h"
+#include "hf/std.h"
+
+#include "vmapi/hf/call.h"
+
+#include "primary_with_secondary.h"
+#include "test/hftest.h"
+#include "test/vmapi/ffa.h"
+
+#define SECONDARY_VM1 (HF_VM_ID_OFFSET + 1)
+
+/**
+ * Runs the secondary VM and waits for it to yield.
+ */
+TEST(no_fdt, run_secondary)
+{
+ struct ffa_value run_res;
+
+ run_res = ffa_run(SECONDARY_VM1, 0);
+ EXPECT_EQ(run_res.func, FFA_YIELD_32);
+}
diff --git a/test/vmapi/primary_with_secondaries/no_fdt_secondary.c b/test/vmapi/primary_with_secondaries/no_fdt_secondary.c
new file mode 100644
index 0000000..193451f
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/no_fdt_secondary.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+#include "vmapi/hf/call.h"
+
+#include "test/hftest.h"
+
+/**
+ * Checks that VM entry is being passed the allocated memory size, rather than a
+ * pointer to the FDT.
+ */
+static void test_memory_size(size_t mem_size)
+{
+ /*
+ * The memory size this test expects to be allocated for the VM.
+ * This is defined in the manifest (manifest_no_fdt.dts).
+ */
+ const size_t expected_mem_size = 0x100000;
+
+ if (mem_size != expected_mem_size) {
+ FAIL("Memory size passed to VM entry %u is not expected size of"
+ "%u.",
+ mem_size, expected_mem_size);
+ }
+}
+
+void test_main_secondary(size_t mem_size)
+{
+ test_memory_size(mem_size);
+
+ /* Yield back to the primary if all tests pass. */
+ ffa_yield();
+}
diff --git a/test/vmapi/primary_with_secondaries/secondary.dts b/test/vmapi/primary_with_secondaries/secondary.dts
new file mode 100644
index 0000000..12a762e
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/secondary.dts
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2020 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+/* A minimal FDT for the unit tests to work. */
+
+/dts-v1/;
+
+/ {
+ #size-cells = <0x02>;
+ #address-cells = <0x02>;
+ compatible = "linux,dummy-virt";
+
+ psci {
+ #size-cells = <0x01>;
+ #address-cells = <0x01>;
+ migrate = <0xc4000005>;
+ cpu_on = <0xc4000003>;
+ cpu_off = <0x84000002>;
+ cpu_suspend = <0xc4000001>;
+ method = "smc";
+ compatible = "arm,psci-1.1\0arm,psci";
+ };
+
+ cpus {
+ #size-cells = <0>;
+ #address-cells = <1>;
+
+ cpu@0 {
+ reg = <0>;
+ enable-method = "psci";
+ compatible = "arm,cortex-a57";
+ device_type = "cpu";
+ };
+ };
+};