Trap EL1 accesses to LORegion registers

Traps EL1 accesses to the LORegion registers if that feature is available.
Linux assumes only one ordering region, and disables this feature if it boots
as EL2.  LORegions are controlled by physical address, and be a security issue.

Bug: 141600635
Change-Id: Ib5c4a8dbf7650348d66815865dc4d021a08023d5
diff --git a/src/arch/aarch64/cpu.c b/src/arch/aarch64/cpu.c
index 5e434f2..5025009 100644
--- a/src/arch/aarch64/cpu.c
+++ b/src/arch/aarch64/cpu.c
@@ -26,6 +26,12 @@
 
 #include "hypervisor/perfmon.h"
 #include "hypervisor/sysregs.h"
+#include "msr.h"
+
+/**
+ * The LO field indicates whether LORegions are supported.
+ */
+#define ID_AA64MMFR1_EL1_LO (UINT64_C(1) << 16)
 
 void arch_irq_disable(void)
 {
@@ -37,6 +43,16 @@
 	__asm__ volatile("msr DAIFClr, #0xf");
 }
 
+static void lor_disable(void)
+{
+	/*
+	 * Accesses to LORC_EL1 are undefined if LORegions are not supported.
+	 */
+	if (read_msr(ID_AA64MMFR1_EL1) & ID_AA64MMFR1_EL1_LO) {
+		write_msr(MSR_LORC_EL1, 0);
+	}
+}
+
 static void gic_regs_reset(struct arch_regs *r, bool is_primary)
 {
 #if GIC_VERSION == 3 || GIC_VERSION == 4
@@ -123,3 +139,12 @@
 	r->r[6] = v.arg6;
 	r->r[7] = v.arg7;
 }
+
+void arch_cpu_init(void)
+{
+	/*
+	 * Linux expects LORegions to be disabled, hence if the current system
+	 * supports them, Hafnium ensures that they are disabled.
+	 */
+	lor_disable();
+}
diff --git a/src/arch/aarch64/hypervisor/sysregs.c b/src/arch/aarch64/hypervisor/sysregs.c
index 9764a7d..779aa66 100644
--- a/src/arch/aarch64/hypervisor/sysregs.c
+++ b/src/arch/aarch64/hypervisor/sysregs.c
@@ -40,7 +40,8 @@
 	 * primary VMs in release builds, but do not trap them in debug builds.
 	 */
 	hcr_el2_value = HCR_EL2_RW | HCR_EL2_TACR | HCR_EL2_TIDCP |
-			HCR_EL2_TSC | HCR_EL2_PTW | HCR_EL2_VM | HCR_EL2_TSW;
+			HCR_EL2_TSC | HCR_EL2_PTW | HCR_EL2_VM | HCR_EL2_TSW |
+			HCR_EL2_TLOR;
 
 	if (vm_id != HF_PRIMARY_VM_ID) {
 		hcr_el2_value |= HCR_EL2_TWE | HCR_EL2_TWI |
diff --git a/src/arch/aarch64/msr.h b/src/arch/aarch64/msr.h
index 595187e..1b96538 100644
--- a/src/arch/aarch64/msr.h
+++ b/src/arch/aarch64/msr.h
@@ -20,16 +20,51 @@
 
 #include "hf/arch/cpu.h"
 
-#define read_msr(name)                                          \
-	__extension__({                                         \
-		uintreg_t __v;                                  \
-		__asm__ volatile("mrs %0, " #name : "=r"(__v)); \
-		__v;                                            \
+/**
+ * Macros to stringify a parameter, and to allow the results of a macro to be
+ * stringified in turn.
+ */
+#define str_(s) #s
+#define str(s) str_(s)
+
+/**
+ * Reads a system register, supported by the current assembler, and returns the
+ * result.
+ */
+#define read_msr(name)                                              \
+	__extension__({                                             \
+		uintreg_t __v;                                      \
+		__asm__ volatile("mrs %0, " str(name) : "=r"(__v)); \
+		__v;                                                \
 	})
 
+/**
+ * Writes the value to the system register, supported by the current assembler.
+ */
 #define write_msr(name, value)                                \
 	__extension__({                                       \
-		__asm__ volatile("msr " #name ", %x0"         \
+		__asm__ volatile("msr " str(name) ", %x0"     \
 				 :                            \
 				 : "rZ"((uintreg_t)(value))); \
 	})
+
+/*
+ * Encodings for registers supported after Armv8.0.
+ * We aim to build one binary that supports a variety of platforms, therefore,
+ * use encodings in Arm Architecture Reference Manual Armv8-a, D13.2 for
+ * registers supported after Armv8.0.
+ */
+
+/*
+ * Registers supported from Armv8.1 onwards.
+ */
+
+/*
+ * Registers for feature Armv8.1-LOR (Limited Ordering Regions).
+ */
+
+/**
+ * Encoding for the LORegion Control register (LORC_EL1).
+ * This register enables and disables LORegions (Armv8.1).
+ */
+#define MSR_LORC_EL1 S3_0_C10_C4_3
diff --git a/src/main.c b/src/main.c
index 7b8df60..8896b77 100644
--- a/src/main.c
+++ b/src/main.c
@@ -30,6 +30,8 @@
 	vm = vcpu->vm;
 	vcpu->cpu = c;
 
+	arch_cpu_init();
+
 	/* Reset the registers to give a clean start for the primary's vCPU. */
 	arch_regs_reset(&vcpu->regs, true, vm->id, c->id, vm->ptable.root);