manifest: Require 'compatible' property in 'hypervisor' node
Other hypervisors also use a 'hypervisor' node in the FDT to pass
config information to the VMs. Add requirement to specify which
hypervisors the node is compatible with. The 'compatible' property
is a list of NULL-separated strings in the format
"<manufacturer>,<model>". Following the naming convention of other
projects, we will use "hafnium,hafnium" as a match-all-versions value,
and later add "hafnium,hafnium_<version>" values to match specific
releases of Hafnium.
Bug: 117551352
Change-Id: Ie6dadcdace37318d4d122e80fefe989715ee9cc9
diff --git a/src/manifest.c b/src/manifest.c
index e5c62a1..4cf490a 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -64,7 +64,11 @@
return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
}
- if (data[size - 1] != '\0') {
+ /*
+ * Require that the value contains exactly one NULL character and that
+ * it is the last byte.
+ */
+ if (memchr(data, '\0', size) != &data[size - 1]) {
return MANIFEST_ERROR_MALFORMED_STRING;
}
@@ -106,6 +110,87 @@
return MANIFEST_SUCCESS;
}
+/**
+ * Represents the value of property whose type is a list of strings. These are
+ * encoded as one contiguous byte buffer with NULL-separated entries.
+ */
+struct stringlist_iter {
+ struct memiter mem_it;
+};
+
+static enum manifest_return_code read_stringlist(const struct fdt_node *node,
+ const char *property,
+ struct stringlist_iter *out)
+{
+ const char *data;
+ uint32_t size;
+
+ if (!fdt_read_property(node, property, &data, &size)) {
+ return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
+ }
+
+ /*
+ * Require that the value ends with a NULL terminator. Other NULL
+ * characters separate the string list entries.
+ */
+ if (data[size - 1] != '\0') {
+ return MANIFEST_ERROR_MALFORMED_STRING_LIST;
+ }
+
+ memiter_init(&out->mem_it, data, size - 1);
+ return MANIFEST_SUCCESS;
+}
+
+static bool stringlist_has_next(const struct stringlist_iter *list)
+{
+ return memiter_size(&list->mem_it) > 0;
+}
+
+static void stringlist_get_next(struct stringlist_iter *list,
+ struct memiter *out)
+{
+ const char *mem_base = memiter_base(&list->mem_it);
+ size_t mem_size = memiter_size(&list->mem_it);
+ const char *null_term;
+
+ CHECK(stringlist_has_next(list));
+
+ null_term = memchr(mem_base, '\0', mem_size);
+ if (null_term == NULL) {
+ /*
+ * NULL terminator not found, this is the last entry.
+ * Set entry memiter to the entire byte range and advance list
+ * memiter to the end of the byte range.
+ */
+ memiter_init(out, mem_base, mem_size);
+ memiter_advance(&list->mem_it, mem_size);
+ } else {
+ /*
+ * Found NULL terminator. Set entry memiter to byte range
+ * [base, null) and move list memiter past the terminator.
+ */
+ size_t entry_size = null_term - mem_base;
+
+ memiter_init(out, mem_base, entry_size);
+ memiter_advance(&list->mem_it, entry_size + 1);
+ }
+}
+
+static bool stringlist_contains(const struct stringlist_iter *list,
+ const char *str)
+{
+ struct stringlist_iter it = *list;
+ struct memiter entry;
+
+ while (stringlist_has_next(&it)) {
+ stringlist_get_next(&it, &entry);
+ if (memiter_iseq(&entry, str)) {
+ return true;
+ }
+ }
+ return false;
+}
+
static enum manifest_return_code parse_vm(struct fdt_node *node,
struct manifest_vm *vm,
spci_vm_id_t vm_id)
@@ -128,6 +213,7 @@
{
char vm_name_buf[VM_NAME_BUF_SIZE];
struct fdt_node hyp_node;
+ struct stringlist_iter compatible_list;
size_t i = 0;
bool found_primary_vm = false;
@@ -145,6 +231,12 @@
return MANIFEST_ERROR_NO_HYPERVISOR_FDT_NODE;
}
+ /* Check "compatible" property. */
+ TRY(read_stringlist(&hyp_node, "compatible", &compatible_list));
+ if (!stringlist_contains(&compatible_list, "hafnium,hafnium")) {
+ return MANIFEST_ERROR_NOT_COMPATIBLE;
+ }
+
/* 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;
@@ -197,6 +289,8 @@
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_NOT_COMPATIBLE:
+ return "Hypervisor manifest entry not compatible with Hafnium";
case MANIFEST_ERROR_RESERVED_VM_ID:
return "Manifest defines a VM with a reserved ID";
case MANIFEST_ERROR_NO_PRIMARY_VM:
@@ -208,6 +302,8 @@
return "Property not found";
case MANIFEST_ERROR_MALFORMED_STRING:
return "Malformed string property";
+ case MANIFEST_ERROR_MALFORMED_STRING_LIST:
+ return "Malformed string list property";
case MANIFEST_ERROR_MALFORMED_INTEGER:
return "Malformed integer property";
case MANIFEST_ERROR_INTEGER_OVERFLOW: