feat(fake_host): Add support for per PE sysreg emulation

This patch adds support to emulate per PE sysreg access to the host_util
library on the fake_host platform. This allows to emulate different CPUs
by calling host_util_cpuid() with the desired CPU to emulate.

Signed-off-by: Javier Almansa Sobrino <javier.almansasobrino@arm.com>
Change-Id: I97c7f299ff78a9cf5dc78f353d7526a0209cdc6b
diff --git a/lib/arch/include/fake_host/cpuid.h b/lib/arch/include/fake_host/cpuid.h
index c4c6a6e..1176a93 100644
--- a/lib/arch/include/fake_host/cpuid.h
+++ b/lib/arch/include/fake_host/cpuid.h
@@ -6,9 +6,11 @@
 #ifndef CPUID_H
 #define CPUID_H
 
+#include <arch_helpers.h>
+
 static inline unsigned int my_cpuid(void)
 {
-	return 0UL;
+	return (unsigned int)read_tpidr_el2();
 }
 
 #endif /* CPUID_H */
diff --git a/plat/host/common/include/host_utils.h b/plat/host/common/include/host_utils.h
index 77a3b5e..645ea3b 100644
--- a/plat/host/common/include/host_utils.h
+++ b/plat/host/common/include/host_utils.h
@@ -42,18 +42,35 @@
 typedef void (*wr_cb_t)(u_register_t val, u_register_t *reg);
 
 /*
- * Structure to hold the callback pointers and value of the emulated sysreg.
+ * Structure to hold the callback pointers for register access emulation.
  */
 struct sysreg_cb {
-	char sysreg[MAX_SYSREG_NAME_LEN + 1U];
 	rd_cb_t rd_cb;
 	wr_cb_t wr_cb;
-	u_register_t value;
+	/*
+	 * Pointer to the instance of the register corresponding to the
+	 * current CPU
+	 */
+	u_register_t *reg;
+};
+
+/*
+ * Structure to hold register access emulation data.
+ */
+struct sysreg_data {
+	char name[MAX_SYSREG_NAME_LEN + 1U];
+	struct sysreg_cb callbacks;
+	u_register_t value[MAX_CPUS];
 };
 
 /*
  * Return the callbacks for a given sysreg or NULL
  * if no callbacks are found.
+ *
+ * Arguments:
+ *	name - String containing the name of the sysreg. The name cannot exceed
+ *	       MAX_SYSREG_NAME_LEN (excluding the terminatig NULL character)
+ *	       or it will be truncated.
  */
 struct sysreg_cb *host_util_get_sysreg_cb(char *name);
 
@@ -64,10 +81,10 @@
  * read or write operations. This allows to control what to return on
  * a read or how to process a write.
  *
- * Argsuments:
+ * Arguments:
  *	name - String containing the name of the sysreg. The name of
  *	       the sysreg cannot exceed MAX_SYSREG_NAME_LEN (excluding
- *	       the terminating null character) or it will be truncated.
+ *	       the terminating NULL character) or it will be truncated.
  *	rd_cb - Callback to be invoked on a read operation.
  *	wr_cb - Callback to be invoked on a write operation.
  *	init - Value used as reset value for the sysreg.
@@ -87,7 +104,7 @@
  * Arguments:
  *	name - String containing the name of the sysreg. The name of
  *	       the sysreg cannot exceed MAX_SYSREG_NAME_LEN (excluding
- *	       the terminating null character) or it will be truncated.
+ *	       the terminating NULL character) or it will be truncated.
  *	init - Value used as reset value for the sysreg.
  *
  * Returns:
@@ -105,4 +122,9 @@
  */
 unsigned long host_util_get_granule_base(void);
 
+/*
+ * Set the current CPU emulated by the platform.
+ */
+void host_util_set_cpuid(unsigned int cpuid);
+
 #endif /* HOST_UTILS_H */
diff --git a/plat/host/common/src/host_harness_cmn.c b/plat/host/common/src/host_harness_cmn.c
index 0a4cf05..42c6776 100644
--- a/plat/host/common/src/host_harness_cmn.c
+++ b/plat/host/common/src/host_harness_cmn.c
@@ -91,7 +91,7 @@
 		return 0UL;
 	}
 
-	return callbacks->rd_cb(&callbacks->value);
+	return callbacks->rd_cb(callbacks->reg);
 }
 
 void host_write_sysreg(char *reg_name, u_register_t v)
@@ -104,7 +104,7 @@
 	 */
 	if (callbacks != NULL) {
 		if (callbacks->wr_cb != NULL) {
-			callbacks->wr_cb(v, &callbacks->value);
+			callbacks->wr_cb(v, callbacks->reg);
 		}
 	}
 }
diff --git a/plat/host/common/src/host_utils.c b/plat/host/common/src/host_utils.c
index f1f9103..082f24f 100644
--- a/plat/host/common/src/host_utils.c
+++ b/plat/host/common/src/host_utils.c
@@ -11,8 +11,9 @@
 #include <string.h>
 #include <xlat_tables.h>
 
