Use implicit error sync barriers to isolate SError exceptions

SError exceptions are asynchronous and can happen at an exception level or VM
other than the one responsible for it.  Armv8.2 mandates RAS support, which
adds the option of having implicit error synchronization barriers on entry or
exit from EL2.

Error Synchronization Barriers allow the efficient isolation of errors and are
lighter weight than other barriers, because they do not order accesses or
flush the pipeline necessarily (see Arm Cortex-A76 Core Technical Reference
Manual).

Before this change a malicious VM could potentially trigger an SError that would
cause Hafnium to either panic or to abort another VM.

Bug: 147342742
Bug: 140916188
Change-Id: Ie74d58b1de476789fe0876655f28b38098b1c766
diff --git a/src/arch/aarch64/BUILD.gn b/src/arch/aarch64/BUILD.gn
index 93569ac..ab9b0c0 100644
--- a/src/arch/aarch64/BUILD.gn
+++ b/src/arch/aarch64/BUILD.gn
@@ -21,6 +21,7 @@
   sources = [
     "irq.c",
     "mm.c",
+    "sysregs.c",
     "timer.c",
   ]
 }
diff --git a/src/arch/aarch64/hypervisor/BUILD.gn b/src/arch/aarch64/hypervisor/BUILD.gn
index 8a057e9..25652b8 100644
--- a/src/arch/aarch64/hypervisor/BUILD.gn
+++ b/src/arch/aarch64/hypervisor/BUILD.gn
@@ -38,7 +38,6 @@
     "handler.c",
     "perfmon.c",
     "psci_handler.c",
-    "sysregs.c",
     "vm.c",
   ]
 
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 231bbe4..44fa3ae 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -185,7 +185,7 @@
 	(void)elr;
 	(void)spsr;
 
-	panic("IRQ from current");
+	panic("IRQ from current exception level.");
 }
 
 noreturn void fiq_current_exception_noreturn(uintreg_t elr, uintreg_t spsr)
@@ -193,7 +193,7 @@
 	(void)elr;
 	(void)spsr;
 
-	panic("FIQ from current");
+	panic("FIQ from current exception level.");
 }
 
 noreturn void serr_current_exception_noreturn(uintreg_t elr, uintreg_t spsr)
@@ -201,7 +201,7 @@
 	(void)elr;
 	(void)spsr;
 
-	panic("SERR from current");
+	panic("SError from current exception level.");
 }
 
 noreturn void sync_current_exception_noreturn(uintreg_t elr, uintreg_t spsr)
@@ -625,10 +625,15 @@
 	return irq_lower();
 }
 
-struct vcpu *serr_lower(void)
+noreturn struct vcpu *serr_lower(void)
 {
-	dlog("SERR from lower\n");
-	return api_abort(current());
+	/*
+	 * SError exceptions should be isolated and handled by the responsible
+	 * VM/exception level. Getting here indicates a bug, that isolation is
+	 * not working, or a processor that does not support ARMv8.2-IESB, in
+	 * which case Hafnium routes SError exceptions to EL2 (here).
+	 */
+	panic("SError from a lower exception level.");
 }
 
 /**
diff --git a/src/arch/aarch64/mm.c b/src/arch/aarch64/mm.c
index f856ac4..6522e97 100644
--- a/src/arch/aarch64/mm.c
+++ b/src/arch/aarch64/mm.c
@@ -22,6 +22,7 @@
 #include "hf/dlog.h"
 
 #include "msr.h"
+#include "sysregs.h"
 
 /* Keep macro alignment */
 /* clang-format off */
@@ -681,19 +682,7 @@
 			(25 << 0) | /* T0SZ, input address is 2^39 bytes. */
 			0,
 
