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/inc/hf/arch/cpu.h b/inc/hf/arch/cpu.h
index 5a97681..ae0d08f 100644
--- a/inc/hf/arch/cpu.h
+++ b/inc/hf/arch/cpu.h
@@ -59,3 +59,8 @@
  * by any other physical CPU.
  */
 void arch_regs_set_retval(struct arch_regs *r, struct spci_value v);
+
+/**
+ * Initialize and reset CPU-wide register values.
+ */
+void arch_cpu_init(void);
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);
 
diff --git a/test/vmapi/arch/aarch64/BUILD.gn b/test/vmapi/arch/aarch64/BUILD.gn
index 618fe3a..8441677 100644
--- a/test/vmapi/arch/aarch64/BUILD.gn
+++ b/test/vmapi/arch/aarch64/BUILD.gn
@@ -28,6 +28,7 @@
   public_configs = [ "//src/arch/aarch64:config" ]
 
   sources = [
+    "arch_features.c",
     "smc_whitelist.c",
     "smccc.c",
   ]
diff --git a/test/vmapi/arch/aarch64/arch_features.c b/test/vmapi/arch/aarch64/arch_features.c
new file mode 100644
index 0000000..2d5196d
--- /dev/null
+++ b/test/vmapi/arch/aarch64/arch_features.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2019 The Hafnium Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vmapi/hf/call.h"
+
+#include "../msr.h"
+#include "hftest.h"
+
+/**
+ * Test that encoding a system register using the implementation defined syntax
+ * maps to the same register defined by name.
+ */
+TEST(arch_features, read_write_msr_impdef)
+{
+	uintreg_t value = 0xa;
+	write_msr(S3_3_C9_C13_0, value);
+	EXPECT_EQ(read_msr(S3_3_C9_C13_0), value);
+	EXPECT_EQ(read_msr(PMCCNTR_EL0), value);
+}