feat(pmu): add PMU support for Realms

This patch adds support for using PMU in Realms.
It adds 'bool pmu_enabled' and 'unsigned int pmu_num_cnts'
variables in 'struct rd' and 'struct rec.realm_info'.

Signed-off-by: AlexeiFedorov <Alexei.Fedorov@arm.com>
Change-Id: I13aad600a0215ba66d25be12ede5f4b86e6b018a
diff --git a/runtime/core/run.c b/runtime/core/run.c
index f136947..64f4b93 100644
--- a/runtime/core/run.c
+++ b/runtime/core/run.c
@@ -11,6 +11,7 @@
 #include <cpuid.h>
 #include <exit.h>
 #include <fpu_helpers.h>
+#include <pmu.h>
 #include <rec.h>
 #include <run.h>
 #include <smc-rmi.h>
@@ -19,7 +20,8 @@
 
 static struct ns_state g_ns_data[MAX_CPUS];
 static uint8_t g_sve_data[MAX_CPUS][sizeof(struct sve_state)]
-		__attribute__((aligned(sizeof(__uint128_t))));
+		__aligned(sizeof(__uint128_t));
+static struct pmu_state g_pmu_data[MAX_CPUS];
 
 /*
  * Initialize the aux data and any buffer pointers to the aux granule memory for
@@ -29,10 +31,12 @@
 			  void *rec_aux,
 			  unsigned int num_rec_aux)
 {
-	aux_data->attest_heap_buf = (uint8_t *)rec_aux;
-
 	/* Ensure we have enough aux granules for use by REC */
-	assert(num_rec_aux >= REC_HEAP_PAGES);
+	assert(num_rec_aux >= REC_NUM_PAGES);
+
+	aux_data->attest_heap_buf = (uint8_t *)rec_aux;
+	aux_data->pmu = (struct pmu_state *)((uint8_t *)rec_aux +
+						REC_HEAP_SIZE);
 }
 
 /*
@@ -69,7 +73,6 @@
 	sysregs->elr_el1 = read_elr_el12();
 	sysregs->spsr_el1 = read_spsr_el12();
 	sysregs->pmcr_el0 = read_pmcr_el0();
-	sysregs->pmuserenr_el0 = read_pmuserenr_el0();
 	sysregs->tpidrro_el0 = read_tpidrro_el0();
 	sysregs->tpidr_el0 = read_tpidr_el0();
 	sysregs->csselr_el1 = read_csselr_el1();
@@ -105,7 +108,7 @@
 	sysregs->cntv_cval_el0 = read_cntv_cval_el02();
 }
 
-static void save_realm_state(struct rec *rec)
+static void save_realm_state(struct rec *rec, struct rmi_rec_exit *rec_exit)
 {
 	save_sysreg_state(&rec->sysregs);
 
@@ -113,6 +116,15 @@
 	rec->pstate = read_spsr_el2();
 
 	gic_save_state(&rec->sysregs.gicstate);
+
+	if (rec->realm_info.pmu_enabled) {
+		/* Expose PMU Realm state to NS */
+		pmu_update_rec_exit(rec_exit);
+
+		/* Save PMU context */
+		pmu_save_state(rec->aux_data.pmu,
+				rec->realm_info.pmu_num_cnts);
+	}
 }
 
 static void restore_sysreg_state(struct sysreg_state *sysregs)
@@ -122,7 +134,6 @@
 	write_elr_el12(sysregs->elr_el1);
 	write_spsr_el12(sysregs->spsr_el1);
 	write_pmcr_el0(sysregs->pmcr_el0);
-	write_pmuserenr_el0(sysregs->pmuserenr_el0);
 	write_tpidrro_el0(sysregs->tpidrro_el0);
 	write_tpidr_el0(sysregs->tpidr_el0);
 	write_csselr_el1(sysregs->csselr_el1);
@@ -166,6 +177,12 @@
 	write_cntv_ctl_el02(sysregs->cntv_ctl_el0);
 }
 
+static void configure_realm_stage2(struct rec *rec)
+{
+	write_vtcr_el2(rec->common_sysregs.vtcr_el2);
+	write_vttbr_el2(rec->common_sysregs.vttbr_el2);
+}
+
 static void restore_realm_state(struct rec *rec)
 {
 	/*
@@ -177,21 +194,29 @@
 	isb();
 
 	restore_sysreg_state(&rec->sysregs);
+
 	write_elr_el2(rec->pc);
 	write_spsr_el2(rec->pstate);
 	write_hcr_el2(rec->sysregs.hcr_el2);
 
+	/* Control trapping of accesses to PMU registers */
+	write_mdcr_el2(rec->common_sysregs.mdcr_el2);
+
 	gic_restore_state(&rec->sysregs.gicstate);
+
+	configure_realm_stage2(rec);
+
+	if (rec->realm_info.pmu_enabled) {
+		/* Restore PMU context */
+		pmu_restore_state(rec->aux_data.pmu,
+				  rec->realm_info.pmu_num_cnts);
+	}
 }
 