-		.sctlr_el2 = (1 << 0) |  /* M, enable stage 1 EL2 MMU. */
-			     (1 << 1) |  /* A, enable alignment check faults. */
-			     (1 << 2) |  /* C, data cache enable. */
-			     (1 << 3) |  /* SA, enable stack alignment check. */
-			     (3 << 4) |  /* RES1 bits. */
-			     (1 << 11) | /* RES1 bit. */
-			     (1 << 12) | /* I, instruction cache enable. */
-			     (1 << 16) | /* RES1 bit. */
-			     (1 << 18) | /* RES1 bit. */
-			     (1 << 19) | /* WXN bit, writable execute never. */
-			     (3 << 22) | /* RES1 bits. */
-			     (3 << 28) | /* RES1 bits. */
-			     0,
+		.sctlr_el2 = get_sctlr_el2_value(),
 	};
 
 	return true;
diff --git a/src/arch/aarch64/hypervisor/sysregs.c b/src/arch/aarch64/sysregs.c
similarity index 61%
rename from src/arch/aarch64/hypervisor/sysregs.c
rename to src/arch/aarch64/sysregs.c
index 06385eb..df300fd 100644
--- a/src/arch/aarch64/hypervisor/sysregs.c
+++ b/src/arch/aarch64/sysregs.c
@@ -19,6 +19,19 @@
 #include "msr.h"
 
 /**
+ * RAS Extension version.
+ */
+#define ID_AA64PFR0_EL1_RAS (UINT64_C(0xf) << 28)
+
+/**
+ * Returns true if the current processor supports the RAS extension.
+ */
+static bool has_ras_support(void)
+{
+	return read_msr(ID_AA64PFR0_EL1) & ID_AA64PFR0_EL1_RAS;
+}
+
+/**
  * Returns the value for HCR_EL2 for the particular VM.
  * For now, the primary VM has one value and all secondary VMs share a value.
  */
@@ -74,8 +87,25 @@
 		 */
 		hcr_el2_value |= HCR_EL2_FB;
 
-		/* Route physical SError/IRQ/FIQ interrupts to EL2. */
-		hcr_el2_value |= HCR_EL2_AMO | HCR_EL2_IMO | HCR_EL2_FMO;
+		/*
+		 * Route physical IRQ/FIQ interrupts to EL2. Do not route
+		 * SError exceptions to EL2 (AMO). Instead let each VM handle
+		 * it. Not setting AMO requires explicit Error Synchronisation
+		 * Barrier instructions (esb) on hypervisor entry/exit, or
+		 * implicit barriers (SCTLR_EL2_IESB is set).
+		 */
+		hcr_el2_value |= HCR_EL2_IMO | HCR_EL2_FMO;
+
+		if (!has_ras_support()) {
+			/*
+			 * Trap SErrors into EL2 if the processor does not
+			 * support RAS, because without error synchronization
+			 * barriers, isolating SErrors could impose a high
+			 * overhead. RAS is mandatory from Armv8.2, so this
+			 * should not be common.
+			 */
+			hcr_el2_value |= HCR_EL2_AMO;
+		}
 
 		/* Trap wait for event/interrupt instructions. */
 		hcr_el2_value |= HCR_EL2_TWE | HCR_EL2_TWI;
@@ -110,3 +140,39 @@
 {
 	return CPTR_EL2_TTA;
 }
+
+/**
+ * Returns the value for SCTLR_EL2 for the CPU.
+ */
+uintreg_t get_sctlr_el2_value(void)
+{
+	uintreg_t sctlr_el2_value = 0;
+
+	/*
+	 * Implicit Error Synchronization Barrier (Armv8.2-IESB). This feature
+	 * is mandatory from Armv8.2 onwards.
+	 * Hafnium uses it to ensure that all SError exceptions are caught by
+	 * the VM responsible for it.
+	 */
+	sctlr_el2_value |= SCTLR_EL2_IESB;
+
+	/* MMU-related bits.  */
+	sctlr_el2_value |= SCTLR_EL2_M;
+	sctlr_el2_value |= SCTLR_EL2_A;
+	sctlr_el2_value |= SCTLR_EL2_C;
+	sctlr_el2_value |= SCTLR_EL2_SA;
+	sctlr_el2_value |= SCTLR_EL2_I;
+	sctlr_el2_value |= SCTLR_EL2_WXN;
+
+	/* RES1 Bits. */
+	sctlr_el2_value |= SCTLR_EL2_B4;
+	sctlr_el2_value |= SCTLR_EL2_B16;
+	sctlr_el2_value |= SCTLR_EL2_B18;
+	sctlr_el2_value |= SCTLR_EL2_B28;
+
+	/* Unsupported features that otherwise are RES1. */
+	sctlr_el2_value |= SCTLR_EL2_EOS;
+	sctlr_el2_value |= SCTLR_EL2_EIS;
+
+	return sctlr_el2_value;
+}
diff --git a/src/arch/aarch64/hypervisor/sysregs.h b/src/arch/aarch64/sysregs.h
similarity index 88%
rename from src/arch/aarch64/hypervisor/sysregs.h
rename to src/arch/aarch64/sysregs.h
index 250c9bf..bf8f084 100644
--- a/src/arch/aarch64/hypervisor/sysregs.h
+++ b/src/arch/aarch64/sysregs.h
@@ -444,8 +444,82 @@
  */
 #define PSR_PE_MODE_EL1H UINT64_C(0x5)
 
