feat(manifest): check interrupt IDs are unique
Add a checks to prevent two endpoints from declaring the same
interrupt ID resource for a device peripheral.
Also add macros used to access the bitmask that tracks
interrupts.
Signed-off-by: Daniel Boulby <daniel.boulby@arm.com>
Change-Id: I5074e8d4209588a6c8f62ee06e0eb05b20cac387
diff --git a/src/manifest.c b/src/manifest.c
index d8350fc..cd70e11 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -38,6 +38,42 @@
(HF_OTHER_WORLD_ID < HF_VM_ID_BASE),
"TrustZone VM ID clashes with normal VM range.");
+/**
+ * A struct to keep track of fields that are allocated by partitions
+ * in the manifest.
+ */
+struct allocated_fields {
+ uint32_t intids[HF_NUM_INTIDS / INTERRUPT_REGISTER_BITS];
+};
+
+/**
+ * Ensure the allocated_fields struct will fit in the entry allocated from
+ * the mpool.
+ */
+static_assert(sizeof(struct allocated_fields) < MM_PPOOL_ENTRY_SIZE,
+ "More space required for the allocated_fields struct.");
+static struct allocated_fields *allocated_fields;
+
+/**
+ * Allocates memory for the allocated fields struct in the given memory
+ * pool.
+ * Returns true if the memory is successfully allocated.
+ */
+static bool manifest_allocated_fields_init(struct mpool *ppool)
+{
+ allocated_fields = (struct allocated_fields *)mpool_alloc(ppool);
+ return allocated_fields != NULL;
+}
+
+/**
+ * Frees the memory used for the allocated field struct in the given
+ * memory pool.
+ */
+static void manifest_allocated_fields_deinit(struct mpool *ppool)
+{
+ mpool_free(ppool, allocated_fields);
+}
+
static inline size_t count_digits(ffa_vm_id_t vm_id)
{
size_t digits = 0;
@@ -450,6 +486,7 @@
struct uint32list_iter list;
uint16_t i = 0;
uint32_t j = 0;
+ uint32_t *allocated_intids = allocated_fields->intids;
dlog_verbose(" Partition Device Regions\n");
@@ -509,8 +546,25 @@
j = 0;
while (uint32list_has_next(&list) &&
j < PARTITION_MAX_INTERRUPTS_PER_DEVICE) {
+ uint32_t intid;
+ uint32_t intid_index;
+ uint32_t intid_mask;
+
TRY(uint32list_get_next(
&list, &dev_regions[i].interrupts[j].id));
+ intid = dev_regions[i].interrupts[j].id;
+ intid_index = INTID_INDEX(intid);
+ intid_mask = INTID_MASK(1U, intid);
+
+ dlog_verbose(" ID = %u\n", intid);
+
+ if ((allocated_intids[intid_index] & intid_mask) !=
+ 0U) {
+ return MANIFEST_ERROR_INTERRUPT_ID_REPEATED;
+ }
+
+ allocated_intids[intid_index] |= intid_mask;
+
if (uint32list_has_next(&list)) {
TRY(uint32list_get_next(&list,
&dev_regions[i]
@@ -520,8 +574,7 @@
return MANIFEST_ERROR_MALFORMED_INTEGER_LIST;
}
- dlog_verbose(" ID = %u, attributes = %u\n",
- dev_regions[i].interrupts[j].id,
+ dlog_verbose(" attributes = %u\n",
dev_regions[i].interrupts[j].attributes);
j++;
}
@@ -900,6 +953,12 @@
memset_s(manifest, sizeof(*manifest), 0, sizeof(*manifest));
+ /* Allocate space in the ppool for tracking the allocated fields. */
+ if (!manifest_allocated_fields_init(ppool)) {
+ panic("Unable to allocated space for allocated fields "
+ "struct.\n");
+ }
+
if (!fdt_init_from_memiter(&fdt, manifest_fdt)) {
return MANIFEST_ERROR_FILE_SIZE; /* TODO */
}
@@ -971,6 +1030,12 @@
return MANIFEST_SUCCESS;
}
+/* Free resources used when parsing the manifest. */
+void manifest_deinit(struct mpool *ppool)
+{
+ manifest_allocated_fields_deinit(ppool);
+}
+
const char *manifest_strerror(enum manifest_return_code ret_code)
{
switch (ret_code) {
@@ -1017,6 +1082,8 @@
return "Memory permission should be RO, RW or RX";
case MANIFEST_ERROR_ARGUMENTS_LIST_EMPTY:
return "Arguments-list node should have at least one argument";
+ case MANIFEST_ERROR_INTERRUPT_ID_REPEATED:
+ return "Interrupt ID already assigned to another endpoint";
}
panic("Unexpected manifest return code.");