Introduce a DT-based manifest
These are first steps towards a new manifest format. A new "device_tree"
build target is introduced to compile DTS files to DTB, and
`generate_initrd.py` now does not produce a "vms.txt" file. Instead
"initrd" targets are expected to provide a path to a DTS manifest in the
format:
/dts-v1/;
/ {
hypervisor {
vm1 {
debug_name = "primary";
};
vm2 {
debug_name = "secondary1";
kernel_filename = "filename";
vcpu_count = <N>;
mem_size = <M>;
};
...
};
};
The information provided in the manifest matches "vms.txt".
Bug: 117551352
Test: manifest_test.cc
Test: used by hftest
Change-Id: I6b70bd44d2b110c4f7a6b971018c834084b6d8c4
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 8c5b6e5..f6f55dc 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -48,6 +48,7 @@
"abort.c",
"api.c",
"cpu.c",
+ "manifest.c",
"panic.c",
"spci_architected_message.c",
"vm.c",
@@ -151,6 +152,7 @@
"api_test.cc",
"fdt_handler_test.cc",
"fdt_test.cc",
+ "manifest_test.cc",
"mm_test.cc",
"mpool_test.cc",
"spci_test.cc",
diff --git a/src/fdt.c b/src/fdt.c
index 0ba1177..d9cd71d 100644
--- a/src/fdt.c
+++ b/src/fdt.c
@@ -16,8 +16,10 @@
#include "hf/fdt.h"
+#include <stdalign.h>
#include <stdint.h>
+#include "hf/check.h"
#include "hf/dlog.h"
#include "hf/std.h"
@@ -56,6 +58,8 @@
#define FDT_VERSION 17
#define FDT_MAGIC 0xd00dfeed
+#define FDT_TOKEN_ALIGNMENT sizeof(uint32_t)
+
static void fdt_tokenizer_init(struct fdt_tokenizer *t, const char *strs,
const char *begin, const char *end)
{
@@ -66,7 +70,7 @@
static void fdt_tokenizer_align(struct fdt_tokenizer *t)
{
- t->cur = (char *)align_up(t->cur, 4);
+ t->cur = (char *)align_up(t->cur, FDT_TOKEN_ALIGNMENT);
}
static bool fdt_tokenizer_uint32(struct fdt_tokenizer *t, uint32_t *res)
@@ -276,6 +280,43 @@
return false;
}
+/**
+ * Helper method for parsing 32/64-bit uints from FDT data.
+ */
+bool fdt_parse_number(const char *data, uint32_t size, uint64_t *value)
+{
+ union {
+ volatile uint64_t v;
+ char a[8];
+ } t;
+
+ /* FDT values should be aligned to 32-bit boundary. */
+ CHECK(is_aligned(data, FDT_TOKEN_ALIGNMENT));
+
+ switch (size) {
+ case sizeof(uint32_t):
+ /*
+ * Assert that `data` is already sufficiently aligned to
+ * dereference as uint32_t. We cannot use static_assert()
+ * because alignof() is not an expression under ISO C11.
+ */
+ CHECK(alignof(uint32_t) <= FDT_TOKEN_ALIGNMENT);
+ *value = be32toh(*(uint32_t *)data);
+ return true;
+ case sizeof(uint64_t):
+ /*
+ * ARMv8 requires `data` to be realigned to 64-bit boundary
+ * to dereference as uint64_t. May not be needed on other
+ * architectures.
+ */
+ memcpy_s(t.a, sizeof(t.a), data, sizeof(uint64_t));
+ *value = be64toh(t.v);
+ return true;
+ default:
+ return false;
+ }
+}
+
bool fdt_first_child(struct fdt_node *node, const char **child_name)
{
struct fdt_tokenizer t;
@@ -333,7 +374,7 @@
return false;
}
-void fdt_dump(struct fdt_header *hdr)
+void fdt_dump(const struct fdt_header *hdr)
{
uint32_t token;
size_t depth = 0;
diff --git a/src/fdt_handler.c b/src/fdt_handler.c
index db8c0ef..900f766 100644
--- a/src/fdt_handler.c
+++ b/src/fdt_handler.c
@@ -17,6 +17,7 @@
#include "hf/fdt_handler.h"
#include "hf/boot_params.h"
+#include "hf/check.h"
#include "hf/cpu.h"
#include "hf/dlog.h"
#include "hf/fdt.h"
@@ -24,24 +25,6 @@
#include "hf/mm.h"
#include "hf/std.h"
-static uint64_t convert_number(const char *data, uint32_t size)
-{
- union {
- volatile uint64_t v;
- char a[8];
- } t;
-
- switch (size) {
- case sizeof(uint32_t):
- return be32toh(*(uint32_t *)data);
- case sizeof(uint64_t):
- memcpy_s(t.a, sizeof(t.a), data, sizeof(uint64_t));
- return be64toh(t.v);
- default:
- return 0;
- }
-}
-
static bool fdt_read_number(const struct fdt_node *node, const char *name,
uint64_t *value)
{
@@ -55,7 +38,7 @@
switch (size) {
case sizeof(uint32_t):
case sizeof(uint64_t):
- *value = convert_number(data, size);
+ CHECK(fdt_parse_number(data, size, value));
break;
default:
@@ -163,13 +146,18 @@
/* Get all entries for this CPU. */
while (size >= address_size) {
+ uint64_t value;
+
if (*cpu_count >= MAX_CPUS) {
dlog("Found more than %d CPUs\n", MAX_CPUS);
return;
}
- cpu_ids[(*cpu_count)++] =
- convert_number(data, address_size);
+ if (!fdt_parse_number(data, address_size, &value)) {
+ dlog("Could not parse CPU id\n");
+ return;
+ }
+ cpu_ids[(*cpu_count)++] = value;
size -= address_size;
data += address_size;
@@ -219,9 +207,12 @@
/* Traverse all memory ranges within this node. */
while (size >= entry_size) {
- uintpaddr_t addr = convert_number(data, address_size);
- size_t len =
- convert_number(data + address_size, size_size);
+ uintpaddr_t addr;
+ size_t len;
+
+ CHECK(fdt_parse_number(data, address_size, &addr));
+ CHECK(fdt_parse_number(data + address_size, size_size,
+ &len));
if (mem_range_index < MAX_MEM_RANGES) {
p->mem_ranges[mem_range_index].begin =
diff --git a/src/load.c b/src/load.c
index 602cfa8..341f276 100644
--- a/src/load.c
+++ b/src/load.c
@@ -22,6 +22,7 @@
#include "hf/boot_params.h"
#include "hf/dlog.h"
#include "hf/layout.h"
+#include "hf/manifest.h"
#include "hf/memiter.h"
#include "hf/mm.h"
#include "hf/plat/console.h"
@@ -40,8 +41,10 @@
* so the data must be available without the cache.
*/
static bool copy_to_unmapped(struct mm_stage1_locked stage1_locked, paddr_t to,
- const void *from, size_t size, struct mpool *ppool)
+ struct memiter *from_it, struct mpool *ppool)
{
+ const void *from = memiter_base(from_it);
+ size_t size = memiter_size(from_it);
paddr_t to_end = pa_add(to, size);
void *ptr;
@@ -120,8 +123,7 @@
}
dlog("Copying primary to %p\n", pa_addr(primary_begin));
- if (!copy_to_unmapped(stage1_locked, primary_begin, it.next,
- it.limit - it.next, ppool)) {
+ if (!copy_to_unmapped(stage1_locked, primary_begin, &it, ppool)) {
dlog("Unable to relocate kernel for primary vm.\n");
return false;
}
@@ -254,12 +256,11 @@
struct boot_params_update *update, struct mpool *ppool)
{
struct vm *primary;
- struct memiter it;
- struct memiter name;
- uint64_t mem;
- uint64_t cpu;
+ struct manifest manifest;
+ struct memiter manifest_fdt;
struct mem_range mem_ranges_available[MAX_MEM_RANGES];
size_t i;
+ enum manifest_return_code manifest_ret;
static_assert(
sizeof(mem_ranges_available) == sizeof(params->mem_ranges),
@@ -272,61 +273,74 @@
primary = vm_find(HF_PRIMARY_VM_ID);
- if (!find_file(cpio, "vms.txt", &it)) {
- dlog("vms.txt is missing\n");
- return true;
- }
-
/* Round the last addresses down to the page size. */
for (i = 0; i < params->mem_ranges_count; ++i) {
mem_ranges_available[i].end = pa_init(align_down(
pa_addr(mem_ranges_available[i].end), PAGE_SIZE));
}
- while (memiter_parse_uint(&it, &mem) && memiter_parse_uint(&it, &cpu) &&
- memiter_parse_str(&it, &name)) {
+ if (!find_file(cpio, "manifest.dtb", &manifest_fdt)) {
+ dlog("Could not find \"manifest.dtb\" in cpio.\n");
+ return false;
+ }
+
+ manifest_ret = manifest_init(&manifest, &manifest_fdt);
+ if (manifest_ret != MANIFEST_SUCCESS) {
+ dlog("Could not parse manifest: %s.\n",
+ manifest_strerror(manifest_ret));
+ return false;
+ }
+
+ for (i = 0; i < manifest.num_vms; ++i) {
+ struct manifest_vm *manifest_vm = &manifest.vm[i];
+ spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
+ struct vm *vm;
+ struct vcpu *vcpu;
struct memiter kernel;
+ uint64_t mem_size;
paddr_t secondary_mem_begin;
paddr_t secondary_mem_end;
ipaddr_t secondary_entry;
- const char *p;
- struct vm *vm;
- struct vcpu *vcpu;
- dlog("Loading ");
- for (p = name.next; p != name.limit; ++p) {
- dlog("%c", *p);
- }
- dlog("\n");
-
- if (!memiter_find_file(cpio, &name, &kernel)) {
- dlog("Unable to load kernel\n");
+ if (vm_id == HF_PRIMARY_VM_ID) {
continue;
}
- /* Round up to page size. */
- mem = (mem + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1);
+ dlog("Loading VM%d: ", (int)vm_id);
+ memiter_dlog_str(&manifest_vm->debug_name);
+ dlog(".\n");
- if (mem < kernel.limit - kernel.next) {
+ if (!memiter_find_file(cpio,
+ &manifest_vm->secondary.kernel_filename,
+ &kernel)) {
+ dlog("Could not find kernel file \"");
+ memiter_dlog_str(
+ &manifest_vm->secondary.kernel_filename);
+ dlog("\".\n");
+ continue;
+ }
+
+ mem_size = align_up(manifest_vm->secondary.mem_size, PAGE_SIZE);
+ if (mem_size < memiter_size(&kernel)) {
dlog("Kernel is larger than available memory\n");
continue;
}
- if (!carve_out_mem_range(
- mem_ranges_available, params->mem_ranges_count, mem,
- &secondary_mem_begin, &secondary_mem_end)) {
- dlog("Not enough memory (%u bytes)\n", mem);
+ if (!carve_out_mem_range(mem_ranges_available,
+ params->mem_ranges_count, mem_size,
+ &secondary_mem_begin,
+ &secondary_mem_end)) {
+ dlog("Not enough memory (%u bytes)\n", mem_size);
continue;
}
if (!copy_to_unmapped(stage1_locked, secondary_mem_begin,
- kernel.next, kernel.limit - kernel.next,
- ppool)) {
+ &kernel, ppool)) {
dlog("Unable to copy kernel\n");
continue;
}
- if (!vm_init(cpu, ppool, &vm)) {
+ if (!vm_init(manifest_vm->secondary.vcpu_count, ppool, &vm)) {
dlog("Unable to initialise VM\n");
continue;
}
@@ -347,7 +361,8 @@
return false;
}
- dlog("Loaded with %u vcpus, entry at %#x\n", cpu,
+ dlog("Loaded with %u vcpus, entry at %#x\n",
+ manifest_vm->secondary.vcpu_count,
pa_addr(secondary_mem_begin));
vcpu = vm_get_vcpu(vm, 0);
diff --git a/src/manifest.c b/src/manifest.c
new file mode 100644
index 0000000..e5c62a1
--- /dev/null
+++ b/src/manifest.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hf/manifest.h"
+
+#include "hf/addr.h"
+#include "hf/check.h"
+#include "hf/fdt.h"
+#include "hf/static_assert.h"
+#include "hf/std.h"
+
+#define TRY(expr) \
+ do { \
+ enum manifest_return_code ret_code = (expr); \
+ if (ret_code != MANIFEST_SUCCESS) { \
+ return ret_code; \
+ } \
+ } while (0)
+
+#define VM_NAME_BUF_SIZE (2 + 5 + 1) /* "vm" + number + null terminator */
+static_assert(MAX_VMS <= 99999, "Insufficient VM_NAME_BUF_SIZE");
+
+/**
+ * Generates a string with the two letters "vm" followed by an integer.
+ * Assumes `buf` is of size VM_NAME_BUF_SIZE.
+ */
+static const char *generate_vm_node_name(char *buf, spci_vm_id_t vm_id)
+{
+ static const char *digits = "0123456789";
+ char *ptr = buf + VM_NAME_BUF_SIZE;
+
+ *(--ptr) = '\0';
+ do {
+ *(--ptr) = digits[vm_id % 10];
+ vm_id /= 10;
+ } while (vm_id);
+ *(--ptr) = 'm';
+ *(--ptr) = 'v';
+
+ return ptr;
+}
+
+static enum manifest_return_code read_string(const struct fdt_node *node,
+ const char *property,
+ struct memiter *out)
+{
+ const char *data;
+ uint32_t size;
+
+ if (!fdt_read_property(node, property, &data, &size)) {
+ return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
+ }
+
+ if (data[size - 1] != '\0') {
+ return MANIFEST_ERROR_MALFORMED_STRING;
+ }
+
+ memiter_init(out, data, size - 1);
+ return MANIFEST_SUCCESS;
+}
+
+static enum manifest_return_code read_uint64(const struct fdt_node *node,
+ const char *property,
+ uint64_t *out)
+{
+ const char *data;
+ uint32_t size;
+
+ if (!fdt_read_property(node, property, &data, &size)) {
+ return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
+ }
+
+ if (!fdt_parse_number(data, size, out)) {
+ return MANIFEST_ERROR_MALFORMED_INTEGER;
+ }
+
+ return MANIFEST_SUCCESS;
+}
+
+static enum manifest_return_code read_uint16(const struct fdt_node *node,
+ const char *property,
+ uint16_t *out)
+{
+ uint64_t value;
+
+ TRY(read_uint64(node, property, &value));
+
+ if (value > UINT16_MAX) {
+ return MANIFEST_ERROR_INTEGER_OVERFLOW;
+ }
+
+ *out = (uint16_t)value;
+ return MANIFEST_SUCCESS;
+}
+
+static enum manifest_return_code parse_vm(struct fdt_node *node,
+ struct manifest_vm *vm,
+ spci_vm_id_t vm_id)
+{
+ TRY(read_string(node, "debug_name", &vm->debug_name));
+ if (vm_id != HF_PRIMARY_VM_ID) {
+ TRY(read_string(node, "kernel_filename",
+ &vm->secondary.kernel_filename));
+ TRY(read_uint64(node, "mem_size", &vm->secondary.mem_size));
+ TRY(read_uint16(node, "vcpu_count", &vm->secondary.vcpu_count));
+ }
+ return MANIFEST_SUCCESS;
+}
+
+/**
+ * Parse manifest from FDT.
+ */
+enum manifest_return_code manifest_init(struct manifest *manifest,
+ struct memiter *fdt)
+{
+ char vm_name_buf[VM_NAME_BUF_SIZE];
+ struct fdt_node hyp_node;
+ size_t i = 0;
+ bool found_primary_vm = false;
+
+ memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
+
+ /* Find hypervisor node. */
+ if (!fdt_root_node(&hyp_node,
+ (const struct fdt_header *)memiter_base(fdt))) {
+ return MANIFEST_ERROR_CORRUPTED_FDT;
+ }
+ if (!fdt_find_child(&hyp_node, "")) {
+ return MANIFEST_ERROR_NO_ROOT_FDT_NODE;
+ }
+ if (!fdt_find_child(&hyp_node, "hypervisor")) {
+ return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
+ }
+
+ /* Iterate over reserved VM IDs and check no such nodes exist. */
+ for (i = 0; i < HF_VM_ID_OFFSET; i++) {
+ spci_vm_id_t vm_id = (spci_vm_id_t)i;
+ struct fdt_node vm_node = hyp_node;
+ const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
+
+ if (fdt_find_child(&vm_node, vm_name)) {
+ return MANIFEST_ERROR_RESERVED_VM_ID;
+ }
+ }
+
+ /* Iterate over VM nodes until we find one that does not exist. */
+ for (i = 0; i <= MAX_VMS; ++i) {
+ spci_vm_id_t vm_id = HF_VM_ID_OFFSET + i;
+ struct fdt_node vm_node = hyp_node;
+ const char *vm_name = generate_vm_node_name(vm_name_buf, vm_id);
+
+ if (!fdt_find_child(&vm_node, vm_name)) {
+ break;
+ }
+
+ if (i == MAX_VMS) {
+ return MANIFEST_ERROR_TOO_MANY_VMS;
+ }
+
+ if (vm_id == HF_PRIMARY_VM_ID) {
+ CHECK(found_primary_vm == false); /* sanity check */
+ found_primary_vm = true;
+ }
+
+ manifest->num_vms = i + 1;
+ TRY(parse_vm(&vm_node, &manifest->vm[i], vm_id));
+ }
+
+ if (!found_primary_vm) {
+ return MANIFEST_ERROR_NO_PRIMARY_VM;
+ }
+
+ return MANIFEST_SUCCESS;
+}
+
+const char *manifest_strerror(enum manifest_return_code ret_code)
+{
+ switch (ret_code) {
+ case MANIFEST_SUCCESS:
+ return "Success";
+ case MANIFEST_ERROR_CORRUPTED_FDT:
+ return "Manifest failed FDT validation";
+ case MANIFEST_ERROR_NO_ROOT_FDT_NODE:
+ return "Could not find root node of manifest";
+ case MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE:
+ return "Could not find \"hypervisor\" node in manifest";
+ case MANIFEST_ERROR_RESERVED_VM_ID:
+ return "Manifest defines a VM with a reserved ID";
+ case MANIFEST_ERROR_NO_PRIMARY_VM:
+ return "Manifest does not contain a primary VM entry";
+ case MANIFEST_ERROR_TOO_MANY_VMS:
+ return "Manifest specifies more VMs than Hafnium has "
+ "statically allocated space for";
+ case MANIFEST_ERROR_PROPERTY_NOT_FOUND:
+ return "Property not found";
+ case MANIFEST_ERROR_MALFORMED_STRING:
+ return "Malformed string property";
+ case MANIFEST_ERROR_MALFORMED_INTEGER:
+ return "Malformed integer property";
+ case MANIFEST_ERROR_INTEGER_OVERFLOW:
+ return "Integer overflow";
+ }
+
+ panic("Unexpected manifest return code.");
+}
diff --git a/src/manifest_test.cc b/src/manifest_test.cc
new file mode 100644
index 0000000..3a86a7a
--- /dev/null
+++ b/src/manifest_test.cc
@@ -0,0 +1,305 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+extern "C" {
+#include "hf/manifest.h"
+}
+
+namespace
+{
+using ::testing::Eq;
+using ::testing::NotNull;
+
+/*
+ * DTB files compiled with:
+ * $ dtc -I dts -O dtb --out-version 17 test.dts | xxd -i
+ */
+
+/*
+ * /dts-v1/;
+ *
+ * / {
+ * };
+ *
+ */
+constexpr uint8_t dtb_empty_root[] = {
+ 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09};
+
+TEST(manifest, empty_root)
+{
+ struct manifest m;
+ struct memiter it;
+
+ memiter_init(&it, dtb_empty_root, sizeof(dtb_empty_root));
+ ASSERT_EQ(manifest_init(&m, &it),
+ MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE);
+}
+
+/*
+ * /dts-v1/;
+ *
+ * / {
+ * hypervisor {
+ * };
+ * };
+ *
+ */
+constexpr uint8_t dtb_no_vms[] = {
+ 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
+ 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09};
+
+TEST(manifest, no_vms)
+{
+ struct manifest m;
+ struct memiter it;
+
+ memiter_init(&it, dtb_no_vms, sizeof(dtb_no_vms));
+ ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_NO_PRIMARY_VM);
+}
+
+/*
+ * /dts-v1/;
+ *
+ * / {
+ * hypervisor {
+ * vm1 {
+ * debug_name = "primary_vm";
+ * };
+ * vm0 {
+ * debug_name = "reserved_vm";
+ * vcpu_count = <1>;
+ * mem_size = <4096>;
+ * kernel_filename = "kernel";
+ * };
+ * };
+ * };
+ *
+ */
+constexpr uint8_t dtb_reserved_vmid[] = {
+ 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f,
+ 0x00, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
+ 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x76, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b,
+ 0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f,
+ 0x76, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
+ 0x76, 0x6d, 0x30, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c,
+ 0x00, 0x00, 0x00, 0x00, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64,
+ 0x5f, 0x76, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x10, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1f,
+ 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09,
+ 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x76,
+ 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x00, 0x6d, 0x65,
+ 0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6b, 0x65, 0x72, 0x6e, 0x65,
+ 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00};
+
+TEST(manifest, reserved_vmid)
+{
+ struct manifest m;
+ struct memiter it;
+
+ memiter_init(&it, dtb_reserved_vmid, sizeof(dtb_reserved_vmid));
+ ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_RESERVED_VM_ID);
+}
+
+/*
+ * /dts-v1/;
+ *
+ * / {
+ * hypervisor {
+ * vm1 {
+ * debug_name = "";
+ * };
+ * vm2 {
+ * debug_name = "";
+ * vcpu_count = <65535>;
+ * mem_size = <0>;
+ * kernel_filename = "";
+ * };
+ * };
+ * };
+ *
+ */
+constexpr uint8_t dtb_last_valid_vcpu_count[] = {
+ 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f,
+ 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
+ 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x76, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x01, 0x76, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b,
+ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6e, 0x61,
+ 0x6d, 0x65, 0x00, 0x76, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
+ 0x74, 0x00, 0x6d, 0x65, 0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6b,
+ 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61,
+ 0x6d, 0x65, 0x00};
+
+/* Same as above, set "vcpu_count" to 65536. */
+constexpr uint8_t dtb_first_invalid_vcpu_count[] = {
+ 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x00, 0xf3, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f,
+ 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
+ 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x76, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x01, 0x76, 0x6d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x09, 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6e, 0x61,
+ 0x6d, 0x65, 0x00, 0x76, 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x75, 0x6e,
+ 0x74, 0x00, 0x6d, 0x65, 0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6b,
+ 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61,
+ 0x6d, 0x65, 0x00};
+
+TEST(manifest, vcpu_count_limit)
+{
+ struct manifest m;
+ struct memiter it;
+
+ memiter_init(&it, dtb_last_valid_vcpu_count,
+ sizeof(dtb_last_valid_vcpu_count));
+ ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
+ ASSERT_EQ(m.num_vms, 2);
+ ASSERT_EQ(m.vm[1].secondary.vcpu_count, UINT16_MAX);
+
+ memiter_init(&it, dtb_first_invalid_vcpu_count,
+ sizeof(dtb_first_invalid_vcpu_count));
+ ASSERT_EQ(manifest_init(&m, &it), MANIFEST_ERROR_INTEGER_OVERFLOW);
+}
+
+/*
+ * /dts-v1/;
+ *
+ * / {
+ * hypervisor {
+ * vm1 {
+ * debug_name = "primary_vm";
+ * };
+ * vm3 {
+ * debug_name = "second_secondary_vm";
+ * vcpu_count = <43>;
+ * mem_size = <0x12345>;
+ * kernel_filename = "second_kernel";
+ * };
+ * vm2 {
+ * debug_name = "first_secondary_vm";
+ * vcpu_count = <42>;
+ * mem_size = <12345>;
+ * kernel_filename = "first_kernel";
+ * };
+ * };
+ * };
+ *
+ */
+constexpr uint8_t dtb_valid[] = {
+ 0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x00, 0x38,
+ 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11,
+ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f,
+ 0x00, 0x00, 0x01, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x68, 0x79, 0x70, 0x65,
+ 0x72, 0x76, 0x69, 0x73, 0x6f, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x76, 0x6d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0b,
+ 0x00, 0x00, 0x00, 0x00, 0x70, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x5f,
+ 0x76, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
+ 0x76, 0x6d, 0x33, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x5f, 0x73,
+ 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x61, 0x72, 0x79, 0x5f, 0x76, 0x6d, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b,
+ 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
+ 0x00, 0x00, 0x00, 0x16, 0x00, 0x01, 0x23, 0x45, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1f, 0x73, 0x65, 0x63, 0x6f,
+ 0x6e, 0x64, 0x5f, 0x6b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x76, 0x6d, 0x32, 0x00,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64,
+ 0x61, 0x72, 0x79, 0x5f, 0x76, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x2a,
+ 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x16,
+ 0x00, 0x00, 0x30, 0x39, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d,
+ 0x00, 0x00, 0x00, 0x1f, 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x6b, 0x65,
+ 0x72, 0x6e, 0x65, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09,
+ 0x64, 0x65, 0x62, 0x75, 0x67, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x76,
+ 0x63, 0x70, 0x75, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x00, 0x6d, 0x65,
+ 0x6d, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x00, 0x6b, 0x65, 0x72, 0x6e, 0x65,
+ 0x6c, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00};
+
+TEST(manifest, valid)
+{
+ struct manifest m;
+ struct manifest_vm *vm;
+ struct memiter it;
+
+ memiter_init(&it, dtb_valid, sizeof(dtb_valid));
+
+ ASSERT_EQ(manifest_init(&m, &it), MANIFEST_SUCCESS);
+ ASSERT_EQ(m.num_vms, 3);
+
+ vm = &m.vm[0];
+ ASSERT_TRUE(memiter_iseq(&vm->debug_name, "primary_vm"));
+
+ vm = &m.vm[1];
+ ASSERT_TRUE(memiter_iseq(&vm->debug_name, "first_secondary_vm"));
+ ASSERT_EQ(vm->secondary.vcpu_count, 42);
+ ASSERT_EQ(vm->secondary.mem_size, 12345);
+ ASSERT_TRUE(
+ memiter_iseq(&vm->secondary.kernel_filename, "first_kernel"));
+
+ vm = &m.vm[2];
+ ASSERT_TRUE(memiter_iseq(&vm->debug_name, "second_secondary_vm"));
+ ASSERT_EQ(vm->secondary.vcpu_count, 43);
+ ASSERT_EQ(vm->secondary.mem_size, 0x12345);
+ ASSERT_TRUE(
+ memiter_iseq(&vm->secondary.kernel_filename, "second_kernel"));
+}
+
+} /* namespace */
diff --git a/src/memiter.c b/src/memiter.c
index 5ff54ee..3dec9c6 100644
--- a/src/memiter.c
+++ b/src/memiter.c
@@ -16,6 +16,7 @@
#include "hf/memiter.h"
+#include "hf/dlog.h"
#include "hf/std.h"
/**
@@ -93,6 +94,19 @@
}
/**
+ * Prints the contents of memory covered by the iterator to dlog. It does *not*
+ * assume that the string is null-terminated.
+ */
+void memiter_dlog_str(struct memiter *it)
+{
+ const char *p;
+
+ for (p = it->next; p < it->limit; ++p) {
+ dlog("%c", *p);
+ }
+}
+
+/**
* Parses the next string that represents a 64-bit number.
*/
bool memiter_parse_uint(struct memiter *it, uint64_t *value)
@@ -138,3 +152,16 @@
return true;
}
+
+const void *memiter_base(struct memiter *it)
+{
+ return (const void *)it->next;
+}
+
+/**
+ * Returns the number of bytes in interval [it.next, it.limit).
+ */
+size_t memiter_size(struct memiter *it)
+{
+ return it->limit - it->next;
+}