-static void configure_realm_stage2(struct rec *rec)
+static void save_ns_state(struct rec *rec)
 {
-	write_vtcr_el2(rec->common_sysregs.vtcr_el2);
-	write_vttbr_el2(rec->common_sysregs.vttbr_el2);
-}
+	struct ns_state *ns_state = rec->ns;
 
-static void save_ns_state(struct ns_state *ns_state)
-{
 	save_sysreg_state(&ns_state->sysregs);
 
 	/*
@@ -202,10 +227,17 @@
 	ns_state->sysregs.cnthctl_el2 = read_cnthctl_el2();
 
 	ns_state->icc_sre_el2 = read_icc_sre_el2();
+
+	if (rec->realm_info.pmu_enabled) {
+		/* Save PMU context */
+		pmu_save_state(ns_state->pmu, rec->realm_info.pmu_num_cnts);
+	}
 }
 
-static void restore_ns_state(struct ns_state *ns_state)
+static void restore_ns_state(struct rec *rec)
 {
+	struct ns_state *ns_state = rec->ns;
+
 	restore_sysreg_state(&ns_state->sysregs);
 
 	/*
@@ -216,6 +248,12 @@
 	write_cnthctl_el2(ns_state->sysregs.cnthctl_el2);
 
 	write_icc_sre_el2(ns_state->icc_sre_el2);
+
+	if (rec->realm_info.pmu_enabled) {
+		/* Restore PMU state */
+		pmu_restore_state(ns_state->pmu,
+				  rec->realm_info.pmu_num_cnts);
+	}
 }
 
 static void activate_events(struct rec *rec)
@@ -243,14 +281,18 @@
 	void *rec_aux;
 	unsigned int cpuid = my_cpuid();
 
-	assert(rec->ns == NULL);
-
 	assert(cpuid < MAX_CPUS);
+	assert(rec->ns == NULL);
+	assert(rec->fpu_ctx.used == false);
+
 	ns_state = &g_ns_data[cpuid];
 
-	/* ensure SVE/FPU context is cleared */
+	/* Ensure SVE/FPU and PMU context is cleared */
 	assert(ns_state->sve == NULL);
 	assert(ns_state->fpu == NULL);
+	assert(ns_state->pmu == NULL);
+
+	rec->ns = ns_state;
 
 	/* Map auxiliary granules */
 	rec_aux = map_rec_aux(rec->g_aux, rec->num_rec_aux);
@@ -270,7 +312,7 @@
 	 */
 	if (!rec->alloc_info.ctx_initialised) {
 		(void)attestation_heap_ctx_init(rec->aux_data.attest_heap_buf,
-						REC_HEAP_PAGES * SZ_4K);
+						REC_HEAP_SIZE);
 		rec->alloc_info.ctx_initialised = true;
 	}
 
@@ -280,15 +322,11 @@
 		ns_state->fpu = (struct fpu_state *)&g_sve_data[cpuid];
 	}
 
-	save_ns_state(ns_state);
+	ns_state->pmu = &g_pmu_data[cpuid];
+
+	save_ns_state(rec);
 	restore_realm_state(rec);
 
-	/* Prepare for lazy save/restore of FPU/SIMD registers. */
-	rec->ns = ns_state;
-	assert(rec->fpu_ctx.used == false);
-
-	configure_realm_stage2(rec);
-
 	do {
 		/*
 		 * We must check the status of the arch timers in every
@@ -335,22 +373,23 @@
 		rec->fpu_ctx.used = false;
 	}
 
+	report_timer_state_to_ns(rec_exit);
+
+	save_realm_state(rec, rec_exit);
+	restore_ns_state(rec);
+
 	/*
-	 * Clear FPU/SVE context while exiting
+	 * Clear FPU/SVE and PMU context while exiting
 	 */
 	ns_state->sve = NULL;
 	ns_state->fpu = NULL;
+	ns_state->pmu = NULL;
 
 	/*
 	 * Clear NS pointer since that struct is local to this function.
 	 */
 	rec->ns = NULL;
 
-	report_timer_state_to_ns(rec_exit);
-
-	save_realm_state(rec);
-	restore_ns_state(ns_state);
-
 	/* Undo the heap association */
 	attestation_heap_ctx_unassign_pe();
 	/* Unmap auxiliary granules */