Initialize the number of accessible event counters

Set the number of event counters accessible from all exception levels
(MDCR_EL2.HPMN) to be the number of implemented event counters (PMCR_EL0.N).

This might have other security implications later.  Later work to investigate
how Hafnium should handle access to event counters.

Bug: 132394973
Change-Id: I7e63949dde2dcec58d929da4bb3e3efab5d3adee
diff --git a/src/arch/aarch64/hypervisor/debug_el1.c b/src/arch/aarch64/hypervisor/debug_el1.c
index baa2c69..2b9bd16 100644
--- a/src/arch/aarch64/hypervisor/debug_el1.c
+++ b/src/arch/aarch64/hypervisor/debug_el1.c
@@ -114,6 +114,13 @@
 #define GET_ISS_RT(esr) ((ISS_RT_MASK & (esr)) >> ISS_RT_SHIFT)
 
 /**
+ * PMCR_EL0.N: Indicates the number of event counters implemented.
+ */
+#define PMCR_EL0_N_MASK 0xf800
+#define PMCR_EL0_N_SHIFT 11
+#define GET_PMCR_EL0_N(pmcr) ((PMCR_EL0_N_MASK & (pmcr)) >> PMCR_EL0_N_SHIFT)
+
+/**
  * Definitions of read-only debug registers' ISS signatures.
  */
 #define EL1_DEBUG_REGISTERS_READ       \
@@ -212,32 +219,42 @@
  */
 uintreg_t get_mdcr_el2_value(spci_vm_id_t vm_id)
 {
-	uintreg_t mdcr_el2_preserve = read_msr(MDCR_EL2);
+	uintreg_t mdcr_el2_value = read_msr(MDCR_EL2);
+	uintreg_t pmcr_el0 = read_msr(PMCR_EL0);
 
 	/*
-	 * Preserve the values of HPMN and E2PB, which are dependent on whether
-	 * certain features are enabled, and should be set up by the bootcode.
+	 * Preserve E2PB for now, which depends on the SPE implementation.
+	 * TODO: Investigate how to detect whether SPE is implemented, and which
+	 * stage's translation regime is applicable, i.e., EL2 or EL1.
 	 */
-	mdcr_el2_preserve &= (MDCR_EL2_HPMN | MDCR_EL2_E2PB);
+	mdcr_el2_value &= MDCR_EL2_E2PB;
 
-	if (vm_id == HF_PRIMARY_VM_ID) {
+	/*
+	 * Set the number of event counters accessible from all exception levels
+	 * (MDCR_EL2.HPMN) to be the number of implemented event counters
+	 * (PMCR_EL0.N).
+	 * TODO(b/132394973): examine the implications of this setting.
+	 */
+	mdcr_el2_value |= GET_PMCR_EL0_N(pmcr_el0) & MDCR_EL2_HPMN;
+
+	/*
+	 * Trap all VM accesses to debug registers to have fine grained control
+	 * over system register accesses.
+	 * Do not trap the Primary VM's debug events, e.g., watchpoint or
+	 * breakpoint events (!MDCR_EL2_TDE).
+	 */
+	mdcr_el2_value |=
+		MDCR_EL2_TTRF | MDCR_EL2_TDRA | MDCR_EL2_TDOSA | MDCR_EL2_TDA;
+
+	if (vm_id != HF_PRIMARY_VM_ID) {
 		/*
-		 * Trap primary VM accesses to debug registers to have fine
-		 * grained control over system register accesses.
-		 * Do not trap the Primary VM's debug events (!MDCR_EL2_TDE).
+		 * Debug event exceptions should be disabled in secondary VMs
+		 * but trap them for additional security.
 		 */
-		return mdcr_el2_preserve | MDCR_EL2_TTRF | MDCR_EL2_TDRA |
-		       MDCR_EL2_TDOSA | MDCR_EL2_TDA;
+		mdcr_el2_value |= MDCR_EL2_TDE;
 	}
 
-	/*
-	 * Trap all secondary VM debug register accesses as well as debug
-	 * event exceptions.
-	 * Debug event exceptions should be disabled in secondary VMs, but trap
-	 * them for additional security (MDCR_EL2_TDE).
-	 */
-	return mdcr_el2_preserve | MDCR_EL2_TTRF | MDCR_EL2_TDRA |
-	       MDCR_EL2_TDOSA | MDCR_EL2_TDA | MDCR_EL2_TDE;
+	return mdcr_el2_value;
 }
 
 /**