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/Makefile b/Makefile
index b37d946..2d3a885 100644
--- a/Makefile
+++ b/Makefile
@@ -39,7 +39,7 @@
export PATH := $(PREBUILTS)/clang/bin:$(PATH)
CHECKPATCH := $(PWD)/third_party/linux/scripts/checkpatch.pl \
- --ignore BRACES,SPDX_LICENSE_TAG,VOLATILE,SPLIT_STRING,AVOID_EXTERNS,USE_SPINLOCK_T,NEW_TYPEDEFS,INITIALISED_STATIC,FILE_PATH_CHANGES,EMBEDDED_FUNCTION_NAME,SINGLE_STATEMENT_DO_WHILE_MACRO --quiet
+ --ignore BRACES,SPDX_LICENSE_TAG,VOLATILE,SPLIT_STRING,AVOID_EXTERNS,USE_SPINLOCK_T,NEW_TYPEDEFS,INITIALISED_STATIC,FILE_PATH_CHANGES,EMBEDDED_FUNCTION_NAME,SINGLE_STATEMENT_DO_WHILE_MACRO,MACRO_WITH_FLOW_CONTROL --quiet
# Select the project to build.
PROJECT ?= reference
@@ -99,9 +99,9 @@
.PHONY: license
license:
- @find src/ -name \*.S -o -name \*.c -o -name \*.cc -o -name \*.h | xargs -n1 python build/license.py --style c
- @find inc/ -name \*.S -o -name \*.c -o -name \*.cc -o -name \*.h | xargs -n1 python build/license.py --style c
- @find test/ -name \*.S -o -name \*.c -o -name \*.cc -o -name \*.h | xargs -n1 python build/license.py --style c
+ @find src/ -name \*.S -o -name \*.c -o -name \*.cc -o -name \*.h -o -name \*.dts | xargs -n1 python build/license.py --style c
+ @find inc/ -name \*.S -o -name \*.c -o -name \*.cc -o -name \*.h -o -name \*.dts | xargs -n1 python build/license.py --style c
+ @find test/ -name \*.S -o -name \*.c -o -name \*.cc -o -name \*.h -o -name \*.dts | xargs -n1 python build/license.py --style c
@find build/ -name \*.py| xargs -n1 python build/license.py --style hash
@find test/ -name \*.py| xargs -n1 python build/license.py --style hash
@find . \( -name \*.gn -o -name \*.gni \) | xargs -n1 python build/license.py --style hash
diff --git a/build/image/dtc.py b/build/image/dtc.py
new file mode 100644
index 0000000..49eeeb7
--- /dev/null
+++ b/build/image/dtc.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Wrapper around Device Tree Compiler (dtc)"""
+
+import argparse
+import os
+import subprocess
+import sys
+
+DTC = "dtc"
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("input_file")
+ parser.add_argument("output_file")
+ args = parser.parse_args()
+
+ return subprocess.call([
+ DTC,
+ "-I", "dts", "-O", "dtb",
+ "-o", args.output_file,
+ "--out-version", "17",
+ args.input_file
+ ])
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/build/image/generate_initrd.py b/build/image/generate_initrd.py
index 6fb76de..8342cfb 100644
--- a/build/image/generate_initrd.py
+++ b/build/image/generate_initrd.py
@@ -29,17 +29,27 @@
def Main():
parser = argparse.ArgumentParser()
+ parser.add_argument("--manifest", required=True)
parser.add_argument("--primary_vm", required=True)
parser.add_argument("--primary_vm_initrd")
parser.add_argument(
"--secondary_vm",
action="append",
- nargs=4,
- metavar=("MEMORY", "CORES", "NAME", "IMAGE"))
+ nargs=2,
+ metavar=("NAME", "IMAGE"))
parser.add_argument("--staging", required=True)
parser.add_argument("--output", required=True)
args = parser.parse_args()
staged_files = ["vmlinuz", "initrd.img"]
+
+ # Create staging folder if needed.
+ if not os.path.isdir(args.staging):
+ os.makedirs(args.staging)
+
+ # Prepare the manifest.
+ if args.manifest:
+ shutil.copyfile(args.manifest, os.path.join(args.staging, "manifest.dtb"))
+ staged_files += ["manifest.dtb"]
# Prepare the primary VM image.
shutil.copyfile(args.primary_vm, os.path.join(args.staging, "vmlinuz"))
# Prepare the primary VM's initrd.
@@ -48,14 +58,11 @@
else:
open(os.path.join(args.staging, "initrd.img"), "w").close()
# Prepare the secondary VMs.
- with open(os.path.join(args.staging, "vms.txt"), "w") as vms_txt:
- staged_files.append("vms.txt")
- if args.secondary_vm:
- for vm in args.secondary_vm:
- (vm_memory, vm_cores, vm_name, vm_image) = vm
- staged_files.append(vm_name)
- shutil.copy(vm_image, os.path.join(args.staging, vm_name))
- vms_txt.write("{} {} {}\n".format(vm_memory, vm_cores, vm_name))
+ if args.secondary_vm:
+ for vm in args.secondary_vm:
+ (vm_name, vm_image) = vm
+ staged_files.append(vm_name)
+ shutil.copy(vm_image, os.path.join(args.staging, vm_name))
# Package files into an initial RAM disk.
with open(args.output, "w") as initrd:
# Move into the staging directory so the file names taken by cpio don't
diff --git a/build/image/image.gni b/build/image/image.gni
index fbaf14b..bed73df 100644
--- a/build/image/image.gni
+++ b/build/image/image.gni
@@ -148,11 +148,41 @@
}
}
+template("device_tree") {
+ action_foreach(target_name) {
+ forward_variables_from(invoker,
+ [
+ "testonly",
+ "sources",
+ "deps",
+ ])
+ script = "//build/image/dtc.py"
+
+ dtb_file = "${target_out_dir}/{{source_name_part}}.dtb"
+
+ outputs = [
+ dtb_file,
+ ]
+ args = [
+ "{{source}}",
+ rebase_path(dtb_file),
+ ]
+ }
+}
+
# Build the initial RAM disk for the hypervisor.
template("initrd") {
assert(defined(invoker.primary_vm),
"initrd() must specify a \"primary_vm\" value")
+ manifest_target = "${target_name}__manifest"
+
+ device_tree(manifest_target) {
+ sources = [
+ invoker.manifest,
+ ]
+ }
+
action(target_name) {
forward_variables_from(invoker, [ "testonly" ])
script = "//build/image/generate_initrd.py"
@@ -189,27 +219,29 @@
# Add the info about the secondary VMs. The information about the VMs is
# encoded in lists with the following elements:
#
- # 1. Memory in bytes.
- # 2. Number of cores.
- # 3. File name for the VM image.
- # 4. Build target for the VM.
+ # 1. File name for the VM image.
+ # 2. Build target for the VM.
if (defined(invoker.secondary_vms)) {
foreach(vm, invoker.secondary_vms) {
- deps += [ vm[3] ]
+ deps += [ vm[1] ]
args += [
"--secondary_vm",
vm[0],
- vm[1],
- vm[2],
- rebase_path(get_label_info(vm[3], "target_out_dir") + "/" +
- get_label_info(vm[3], "name") + ".bin"),
+ rebase_path(get_label_info(vm[1], "target_out_dir") + "/" +
+ get_label_info(vm[1], "name") + ".bin"),
]
}
}
+ manifest_target_outputs = get_target_outputs(":${manifest_target}")
+ deps += [ ":${manifest_target}" ]
+ args += [
+ "--manifest",
+ rebase_path(manifest_target_outputs[0]),
+ ]
+
outputs = [
initrd_file,
- "${initrd_staging}/vms.txt",
]
}
}
diff --git a/docs/HafniumRamDisk.md b/docs/HafniumRamDisk.md
index 1a537f1..37e27db 100644
--- a/docs/HafniumRamDisk.md
+++ b/docs/HafniumRamDisk.md
@@ -5,27 +5,69 @@
* `vmlinuz` -- the kernel of the primary VM.
* `initrd.img` -- the initial ramdisk of the primary VM.
-* `vms.txt` -- optionally describes the secondary VMs.
-* kernels for the secondary VMs, whose names are described in `vms.txt`.
+* `manifest.dtb` -- hypervisor configuration file.
+* kernels for the secondary VMs, whose names are described in the manifest.
Follow the [preparing Linux](PreparingLinux.md) instructions to produce
`vmlinuz` and `initrd.img` for a basic Linux primary VM.
-## Format of `vms.txt` file
+## Manifest file
-The format is currently one line per secondary VM, with the following format:
+The format is currently a simple Device Tree:
-```shell
-<memory-size-in-bytes> <number-of-cpus> <kernel-filename>
+```
+/dts-v1/;
+
+/ {
+ hypervisor {
+ vm1 {
+ debug_name = "name";
+ };
+
+ vm2 {
+ debug_name = "name";
+ kernel_filename = "filename";
+ vcpu_count = <N>;
+ mem_size = <M>;
+ };
+ ...
+ };
+};
```
For example, the following defines two secondary VMs, the first one with 1MB of
memory, 2 CPUs and kernel image called `kernel0`, while the second one has 2MB
of memory, 4 CPUs and a kernel image called `kernel1`.
+```
+/dts-v1/;
+
+/ {
+ hypervisor {
+ vm1 {
+ debug_name = "primary VM";
+ };
+
+ vm2 {
+ debug_name = "secondary VM 1";
+ kernel_filename = "kernel0";
+ vcpu_count = <2>;
+ mem_size = <0x100000>;
+ };
+
+ vm3 {
+ debug_name = "secondary VM 2";
+ kernel_filename = "kernel1";
+ vcpu_count = <4>;
+ mem_size = <0x200000>;
+ };
+ };
+};
+```
+
+Hafnium expects the manifest in Device Tree Blob format. Compile it with:
```shell
-1048576 2 kernel0
-2097152 4 kernel1
+dtc -I dts -O dtb --out-version 17 -o manifest.dtb <manifest_source_file>
```
## Create a RAM disk for Hafnium
diff --git a/inc/hf/fdt.h b/inc/hf/fdt.h
index 2f55306..8f72856 100644
--- a/inc/hf/fdt.h
+++ b/inc/hf/fdt.h
@@ -29,13 +29,14 @@
size_t fdt_header_size(void);
uint32_t fdt_total_size(struct fdt_header *hdr);
-void fdt_dump(struct fdt_header *hdr);
+void fdt_dump(const struct fdt_header *hdr);
bool fdt_root_node(struct fdt_node *node, const struct fdt_header *hdr);
bool fdt_find_child(struct fdt_node *node, const char *child);
bool fdt_first_child(struct fdt_node *node, const char **child_name);
bool fdt_next_sibling(struct fdt_node *node, const char **sibling_name);
bool fdt_read_property(const struct fdt_node *node, const char *name,
const char **buf, uint32_t *size);
+bool fdt_parse_number(const char *data, uint32_t size, uint64_t *value);
void fdt_add_mem_reservation(struct fdt_header *hdr, uint64_t addr,
uint64_t len);
diff --git a/inc/hf/manifest.h b/inc/hf/manifest.h
new file mode 100644
index 0000000..37f26f3
--- /dev/null
+++ b/inc/hf/manifest.h
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "hf/memiter.h"
+#include "hf/spci.h"
+
+/**
+ * Holds information about one of the VMs described in the manifest.
+ */
+struct manifest_vm {
+ /* Properties defined for both primary and secondary VMs. */
+ struct memiter debug_name;
+
+ /* Properties specific to secondary VMs. */
+ struct {
+ struct memiter kernel_filename;
+ uint64_t mem_size;
+ spci_vcpu_count_t vcpu_count;
+ } secondary;
+};
+
+/**
+ * Hafnium manifest parsed from FDT.
+ */
+struct manifest {
+ spci_vm_count_t num_vms;
+ struct manifest_vm vm[MAX_VMS];
+};
+
+enum manifest_return_code {
+ MANIFEST_SUCCESS = 0,
+ MANIFEST_ERROR_CORRUPTED_FDT,
+ MANIFEST_ERROR_NO_ROOT_FDT_NODE,
+ MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE,
+ MANIFEST_ERROR_RESERVED_VM_ID,
+ MANIFEST_ERROR_NO_PRIMARY_VM,
+ MANIFEST_ERROR_TOO_MANY_VMS,
+ MANIFEST_ERROR_PROPERTY_NOT_FOUND,
+ MANIFEST_ERROR_MALFORMED_STRING,
+ MANIFEST_ERROR_MALFORMED_INTEGER,
+ MANIFEST_ERROR_INTEGER_OVERFLOW,
+};
+
+enum manifest_return_code manifest_init(struct manifest *manifest,
+ struct memiter *fdt);
+
+const char *manifest_strerror(enum manifest_return_code ret_code);
diff --git a/inc/hf/memiter.h b/inc/hf/memiter.h
index d5dbf33..a44ffef 100644
--- a/inc/hf/memiter.h
+++ b/inc/hf/memiter.h
@@ -29,4 +29,8 @@
bool memiter_parse_uint(struct memiter *it, uint64_t *value);
bool memiter_parse_str(struct memiter *it, struct memiter *str);
bool memiter_iseq(const struct memiter *it, const char *str);
+void memiter_dlog_str(struct memiter *it);
bool memiter_advance(struct memiter *it, size_t v);
+
+const void *memiter_base(struct memiter *it);
+size_t memiter_size(struct memiter *it);
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;
+}
diff --git a/test/linux/BUILD.gn b/test/linux/BUILD.gn
index 84e72bc..fcc5907 100644
--- a/test/linux/BUILD.gn
+++ b/test/linux/BUILD.gn
@@ -57,11 +57,10 @@
initrd("linux_test") {
testonly = true
+ manifest = "manifest.dts"
primary_vm = "//third_party:linux__prebuilt"
primary_initrd = ":linux_test_initrd"
secondary_vms = [ [
- "1048576",
- "1",
"socket0",
":socket_vm0",
] ]
diff --git a/test/linux/manifest.dts b/test/linux/manifest.dts
new file mode 100644
index 0000000..5437968
--- /dev/null
+++ b/test/linux/manifest.dts
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+/dts-v1/;
+
+/ {
+ hypervisor {
+ vm1 {
+ debug_name = "primary";
+ };
+
+ vm2 {
+ debug_name = "socket0";
+ vcpu_count = <1>;
+ mem_size = <0x100000>;
+ kernel_filename = "socket0";
+ };
+ };
+};
diff --git a/test/vmapi/gicv3/BUILD.gn b/test/vmapi/gicv3/BUILD.gn
index 162a84a..196f169 100644
--- a/test/vmapi/gicv3/BUILD.gn
+++ b/test/vmapi/gicv3/BUILD.gn
@@ -41,10 +41,9 @@
initrd("gicv3_test") {
testonly = true
+ manifest = "manifest.dts"
primary_vm = ":gicv3_test_vm"
secondary_vms = [ [
- "1048576",
- "1",
"services0",
"services:gicv3_service_vm0",
] ]
diff --git a/test/vmapi/gicv3/manifest.dts b/test/vmapi/gicv3/manifest.dts
new file mode 100644
index 0000000..4e90046
--- /dev/null
+++ b/test/vmapi/gicv3/manifest.dts
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+/dts-v1/;
+
+/ {
+ hypervisor {
+ vm1 {
+ debug_name = "primary";
+ };
+
+ vm2 {
+ debug_name = "services0";
+ vcpu_count = <1>;
+ mem_size = <0x100000>;
+ kernel_filename = "services0";
+ };
+ };
+};
diff --git a/test/vmapi/primary_only/BUILD.gn b/test/vmapi/primary_only/BUILD.gn
index cad9077..ecb197a 100644
--- a/test/vmapi/primary_only/BUILD.gn
+++ b/test/vmapi/primary_only/BUILD.gn
@@ -30,6 +30,6 @@
initrd("primary_only_test") {
testonly = true
-
+ manifest = "manifest.dts"
primary_vm = ":primary_only_test_vm"
}
diff --git a/test/vmapi/primary_only/manifest.dts b/test/vmapi/primary_only/manifest.dts
new file mode 100644
index 0000000..0e1b082
--- /dev/null
+++ b/test/vmapi/primary_only/manifest.dts
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+
+/dts-v1/;
+
+/ {
+ hypervisor {
+ vm1 {
+ debug_name = "primary";
+ };
+ };
+};
diff --git a/test/vmapi/primary_with_secondaries/BUILD.gn b/test/vmapi/primary_with_secondaries/BUILD.gn
index d24570f..60b78a0 100644
--- a/test/vmapi/primary_with_secondaries/BUILD.gn
+++ b/test/vmapi/primary_with_secondaries/BUILD.gn
@@ -47,23 +47,19 @@
initrd("primary_with_secondaries_test") {
testonly = true
+ manifest = "manifest.dts"
+
primary_vm = ":primary_with_secondaries_test_vm"
secondary_vms = [
[
- "1048576",
- "1",
"services0",
"services:service_vm0",
],
[
- "1048576",
- "1",
"services1",
"services:service_vm1",
],
[
- "1048576",
- "2",
"services2",
"services:service_vm2",
],
diff --git a/test/vmapi/primary_with_secondaries/manifest.dts b/test/vmapi/primary_with_secondaries/manifest.dts
new file mode 100644
index 0000000..741becd
--- /dev/null
+++ b/test/vmapi/primary_with_secondaries/manifest.dts
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+/dts-v1/;
+
+/ {
+ hypervisor {
+ vm1 {
+ debug_name = "primary";
+ };
+
+ vm2 {
+ debug_name = "services0";
+ vcpu_count = <1>;
+ mem_size = <0x100000>;
+ kernel_filename = "services0";
+ };
+
+ vm3 {
+ debug_name = "services1";
+ vcpu_count = <1>;
+ mem_size = <0x100000>;
+ kernel_filename = "services1";
+ };
+
+ vm4 {
+ debug_name = "services2";
+ vcpu_count = <2>;
+ mem_size = <0x100000>;
+ kernel_filename = "services2";
+ };
+ };
+};