-static struct sysreg_cb callbacks[SYSREG_MAX_CBS];
+static struct sysreg_data sysregs[SYSREG_MAX_CBS];
 static unsigned int installed_cb_idx;
+static unsigned int current_cpuid;
 
 /*
  * Allocate memory to emulate physical memory to initialize the
@@ -39,9 +40,16 @@
 struct sysreg_cb *host_util_get_sysreg_cb(char *name)
 {
 	for (unsigned int i = 0U; i < SYSREG_MAX_CBS; i++) {
-		if (strncmp(name, &callbacks[i].sysreg[0],
+		if (strncmp(name, &sysregs[i].name[0],
 			    MAX_SYSREG_NAME_LEN) == 0) {
-			return &callbacks[i];
+
+			/*
+			 * Get a pointer to the register value for the
+			 * current CPU.
+			 */
+			sysregs[i].callbacks.reg =
+					&(sysregs[i].value[current_cpuid]);
+			return &sysregs[i].callbacks;
 		}
 	}
 
@@ -52,18 +60,23 @@
 			    u_register_t init)
 {
 	if (installed_cb_idx < SYSREG_MAX_CBS) {
-		callbacks[installed_cb_idx].rd_cb = rd_cb;
-		callbacks[installed_cb_idx].wr_cb = wr_cb;
-		callbacks[installed_cb_idx].value = init;
+		sysregs[installed_cb_idx].callbacks.rd_cb = rd_cb;
+		sysregs[installed_cb_idx].callbacks.wr_cb = wr_cb;
+		sysregs[installed_cb_idx].callbacks.reg =
+							(u_register_t *)NULL;
 
-		(void)strncpy(&(callbacks[installed_cb_idx].sysreg[0]),
+		for (unsigned int i = 0U; i < MAX_CPUS; i++) {
+			sysregs[installed_cb_idx].value[i] = init;
+		}
+
+		(void)strncpy(&(sysregs[installed_cb_idx].name[0]),
 			      &name[0], MAX_SYSREG_NAME_LEN);
 
 		/*
 		 * Add a string termination character in case the
 		 * name were truncated.
 		 */
-		callbacks[installed_cb_idx].sysreg[MAX_SYSREG_NAME_LEN] = '\0';
+		sysregs[installed_cb_idx].name[MAX_SYSREG_NAME_LEN] = '\0';
 
 		++installed_cb_idx;
 
@@ -76,8 +89,8 @@
 void host_util_reset_all_sysreg_cb(void)
 {
 
-	(void)memset((void *)callbacks, 0,
-		     sizeof(struct sysreg_cb) * SYSREG_MAX_CBS);
+	(void)memset((void *)sysregs, 0,
+		     sizeof(struct sysreg_data) * SYSREG_MAX_CBS);
 
 	installed_cb_idx = 0U;
 }
@@ -85,10 +98,17 @@
 int host_util_set_default_sysreg_cb(char *name, u_register_t init)
 {
 	return host_util_set_sysreg_cb(name, &sysreg_rd_cb,
-				     &sysreg_wr_cb, init);
+				       &sysreg_wr_cb, init);
 }
 
 unsigned long host_util_get_granule_base(void)
 {
 	return (unsigned long)granules_buffer;
 }
+
+void host_util_set_cpuid(unsigned int cpuid)
+{
+	assert(cpuid < MAX_CPUS);
+
+	current_cpuid = cpuid;
+}
diff --git a/plat/host/host_build/src/host_setup.c b/plat/host/host_build/src/host_setup.c
index ead28aa..d1b3c98 100644
--- a/plat/host/host_build/src/host_setup.c
+++ b/plat/host/host_build/src/host_setup.c
@@ -33,6 +33,12 @@
 static void setup_sysreg_and_boot_manifest(void)
 {
 	/*
+	 * By default, set current CPU to be CPU0.
+	 * Fake host doesn't support using more than one CPU.
+	 */
+	host_util_set_cpuid(0U);
+
+	/*
 	 * Initialize ID_AA64MMFR0_EL1 with a physical address
 	 * range of 48 bits (PARange bits set to 0b0101)
 	 */
@@ -49,9 +55,15 @@
 	/* SCTLR_EL2 is reset to zero */
 	(void)host_util_set_default_sysreg_cb("sctlr_el2", 0UL);
 
+	/* TPIDR_EL2 is reset to zero */
+	(void)host_util_set_default_sysreg_cb("tpidr_el2", 0UL);
+
 	/* Initialize the boot manifest */
 	boot_manifest->version = RMM_EL3_IFC_SUPPORTED_VERSION;
 	boot_manifest->plat_data = (uintptr_t)NULL;
+
+	/* Store current CPU ID into tpidr_el2 */
+	write_tpidr_el2(0);
 }
 
 /*
@@ -69,10 +81,10 @@
 	(void)argc;
 	(void)argv;
 
-	setup_sysreg_and_boot_manifest();
-
 	VERBOSE("RMM: Beginning of Fake Host execution\n");
 
+	setup_sysreg_and_boot_manifest();
+
 	plat_setup(0UL,
 		   RMM_EL3_IFC_ABI_VERSION,
 		   RMM_EL3_MAX_CPUS,