Enable loading SP config from devicetree

Extend the SP config loader component to load device and memory region
information from the SP manifest.

Signed-off-by: Balint Dobszay <balint.dobszay@arm.com>
Change-Id: I72a701751b4070944a08bc6d005061f17b69cc10
diff --git a/components/config/loader/sp/sp_config_loader.c b/components/config/loader/sp/sp_config_loader.c
index fd052f4..94c2e0b 100644
--- a/components/config/loader/sp/sp_config_loader.c
+++ b/components/config/loader/sp/sp_config_loader.c
@@ -1,15 +1,24 @@
 // SPDX-License-Identifier: BSD-3-Clause
 /*
- * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved.
  */
 
-#include <string.h>
+#include <common/fdt/fdt_helpers.h>
 #include <config/interface/config_store.h>
 #include <config/interface/config_blob.h>
 #include <platform/interface/device_region.h>
-#include "platform/interface/memory_region.h"
+#include <platform/interface/memory_region.h>
+#include <stdbool.h>
+#include <string.h>
+#include <trace.h>
+
 #include "sp_config_loader.h"
 
+/*
+ * According to the FF-A 1.0 spec: in the SP manifest the size of device and
+ * memory regions is expressed as a count of 4K pages.
+ */
+#define FFA_SP_MANIFEST_PAGE_SIZE	0x1000
 
 struct sp_param_region {
 	char name[16];
@@ -20,6 +29,7 @@
 static void load_device_regions(const struct ffa_name_value_pair *value_pair);
 static void load_memory_regions(const struct ffa_name_value_pair *value_pair);
 static void load_blob(const struct ffa_name_value_pair *value_pair);
+static bool load_fdt(const struct ffa_name_value_pair *value_pair);
 
 /**
  * Loads externally provided configuration data passed into the SP via
@@ -40,6 +50,11 @@
 
 			load_memory_regions(&init_info->nvp[param_index]);
 		}
+		else if (!memcmp(init_info->nvp[param_index].name,"TYPE_DT\0\0\0\0\0\0\0\0",
+				 sizeof(init_info->nvp[param_index].name))) {
+			if (!load_fdt(&init_info->nvp[param_index]))
+				EMSG("Failed to load SP config from DT");
+		}
 		else {
 
 			load_blob(&init_info->nvp[param_index]);
@@ -101,3 +116,115 @@
 		(const char *)value_pair->name, 0,
 		&blob, sizeof(blob));
 }
+
+static bool load_fdt(const struct ffa_name_value_pair *value_pair)
+{
+	const void *fdt = (const void *)value_pair->value;
+	size_t fdt_size = value_pair->size;
+	int root = -1, node = -1, subnode = -1, rc = -1;
+
+	/* Sanity check */
+	if (!fdt) {
+		DMSG("error: fdt NULL pointer");
+		return false;
+	}
+
+	rc = fdt_check_full(fdt, fdt_size);
+	if (rc) {
+		DMSG("error: fdt_check_full(): %d", rc);
+		return false;
+	}
+
+	/* Find root node */
+	root = fdt_path_offset(fdt, "/");
+	if (root < 0) {
+		DMSG("error: fdt_path_offset(): %d", root);
+		return false;
+	}
+
+	/* Check if it's a valid SP manifest */
+	rc = fdt_node_check_compatible(fdt, root, "arm,ffa-manifest-1.0");
+	if (rc) {
+		DMSG("error: fdt_node_check_compatible(): %d", rc);
+		return false;
+	}
+
+	/* Find memory regions */
+	node = fdt_node_offset_by_compatible(fdt, root, "arm,ffa-manifest-memory-regions");
+	if (node >= 0) {
+		fdt_for_each_subnode(subnode, fdt, node) {
+			struct memory_region memory_region = {0};
+			uint64_t base_addr = 0;
+			uint32_t page_cnt = 0;
+			const char *subnode_name = fdt_get_name(fdt, subnode, NULL);
+
+			if (!subnode_name) {
+				DMSG("error: subnode name is missing");
+				return false;
+			}
+
+			if(!dt_get_u64(fdt, subnode, "base-address", &base_addr)) {
+				DMSG("error: base-address is missing");
+				return false;
+			}
+
+			if(!dt_get_u32(fdt, subnode, "pages-count", &page_cnt)) {
+				DMSG("error: pages-count is missing");
+				return false;
+			}
+
+			strncpy(memory_region.region_name, subnode_name,
+				sizeof(memory_region.region_name));
+			memory_region.base_addr = (uintptr_t)base_addr;
+			memory_region.region_size = page_cnt * FFA_SP_MANIFEST_PAGE_SIZE;
+
+			if (!config_store_add(CONFIG_CLASSIFIER_MEMORY_REGION,
+					      memory_region.region_name, 0,
+					      &memory_region, sizeof(memory_region))) {
+				DMSG("error: failed to add memory region to config store");
+				return false;
+			}
+		}
+	}
+
+	/* Find device regions */
+	node = fdt_node_offset_by_compatible(fdt, root, "arm,ffa-manifest-device-regions");
+	if (node >= 0) {
+		fdt_for_each_subnode(subnode, fdt, node) {
+			struct device_region device_region = {0};
+			uint64_t base_addr = 0;
+			uint32_t page_cnt = 0;
+			const char *subnode_name = fdt_get_name(fdt, subnode, NULL);
+
+			if (!subnode_name) {
+				DMSG("error: subnode name is missing");
+				return false;
+			}
+
+			if(!dt_get_u64(fdt, subnode, "base-address", &base_addr)) {
+				DMSG("error: base-address is missing");
+				return false;
+			}
+
+			if (!dt_get_u32(fdt, subnode, "pages-count", &page_cnt)) {
+				DMSG("error: pages-count is missing");
+				return false;
+			}
+
+			strncpy(device_region.dev_class, subnode_name,
+				sizeof(device_region.dev_class));
+			device_region.base_addr = base_addr;
+			device_region.io_region_size = page_cnt * FFA_SP_MANIFEST_PAGE_SIZE;
+			device_region.dev_instance = 0;
+
+			if (!config_store_add(CONFIG_CLASSIFIER_DEVICE_REGION,
+					      device_region.dev_class, device_region.dev_instance,
+					      &device_region, sizeof(device_region))) {
+				DMSG("error: failed to add device region to config store");
+				return false;
+			}
+		}
+	}
+
+	return true;
+}