feat(manifest): parse relative addresses in memory regions

FF-A v.1. REL0 Table 5.2 specifies that memory regions can use base
address, or relative offset.

This patch updates the manifest parser to parse a new, optional
`relative-address` property in memory regions.

Change-Id: I0a2396b41de660f98e6a96c6da7097b2db843648
Signed-off-by: Karl Meakin <karl.meakin@arm.com>
diff --git a/src/manifest.c b/src/manifest.c
index 9a87ed4..9de465d 100644
--- a/src/manifest.c
+++ b/src/manifest.c
@@ -533,12 +533,13 @@
 }
 
 static enum manifest_return_code parse_ffa_memory_region_node(
-	struct fdt_node *mem_node, struct memory_region *mem_regions,
-	uint16_t *count, struct rx_tx *rxtx,
+	struct fdt_node *mem_node, uintptr_t load_address,
+	struct memory_region *mem_regions, uint16_t *count, struct rx_tx *rxtx,
 	const struct boot_params *boot_params)
 {
 	uint32_t phandle;
 	uint16_t i = 0;
+	uintptr_t relative_address;
 
 	dlog_verbose("  Partition memory regions\n");
 
@@ -558,11 +559,38 @@
 		dlog_verbose("      Name: %s\n",
 			     string_data(&mem_regions[i].name));
 
-		TRY(read_uint64(mem_node, "base-address",
-				&mem_regions[i].base_address));
+		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_optional_uint64(mem_node, "relative-address",
+					 MANIFEST_INVALID_ADDRESS,
+					 &relative_address));
+		dlog_verbose("      Relative address:  %#x\n",
+			     relative_address);
+
+		if (mem_regions[i].base_address == MANIFEST_INVALID_ADDRESS &&
+		    relative_address == MANIFEST_INVALID_ADDRESS) {
+			return MANIFEST_ERROR_PROPERTY_NOT_FOUND;
+		}
+
+		if (mem_regions[i].base_address != MANIFEST_INVALID_ADDRESS &&
+		    relative_address != MANIFEST_INVALID_ADDRESS) {
+			return MANIFEST_ERROR_BASE_ADDRESS_AND_RELATIVE_ADDRESS;
+		}
+
+		if (relative_address != MANIFEST_INVALID_ADDRESS &&
+		    relative_address > UINT64_MAX - load_address) {
+			return MANIFEST_ERROR_INTEGER_OVERFLOW;
+		}
+
+		if (relative_address != MANIFEST_INVALID_ADDRESS) {
+			mem_regions[i].base_address =
+				load_address + relative_address;
+		}
+
 		TRY(read_uint32(mem_node, "pages-count",
 				&mem_regions[i].page_count));
 		dlog_verbose("      Pages_count:  %u\n",
@@ -1122,7 +1150,8 @@
 	ffa_node = root;
 	if (fdt_find_child(&ffa_node, &mem_region_node_name)) {
 		TRY(parse_ffa_memory_region_node(
-			&ffa_node, vm->partition.mem_regions,
+			&ffa_node, vm->partition.load_addr,
+			vm->partition.mem_regions,
 			&vm->partition.mem_region_count, &vm->partition.rxtx,
 			boot_params));
 	}
@@ -1394,6 +1423,8 @@
 		return "RX and TX buffers should be of same size";
 	case MANIFEST_ERROR_MEM_REGION_EMPTY:
 		return "Memory region should have at least one page";
+	case MANIFEST_ERROR_BASE_ADDRESS_AND_RELATIVE_ADDRESS:
+		return "Base and relative addresses are mutually exclusive";
 	case MANIFEST_ERROR_MEM_REGION_OVERLAP:
 		return "Memory region overlaps with one already allocated";
 	case MANIFEST_ERROR_MEM_REGION_UNALIGNED: