feat(manifest): add overlap checks for SPMC memory

Check that SPMC memory range allocated does not overlap with secure
memory ranges defined in the SP manifests. Overlaps should be prevented
by the system integrator ensuring the SPMC load address range the secure
memory region ranges do not overlap. So this patch is intended to
catch any misconfigurations there.

The patch also includes a small cleanup by moving the
mem_regions_index global variable into the manifest data struct
where the array it refers to also lives.

Signed-off-by: Daniel Boulby <daniel.boulby@arm.com>
Change-Id: I3ffe30ccc3fd25de7d13a6aee1a52138582f689e
diff --git a/src/manifest.c b/src/manifest.c
index 5435408..cc5923e 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -22,6 +22,7 @@
 #include "hf/dlog.h"
 #include "hf/fdt.h"
 #include "hf/ffa.h"
+#include "hf/layout.h"
 #include "hf/mm.h"
 #include "hf/mpool.h"
 #include "hf/sp_pkg.h"
@@ -70,6 +71,7 @@
 	struct mem_range mem_regions[PARTITION_MAX_MEMORY_REGIONS * MAX_VMS +
 				     PARTITION_MAX_DEVICE_REGIONS * MAX_VMS +
 				     MAX_VMS];
+	size_t mem_regions_index;
 	uint64_t boot_order_values[BOOT_ORDER_MAP_ENTRIES];
 };
 
@@ -82,8 +84,6 @@
 	 MM_PPOOL_ENTRY_SIZE);
 
 static struct manifest_data *manifest_data;
-/* Index used to track the number of memory regions allocated. */
-static size_t allocated_mem_regions_index = 0;
 
 static bool check_boot_order(uint16_t boot_order)
 {
@@ -141,12 +141,6 @@
 	memset_s(manifest_data, sizeof(struct manifest_data), 0,
 		 sizeof(struct manifest_data));
 	mpool_add_chunk(ppool, manifest_data, manifest_data_ppool_entries);
-
-	/**
-	 * Reset the index used for tracking the number of memory regions
-	 * allocated.
-	 */
-	allocated_mem_regions_index = 0;
 }
 
 static inline size_t count_digits(ffa_id_t vm_id)
@@ -553,7 +547,8 @@
  * manifests, as well address space defined from their load address.
  */
 static enum manifest_return_code check_and_record_memory_used(
-	uintptr_t base_address, uint32_t page_count)
+	uintptr_t base_address, uint32_t page_count,
+	struct mem_range *mem_ranges, size_t *mem_regions_index)
 {
 	bool overlap_of_regions;
 
@@ -572,17 +567,15 @@
 	}
 
 	overlap_of_regions = is_memory_region_within_ranges(
-		base_address, page_count, manifest_data->mem_regions,
-		allocated_mem_regions_index);
+		base_address, page_count, mem_ranges, *mem_regions_index);
 
 	if (!overlap_of_regions) {
 		paddr_t begin = pa_init(base_address);
 
-		manifest_data->mem_regions[allocated_mem_regions_index].begin =
-			begin;
-		manifest_data->mem_regions[allocated_mem_regions_index].end =
+		mem_ranges[*mem_regions_index].begin = begin;
+		mem_ranges[*mem_regions_index].end =
 			pa_add(begin, page_count * PAGE_SIZE - 1);
-		allocated_mem_regions_index++;
+		(*mem_regions_index)++;
 
 		return MANIFEST_SUCCESS;
 	}
@@ -687,8 +680,10 @@
 			mem_regions[i].base_address, mem_regions[i].page_count,
 			mem_regions[i].attributes, boot_params));
 
-		TRY(check_and_record_memory_used(mem_regions[i].base_address,
-						 mem_regions[i].page_count));
+		TRY(check_and_record_memory_used(
+			mem_regions[i].base_address, mem_regions[i].page_count,
+			manifest_data->mem_regions,
+			&manifest_data->mem_regions_index));
 
 		TRY(read_optional_uint32(mem_node, "smmu-id",
 					 MANIFEST_INVALID_ID,
@@ -849,8 +844,10 @@
 		dlog_verbose("      Pages_count: %u\n",
 			     dev_regions[i].page_count);
 
-		TRY(check_and_record_memory_used(dev_regions[i].base_address,
-						 dev_regions[i].page_count));
+		TRY(check_and_record_memory_used(
+			dev_regions[i].base_address, dev_regions[i].page_count,
+			manifest_data->mem_regions,
+			&manifest_data->mem_regions_index));
 
 		TRY(read_uint32(dev_node, "attributes",
 				&dev_regions[i].attributes));
@@ -1528,6 +1525,10 @@
 	struct fdt_node hyp_node;
 	size_t i = 0;
 	bool found_primary_vm = false;
+	const size_t spmc_size =
+		align_up(pa_difference(layout_text_begin(), layout_image_end()),
+			 PAGE_SIZE);
+	const size_t spmc_page_count = spmc_size / PAGE_SIZE;
 
 	if (boot_params->mem_ranges_count == 0 &&
 	    boot_params->ns_mem_ranges_count == 0) {
@@ -1544,6 +1545,22 @@
 		panic("Unable to allocate manifest data.\n");
 	}
 
+	/*
+	 * Add SPMC load address range to memory ranges to track to ensure
+	 * no partitions overlap with this memory.
+	 * The system integrator should have prevented this by defining the
+	 * secure memory region ranges so as not to overlap the SPMC load
+	 * address range. Therefore, this code is intended to catch any
+	 * potential misconfigurations there.
+	 */
+	if (is_aligned(pa_addr(layout_text_begin()), PAGE_SIZE) &&
+	    spmc_page_count != 0) {
+		TRY(check_and_record_memory_used(
+			pa_addr(layout_text_begin()), spmc_page_count,
+			manifest_data->mem_regions,
+			&manifest_data->mem_regions_index));
+	}
+
 	manifest = &manifest_data->manifest;
 	*manifest_ret = manifest;
 
@@ -1624,8 +1641,9 @@
 			 * + memory size) has been used by other partition.
 			 */
 			TRY(check_and_record_memory_used(
-				manifest->vm[i].partition.load_addr,
-				page_count));
+				manifest->vm[i].partition.load_addr, page_count,
+				manifest_data->mem_regions,
+				&manifest_data->mem_regions_index));
 		} else {
 			TRY(parse_vm(&vm_node, &manifest->vm[i], vm_id));
 		}