PSA FF-A: add memory-region parsing support

This patch implements support for parsing memory regions described in
Section 3.1, Table 10 of PSA FF-A EAC specification.
A maximum of 8 memory-regions can be defined for a given partition.

Change-Id: I348be5aa3510ef02b4b990dbbba07d443750eae7
Signed-off-by: Manish Pandey <manish.pandey2@arm.com>
diff --git a/inc/hf/manifest.h b/inc/hf/manifest.h
index 88f3b6e..fc3307b 100644
--- a/inc/hf/manifest.h
+++ b/inc/hf/manifest.h
@@ -21,6 +21,11 @@
 
 #define SP_RTX_BUF_NAME_SIZE 10
 
+#define SP_MAX_MEMORY_REGIONS 8
+
+/** Mask for getting read/write/execute permission */
+#define MM_PERM_MASK 0x7
+
 enum run_time_el {
 	EL1 = 0,
 	S_EL0,
@@ -41,6 +46,23 @@
 };
 
 /**
+ * Partition Memory region as described in PSA FFA v1.0 spec, Table 10
+ */
+struct memory_region {
+	/**
+	 * Specify PA, VA for S-EL0 partitions or IPA
+	 * for S-EL1 partitions - optional.
+	 */
+	uintptr_t base_address;
+	/** Page count - mandatory */
+	uint32_t page_count;
+	/** Memory attributes - mandatory */
+	uint32_t attributes;
+	/** Name of memory region - optional */
+	struct string name;
+};
+
+/**
  * Partition manifest as described in PSA FF-A v1.0 spec section 3.1
  */
 struct sp_manifest {
@@ -93,6 +115,9 @@
 	bool time_slice_mem;
 	/** optional - tuples SEPID/SMMUID/streamId */
 	uint32_t stream_ep_ids[1];
+
+	/** Memory regions */
+	struct memory_region mem_regions[SP_MAX_MEMORY_REGIONS];
 };
 
 /**
@@ -165,6 +190,7 @@
 	MANIFEST_ERROR_INTEGER_OVERFLOW,
 	MANIFEST_ERROR_MALFORMED_INTEGER_LIST,
 	MANIFEST_ERROR_MALFORMED_BOOLEAN,
+	MANIFEST_ERROR_MEMORY_REGION_NODE_EMPTY,
 };
 
 enum manifest_return_code manifest_init(struct mm_stage1_locked stage1_locked,
diff --git a/src/manifest.c b/src/manifest.c
index 8b40795..879ed12 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -290,6 +290,53 @@
 	return MANIFEST_SUCCESS;
 }
 
+static enum manifest_return_code parse_ffa_memory_region_node(
+	struct fdt_node *mem_node, struct memory_region *mem_regions)
+{
+	unsigned int i = 0;
+
+	dlog_verbose("  Partition memory regions\n");
+
+	if (!fdt_is_compatible(mem_node, "arm,ffa-manifest-memory-regions")) {
+		return MANIFEST_ERROR_NOT_COMPATIBLE;
+	}
+
+	if (!fdt_first_child(mem_node)) {
+		return MANIFEST_ERROR_MEMORY_REGION_NODE_EMPTY;
+	}
+
+	do {
+		dlog_verbose("    Memory Region[%u]\n", i);
+
+		TRY(read_optional_string(mem_node, "description",
+					 &mem_regions[i].name));
+		dlog_verbose("      Name: %s\n",
+			     string_data(&mem_regions[i].name));
+
+		TRY(read_optional_uint64(mem_node, "base-address",
+					 MANIFEST_INVALID_ADDRESS,
+					 &mem_regions[i].base_address));
+		dlog_verbose("      Base address:  %#x\n",
+			     mem_regions[i].base_address);
+
+		TRY(read_uint32(mem_node, "pages-count",
+				&mem_regions[i].page_count));
+		dlog_verbose("      Pages_count:  %u\n",
+			     mem_regions[i].page_count);
+
+		TRY(read_uint32(mem_node, "attributes",
+				&mem_regions[i].attributes));
+		mem_regions[i].attributes &= MM_PERM_MASK;
+		dlog_verbose("      Attributes:  %u\n",
+			     mem_regions[i].attributes);
+		i++;
+	} while (fdt_next_sibling(mem_node) && (i < SP_MAX_MEMORY_REGIONS));
+
+	dlog_verbose("    Total %u memory regions found\n", i);
+
+	return MANIFEST_SUCCESS;
+}
+
 static enum manifest_return_code parse_ffa_manifest(struct fdt *fdt,
 						    struct manifest_vm *vm)
 {
@@ -299,6 +346,7 @@
 	struct fdt_node root;
 	struct fdt_node ffa_node;
 	struct string rxtx_node_name = STRING_INIT("rx_tx-info");
+	struct string mem_region_node_name = STRING_INIT("memory-regions");
 
 	if (!fdt_find_node(fdt, "/", &root)) {
 		return MANIFEST_ERROR_NO_ROOT_NODE;
@@ -369,6 +417,13 @@
 		       (uint8_t *)&vm->sp.messaging_method));
 	dlog_verbose("  SP messaging method %d\n", vm->sp.messaging_method);
 
+	/* Parse memory-regions */
+	ffa_node = root;
+	if (fdt_find_child(&ffa_node, &mem_region_node_name)) {
+		TRY(parse_ffa_memory_region_node(&ffa_node,
+						 vm->sp.mem_regions));
+	}
+
 	return MANIFEST_SUCCESS;
 }
 