+/*
+ * Define configurations bits for the System Control Register (EL2), SCTLR_EL2.
+ * See Arm Architecture Reference Manual, D13.2.106.
+ */
+
+/**
+ * Reserved, RES1.
+ */
+#define SCTLR_EL2_B28 (UINT64_C(0x1) << 28)
+
+/**
+ * Exception entry is a context synchronization Event (Armv8.5-CSEH),
+ * otherwise RES1.
+ */
+#define SCTLR_EL2_EIS (UINT64_C(0x1) << 22)
+
+/**
+ * Implicit Error Synchronization event enable (ARMv8.2-IESB).
+ */
+#define SCTLR_EL2_IESB (UINT64_C(0x1) << 21)
+
+/**
+ * Write permission implies XN (Execute-never).
+ */
+#define SCTLR_EL2_WXN (UINT64_C(0x1) << 19)
+
+/**
+ * Reserved, RES1.
+ */
+#define SCTLR_EL2_B18 (UINT64_C(0x1) << 18)
+
+/**
+ * Reserved, RES1.
+ */
+#define SCTLR_EL2_B16 (UINT64_C(0x1) << 16)
+
+/**
+ * Instruction access Cacheability control.
+ */
+#define SCTLR_EL2_I (UINT64_C(0x1) << 12)
+
+/**
+ * Exception exit is a context synchronization Event (Armv8.5-CSEH),
+ * otherwise RES1.
+ */
+#define SCTLR_EL2_EOS (UINT64_C(0x1) << 11)
+
+/**
+ * Reserved, RES1.
+ */
+#define SCTLR_EL2_B4 (UINT64_C(0x3) << 4)
+
+/**
+ * SP Alignment check enable.
+ */
+#define SCTLR_EL2_SA (UINT64_C(0x1) << 3)
+
+/**
+ * Cacheability control, for data accesses.
+ */
+#define SCTLR_EL2_C (UINT64_C(0x1) << 2)
+
+/**
+ * Alignment check enable.
+ */
+#define SCTLR_EL2_A (UINT64_C(0x1) << 1)
+
+/**
+ * MMU enable for EL2 stage 1 address translation.
+ */
+#define SCTLR_EL2_M (UINT64_C(0x1) << 0)
+
 uintreg_t get_hcr_el2_value(spci_vm_id_t vm_id);
 
 uintreg_t get_mdcr_el2_value(void);
 
 uintreg_t get_cptr_el2_value(void);
+
+uintreg_t get_sctlr_el2_value(void);
diff --git a/test/vmapi/primary_with_secondaries/perfmon.c b/test/vmapi/primary_with_secondaries/perfmon.c
index ad43de9..4b5ff53 100644
--- a/test/vmapi/primary_with_secondaries/perfmon.c
+++ b/test/vmapi/primary_with_secondaries/perfmon.c
@@ -16,7 +16,7 @@
 
 #include "../../src/arch/aarch64/hypervisor/perfmon.h"
 
-#include "../../src/arch/aarch64/hypervisor/sysregs.h"
+#include "../../src/arch/aarch64/sysregs.h"
 #include "primary_with_secondary.h"
 #include "sysregs.h"
 #include "test/vmapi/spci.h"