@@ -626,6 +681,8 @@
 		return "Malformed integer list property";
 	case MANIFEST_ERROR_MALFORMED_BOOLEAN:
 		return "Malformed boolean property";
+	case MANIFEST_ERROR_MEMORY_REGION_NODE_EMPTY:
+		return "Memory-region node should have at least one entry";
 	}
 
 	panic("Unexpected manifest return code.");
diff --git a/src/manifest_test.cc b/src/manifest_test.cc
index 5ac6a31..fcd109e 100644
--- a/src/manifest_test.cc
+++ b/src/manifest_test.cc
@@ -160,6 +160,11 @@
 		return StringProperty("debug_name", value);
 	}
 
+	ManifestDtBuilder &Description(const std::string_view &value)
+	{
+		return StringProperty("description", value);
+	}
+
 	ManifestDtBuilder &KernelFilename(const std::string_view &value)
 	{
 		return StringProperty("kernel_filename", value);
@@ -812,6 +817,50 @@
 		  MANIFEST_ERROR_NOT_COMPATIBLE);
 }
 
+TEST(manifest, ffa_validate_mem_regions)
+{
+	struct manifest m;
+
+	/* Not Compatible */
+	/* clang-format off */
+	std::vector<char>  dtb = ManifestDtBuilder()
+		.FfaValidManifest()
+		.StartChild("memory-regions")
+			.Compatible({ "foo,bar" })
+		.EndChild()
+		.Build();
+	/* clang-format on */
+	ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
+		  MANIFEST_ERROR_NOT_COMPATIBLE);
+
+	/* Memory regions unavailable  */
+	/* clang-format off */
+	dtb = ManifestDtBuilder()
+		.FfaValidManifest()
+		.StartChild("memory-regions")
+			.Compatible({ "arm,ffa-manifest-memory-regions" })
+		.EndChild()
+		.Build();
+	/* clang-format on */
+	ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
+		  MANIFEST_ERROR_MEMORY_REGION_NODE_EMPTY);
+
+	/* Missing Properties */
+	/* clang-format off */
+	dtb = ManifestDtBuilder()
+		.FfaValidManifest()
+		.StartChild("memory-regions")
+			.Compatible({ "arm,ffa-manifest-memory-regions" })
+			.StartChild("test-memory")
+				.Description("test-memory")
+			.EndChild()
+		.EndChild()
+		.Build();
+	/* clang-format on */
+	ASSERT_EQ(ffa_manifest_from_vec(&m, dtb),
+		  MANIFEST_ERROR_PROPERTY_NOT_FOUND);
+}
+
 TEST(manifest, ffa_valid)
 {
 	struct manifest m;
@@ -819,6 +868,15 @@
 	/* clang-format off */
 	std::vector<char>  dtb = ManifestDtBuilder()
 		.FfaValidManifest()
+		.StartChild("memory-regions")
+			.Compatible({ "arm,ffa-manifest-memory-regions" })
+			.StartChild("test-memory")
+				.Description("test-memory")
+				.Property("base-address", "<0x7100000>")
+				.Property("pages-count", "<4>")
+				.Property("attributes", "<7>")
+			.EndChild()
+		.EndChild()
 		.Build();
 	/* clang-format on */
 
@@ -835,6 +893,9 @@
 	ASSERT_EQ(m.vm[0].sp.ep_offset, 0x00001000);
 	ASSERT_EQ(m.vm[0].sp.xlat_granule, PAGE_4KB);
 	ASSERT_EQ(m.vm[0].sp.messaging_method, INDIRECT_MESSAGING);
+	ASSERT_EQ(m.vm[0].sp.mem_regions[0].base_address, 0x7100000);
+	ASSERT_EQ(m.vm[0].sp.mem_regions[0].page_count, 4);
+	ASSERT_EQ(m.vm[0].sp.mem_regions[0].attributes, 7);
 }
 
 } /* namespace */