aarch64: enable standard branch protection
Branch protection build option (pac-ret+bti) is added to clang command
line for the secure versions of Hafnium. This relates to Armv8.3-PAuth
and Armv8.5-BTI architecture extensions.
For pointer authentication, setting PAuth APIA key at hypervisor entry
to an arbitrary value. This will need a better way to define the key
possibly in a platform specific manner (e.g. from an entropy source).
PAuth for instructions is enabled in SCTLR_EL2. The compiler uses APIA
key for signing/authenticating function return addresses. VM entry/exit
saves/restores the APIA key and replaces it with the EL2 key value. The
vCPU switch function takes care of other keys (APIB, APDA, APDB, APGA)
by saving/restoring registers lazily.
Branch Target Indicator is enabled by setting the GP bit in Hafnium EL2
executable code pages and compiler emitting BTI landpad instructions at
callee when using BR/BLR instructions at call site.
Change-Id: I2e014121485e7d3fb925841318521b8d036f5263
Signed-off-by: Olivier Deprez <olivier.deprez@arm.com>
diff --git a/build/toolchain/embedded.gni b/build/toolchain/embedded.gni
index 066d1b5..d9cb615 100644
--- a/build/toolchain/embedded.gni
+++ b/build/toolchain/embedded.gni
@@ -327,6 +327,11 @@
# Add a macro so files can tell whether they are not being built for a VM.
extra_defines = " -DVM_TOOLCHAIN=0"
+
+ if (defined(invoker.branch_protection)) {
+ extra_cflags = " -mbranch-protection=${invoker.branch_protection}"
+ extra_defines += " -DBRANCH_PROTECTION=1"
+ }
}
# Toolchain for building test VMs which run under Hafnium.
diff --git a/prebuilts b/prebuilts
index e230bec..71a6c24 160000
--- a/prebuilts
+++ b/prebuilts
@@ -1 +1 @@
-Subproject commit e230bec4078c8d61315e8da18b02b31d2ddb0936
+Subproject commit 71a6c2457e5b2ee89a0e1a9dc1eb30333e19c402
diff --git a/project/reference b/project/reference
index f2a1e5c..16fc50e 160000
--- a/project/reference
+++ b/project/reference
@@ -1 +1 @@
-Subproject commit f2a1e5c2669daff3bd7beb9aa6149097eda4d143
+Subproject commit 16fc50ea3fd7f2e53905c6a3628621fa6ade4c21
diff --git a/src/arch/aarch64/args.gni b/src/arch/aarch64/args.gni
index 36b505f..e51f164 100644
--- a/src/arch/aarch64/args.gni
+++ b/src/arch/aarch64/args.gni
@@ -13,5 +13,8 @@
plat_interrupts = "//src/arch/aarch64/plat/interrupts:absent"
+ # Pseudo random numbers source. Used for pointer authentication.
+ plat_prng = "//src/arch/aarch64/plat/prng:absent"
+
secure_world = "0"
}
diff --git a/src/arch/aarch64/exception_macros.S b/src/arch/aarch64/exception_macros.S
index 8e9f8a1..9a9336c 100644
--- a/src/arch/aarch64/exception_macros.S
+++ b/src/arch/aarch64/exception_macros.S
@@ -138,3 +138,14 @@
save_volatile_to_stack \elx
b \handler
.endm
+
+/**
+ * Restore Hypervisor pointer authentication APIA key.
+ */
+.macro pauth_restore_hypervisor_key reg1 reg2
+ adrp \reg1, pauth_apia_key
+ add \reg1, \reg1, :lo12: pauth_apia_key
+ ldp \reg1, \reg2, [\reg1]
+ msr APIAKEYLO_EL1, \reg1
+ msr APIAKEYHI_EL1, \reg2
+.endm
diff --git a/src/arch/aarch64/hypervisor/BUILD.gn b/src/arch/aarch64/hypervisor/BUILD.gn
index 096026f..3648cba 100644
--- a/src/arch/aarch64/hypervisor/BUILD.gn
+++ b/src/arch/aarch64/hypervisor/BUILD.gn
@@ -53,6 +53,7 @@
"//src/arch/aarch64:entry",
"//src/arch/aarch64:smc",
plat_interrupts,
+ plat_prng,
plat_psci,
plat_smc,
]
diff --git a/src/arch/aarch64/hypervisor/cpu.c b/src/arch/aarch64/hypervisor/cpu.c
index 37e7e95..a33a855 100644
--- a/src/arch/aarch64/hypervisor/cpu.c
+++ b/src/arch/aarch64/hypervisor/cpu.c
@@ -24,6 +24,12 @@
#include "perfmon.h"
#include "sysregs.h"
+#if BRANCH_PROTECTION
+
+__uint128_t pauth_apia_key;
+
+#endif
+
/**
* The LO field indicates whether LORegions are supported.
*/
diff --git a/src/arch/aarch64/hypervisor/exceptions.S b/src/arch/aarch64/hypervisor/exceptions.S
index bba56ea..54b0deb 100644
--- a/src/arch/aarch64/hypervisor/exceptions.S
+++ b/src/arch/aarch64/hypervisor/exceptions.S
@@ -57,6 +57,11 @@
.macro lower_exception handler:req
save_volatile_to_vcpu
+#if BRANCH_PROTECTION
+ /* NOTE: x18 still holds pointer to current vCPU. */
+ bl pauth_save_vcpu_and_restore_hyp_key
+#endif
+
/* Call C handler. */
bl \handler
@@ -74,6 +79,11 @@
.macro lower_sync_exception
save_volatile_to_vcpu
+#if BRANCH_PROTECTION
+ /* NOTE: x18 still holds pointer to current vCPU. */
+ bl pauth_save_vcpu_and_restore_hyp_key
+#endif
+
/* Extract the exception class (EC) from exception syndrome register. */
mrs x18, esr_el2
lsr x18, x18, #26
@@ -191,6 +201,27 @@
.balign 0x40
/**
+ * pauth_save_vcpu_and_restore_hyp_key
+ *
+ * NOTE: expect x18 holds pointer to current vCPU.
+ */
+#if BRANCH_PROTECTION
+pauth_save_vcpu_and_restore_hyp_key:
+ /*
+ * Save APIA key for the vCPU as Hypervisor replaces it with its
+ * own key. Other vCPU PAuth keys are taken care in vcpu_switch.
+ */
+ mrs x0, APIAKEYLO_EL1
+ mrs x1, APIAKEYHI_EL1
+ add x18, x18, #VCPU_PAC
+ stp x0, x1, [x18]
+
+ /* Restore Hypervisor APIA key. */
+ pauth_restore_hypervisor_key x0 x1
+ ret
+#endif
+
+/**
* Handle accesses to system registers (EC=0x18) and return to original caller.
*/
system_register_access:
@@ -305,6 +336,22 @@
mrs x9, pmintenset_el1
stp x8, x9, [x28], #16
+#if BRANCH_PROTECTION
+ add x2, x1, #(VCPU_PAC + 16)
+ mrs x10, APIBKEYLO_EL1
+ mrs x11, APIBKEYHI_EL1
+ stp x10, x11, [x2], #16
+ mrs x12, APDAKEYLO_EL1
+ mrs x13, APDAKEYHI_EL1
+ stp x12, x13, [x2], #16
+ mrs x14, APDBKEYLO_EL1
+ mrs x15, APDBKEYHI_EL1
+ stp x14, x15, [x2], #16
+ mrs x16, APGAKEYLO_EL1
+ mrs x17, APGAKEYHI_EL1
+ stp x16, x17, [x2], #16
+#endif
+
/* Save GIC registers. */
#if GIC_VERSION == 3 || GIC_VERSION == 4
/* Offset is too large, so start from a new base. */
@@ -366,6 +413,23 @@
ldp x4, x5, [x19, #VCPU_REGS + 8 * 4]
ldp x6, x7, [x19, #VCPU_REGS + 8 * 6]
+#if BRANCH_PROTECTION
+ /*
+ * EL3 saves pointer authentication keys when entering by SMC.
+ * Although prefer clearing the keys to be on the safe side.
+ */
+ msr APIAKEYLO_EL1, xzr
+ msr APIAKEYHI_EL1, xzr
+ msr APIBKEYLO_EL1, xzr
+ msr APIBKEYHI_EL1, xzr
+ msr APDAKEYLO_EL1, xzr
+ msr APDAKEYHI_EL1, xzr
+ msr APDBKEYLO_EL1, xzr
+ msr APDBKEYHI_EL1, xzr
+ msr APGAKEYLO_EL1, xzr
+ msr APGAKEYHI_EL1, xzr
+#endif
+
smc #0
/*
@@ -386,6 +450,10 @@
mrs x1, fpcr
stp x0, x1, [x18]
+#if BRANCH_PROTECTION
+ pauth_restore_hypervisor_key x0 x1
+#endif
+
/*
* Stack is at top and execution can restart straight into C code.
* Handle the FF-A call from other world.
@@ -521,6 +589,22 @@
msr pmintenclr_el1, x27
msr pmintenset_el1, x9
+#if BRANCH_PROTECTION
+ add x2, x0, #(VCPU_PAC + 16)
+ ldp x10, x11, [x2], #16
+ msr APIBKEYLO_EL1, x10
+ msr APIBKEYHI_EL1, x11
+ ldp x12, x13, [x2], #16
+ msr APDAKEYLO_EL1, x12
+ msr APDAKEYHI_EL1, x13
+ ldp x14, x15, [x2], #16
+ msr APDBKEYLO_EL1, x14
+ msr APDBKEYHI_EL1, x15
+ ldp x16, x17, [x2], #16
+ msr APGAKEYLO_EL1, x16
+ msr APGAKEYHI_EL1, x17
+#endif
+
/* Restore GIC registers. */
#if GIC_VERSION == 3 || GIC_VERSION == 4
/* Offset is too large, so start from a new base. */
@@ -558,6 +642,15 @@
* x0 is a pointer to the target vCPU.
*/
vcpu_restore_volatile_and_run:
+#if BRANCH_PROTECTION
+ add x1, x0, #VCPU_PAC
+ ldp x1, x2, [x1]
+
+ /* Restore vCPU APIA key. */
+ msr APIAKEYLO_EL1, x1
+ msr APIAKEYHI_EL1, x2
+#endif
+
ldp x4, x5, [x0, #VCPU_REGS + 8 * 4]
ldp x6, x7, [x0, #VCPU_REGS + 8 * 6]
ldp x8, x9, [x0, #VCPU_REGS + 8 * 8]
diff --git a/src/arch/aarch64/hypervisor/hypervisor_entry.S b/src/arch/aarch64/hypervisor/hypervisor_entry.S
index 6bff8aa..2ea4f37 100644
--- a/src/arch/aarch64/hypervisor/hypervisor_entry.S
+++ b/src/arch/aarch64/hypervisor/hypervisor_entry.S
@@ -58,6 +58,19 @@
*/
bl one_time_init_mm
+#if BRANCH_PROTECTION
+ /* Expect pointer authentication is implemented. */
+ mrs x1, id_aa64isar1_el1
+ ands x1, x1, #0xff0 /* API / APA */
+ beq .
+
+ /* Gather a random number to use as pointer authentication key. */
+ bl plat_prng_get_number
+ adrp x3, pauth_apia_key
+ add x2, x3, :lo12: pauth_apia_key
+ stp x0, x1, [x2]
+#endif
+
/* Enable MMU and caches before running the rest of initialization. */
bl mm_enable
bl one_time_init
@@ -115,6 +128,13 @@
#endif
+#if BRANCH_PROTECTION
+ /* Expect pointer authentication is implemented. */
+ mrs x1, id_aa64isar1_el1
+ ands x1, x1, #0xff0 /* API / APA */
+ beq .
+#endif
+
bl mm_enable
bl prepare_for_c
@@ -189,6 +209,15 @@
dsb sy
isb
+#if BRANCH_PROTECTION
+ /* Load EL2 APIA Key. */
+ adrp x1, pauth_apia_key
+ add x1, x1, :lo12: pauth_apia_key
+ ldp x1, x2, [x1]
+ msr APIAKEYLO_EL1, x1
+ msr APIAKEYHI_EL1, x2
+#endif
+
/*
* Configure sctlr_el2 to enable MMU and cache and don't proceed until
* this has completed.
diff --git a/src/arch/aarch64/hypervisor/offsets.c b/src/arch/aarch64/hypervisor/offsets.c
index 2273678..1d9c60f 100644
--- a/src/arch/aarch64/hypervisor/offsets.c
+++ b/src/arch/aarch64/hypervisor/offsets.c
@@ -18,6 +18,9 @@
DEFINE_OFFSETOF(VCPU_REGS, struct vcpu, regs)
DEFINE_OFFSETOF(VCPU_LAZY, struct vcpu, regs.lazy)
DEFINE_OFFSETOF(VCPU_FREGS, struct vcpu, regs.fp)
+#if BRANCH_PROTECTION
+DEFINE_OFFSETOF(VCPU_PAC, struct vcpu, regs.pac)
+#endif
DEFINE_OFFSETOF(VM_ID, struct vm, id)
diff --git a/src/arch/aarch64/hypervisor/other_world.c b/src/arch/aarch64/hypervisor/other_world.c
index 74a2bb1..e43d89a 100644
--- a/src/arch/aarch64/hypervisor/other_world.c
+++ b/src/arch/aarch64/hypervisor/other_world.c
@@ -16,6 +16,7 @@
#include "hf/panic.h"
#include "hf/vm.h"
+#include "msr.h"
#include "smc.h"
#if SECURE_WORLD == 0
diff --git a/src/arch/aarch64/inc/hf/arch/types.h b/src/arch/aarch64/inc/hf/arch/types.h
index 0fcc636..4bfb8a5 100644
--- a/src/arch/aarch64/inc/hf/arch/types.h
+++ b/src/arch/aarch64/inc/hf/arch/types.h
@@ -141,4 +141,20 @@
uintreg_t cntv_cval_el0;
uintreg_t cntv_ctl_el0;
} peripherals;
+
+#if BRANCH_PROTECTION
+ /* Pointer authentication keys */
+ struct {
+ uintreg_t apiakeylo_el1;
+ uintreg_t apiakeyhi_el1;
+ uintreg_t apibkeylo_el1;
+ uintreg_t apibkeyhi_el1;
+ uintreg_t apdakeylo_el1;
+ uintreg_t apdakeyhi_el1;
+ uintreg_t apdbkeylo_el1;
+ uintreg_t apdbkeyhi_el1;
+ uintreg_t apgakeylo_el1;
+ uintreg_t apgakeyhi_el1;
+ } pac;
+#endif
};
diff --git a/src/arch/aarch64/mm.c b/src/arch/aarch64/mm.c
index 457af4d..51bc94e 100644
--- a/src/arch/aarch64/mm.c
+++ b/src/arch/aarch64/mm.c
@@ -32,6 +32,7 @@
#define STAGE1_PXN (UINT64_C(1) << 53)
#define STAGE1_CONTIGUOUS (UINT64_C(1) << 52)
#define STAGE1_DBM (UINT64_C(1) << 51)
+#define STAGE1_GP (UINT64_C(1) << 50)
#define STAGE1_NG (UINT64_C(1) << 11)
#define STAGE1_AF (UINT64_C(1) << 10)
#define STAGE1_SH(x) ((x) << 8)
@@ -432,6 +433,14 @@
if (!(mode & MM_MODE_X)) {
attrs |= STAGE1_XN;
}
+#if BRANCH_PROTECTION
+ else {
+ /* Mark code pages as Guarded Pages if BTI is supported. */
+ if (is_arch_feat_bti_supported()) {
+ attrs |= STAGE1_GP;
+ }
+ }
+#endif
/* Define the read/write bits. */
if (mode & MM_MODE_W) {
diff --git a/src/arch/aarch64/msr.h b/src/arch/aarch64/msr.h
index 98da341..0b96ed4 100644
--- a/src/arch/aarch64/msr.h
+++ b/src/arch/aarch64/msr.h
@@ -78,3 +78,18 @@
* VSTCR_EL2, Virtualization Secure Translation Control Register
*/
#define MSR_VSTCR_EL2 S3_4_C2_C6_2
+
+#if BRANCH_PROTECTION
+
+#define APIAKEYLO_EL1 S3_0_C2_C1_0
+#define APIAKEYHI_EL1 S3_0_C2_C1_1
+#define APIBKEYLO_EL1 S3_0_C2_C1_2
+#define APIBKEYHI_EL1 S3_0_C2_C1_3
+#define APDAKEYLO_EL1 S3_0_C2_C2_0
+#define APDAKEYHI_EL1 S3_0_C2_C2_1
+#define APDBKEYLO_EL1 S3_0_C2_C2_2
+#define APDBKEYHI_EL1 S3_0_C2_C2_3
+#define APGAKEYLO_EL1 S3_0_C2_C3_0
+#define APGAKEYHI_EL1 S3_0_C2_C3_1
+
+#endif
diff --git a/src/arch/aarch64/plat/prng/BUILD.gn b/src/arch/aarch64/plat/prng/BUILD.gn
new file mode 100644
index 0000000..933c8d7
--- /dev/null
+++ b/src/arch/aarch64/plat/prng/BUILD.gn
@@ -0,0 +1,20 @@
+# Copyright 2021 The Hafnium Authors.
+#
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file or at
+# https://opensource.org/licenses/BSD-3-Clause.
+
+import("//build/toolchain/platform.gni")
+
+source_set("absent") {
+ sources = [
+ "absent.c",
+ ]
+}
+
+source_set("prng") {
+ public_configs = [ "//src/arch/${plat_arch}:config" ]
+ sources = [
+ "prng.c",
+ ]
+}
diff --git a/src/arch/aarch64/plat/prng/absent.c b/src/arch/aarch64/plat/prng/absent.c
new file mode 100644
index 0000000..8756510
--- /dev/null
+++ b/src/arch/aarch64/plat/prng/absent.c
@@ -0,0 +1,12 @@
+/*
+ * Copyright 2021 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+__uint128_t plat_prng_get_number(void)
+{
+ return (__uint128_t)0;
+}
diff --git a/src/arch/aarch64/plat/prng/prng.c b/src/arch/aarch64/plat/prng/prng.c
new file mode 100644
index 0000000..11e6ba3
--- /dev/null
+++ b/src/arch/aarch64/plat/prng/prng.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021 The Hafnium Authors.
+ *
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/BSD-3-Clause.
+ */
+
+#include "hf/types.h"
+
+#include "msr.h"
+
+__uint128_t pauth_apia_key;
+
+/**
+ * plat_prng_get_number
+ *
+ * Return a 128 bits pseudo random number
+ *
+ * https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git/tree/plat/arm/common/aarch64/arm_pauth.c?h=v2.4#n17
+ * "This is only a toy implementation to generate a seemingly random
+ * 128-bit key from sp, x30 and cntpct_el0 values.
+ * A production system must re-implement this function to generate
+ * keys from a reliable randomness source."
+ */
+__uint128_t plat_prng_get_number(void)
+{
+ uint64_t return_addr = (uint64_t)__builtin_return_address(0U);
+ uint64_t frame_addr = (uint64_t)__builtin_frame_address(0U);
+ uint64_t cntpct = read_msr(cntpct_el0);
+
+ /* Generate 128-bit key */
+ uint64_t key_lo = (return_addr << 13) ^ frame_addr ^ cntpct;
+ uint64_t key_hi = (frame_addr << 15) ^ return_addr ^ cntpct;
+
+ return ((__uint128_t)key_hi << 64) | (__uint128_t)key_lo;
+}
diff --git a/src/arch/aarch64/sysregs.c b/src/arch/aarch64/sysregs.c
index 070a3a8..b1817d7 100644
--- a/src/arch/aarch64/sysregs.c
+++ b/src/arch/aarch64/sysregs.c
@@ -159,6 +159,14 @@
sctlr_el2_value |= SCTLR_EL2_I;
sctlr_el2_value |= SCTLR_EL2_WXN;
+#if BRANCH_PROTECTION
+ /* Enable pointer authentication for instructions. */
+ sctlr_el2_value |= SCTLR_EL2_ENIA;
+
+ /* PACIASP/PACIBSP are compatible with PSTATE.BTYPE==11b. */
+ sctlr_el2_value &= ~SCTLR_EL2_BT;
+#endif
+
/* RES1 Bits. */
sctlr_el2_value |= SCTLR_EL2_B4;
sctlr_el2_value |= SCTLR_EL2_B16;
@@ -171,3 +179,13 @@
return sctlr_el2_value;
}
+
+/*
+ * Returns true if FEAT_BTI is supported.
+ */
+bool is_arch_feat_bti_supported(void)
+{
+ uint64_t id_aa64pfr1_el1 = read_msr(ID_AA64PFR1_EL1);
+
+ return (id_aa64pfr1_el1 & ID_AA64PFR1_EL1_BT) == 1ULL;
+}
diff --git a/src/arch/aarch64/sysregs.h b/src/arch/aarch64/sysregs.h
index 3b95e68..c4d93a6 100644
--- a/src/arch/aarch64/sysregs.h
+++ b/src/arch/aarch64/sysregs.h
@@ -521,6 +521,19 @@
*/
/**
+ * When FEAT_BTI is implemented, BT bit set, and PE executing at EL2
+ * PACIASP/PACIBSP are not compatible with PSTATE.BTYPE==11.
+ */
+#define SCTLR_EL2_BT (UINT64_C(0x1) << 36)
+
+/**
+ * When FEAT_PAUTH is implemented, controls enabling of pointer authentication
+ * using APIAKey_EL1 of instructions addresses in EL2 or EL2&0 translation
+ * regimes.
+ */
+#define SCTLR_EL2_ENIA (UINT64_C(0x1) << 31)
+
+/**
* Reserved, RES1.
*/
#define SCTLR_EL2_B28 (UINT64_C(0x1) << 28)
@@ -594,3 +607,10 @@
uintreg_t get_cptr_el2_value(void);
uintreg_t get_sctlr_el2_value(void);
+
+/**
+ * Branch Target Identification mechanism support in AArch64 state.
+ */
+#define ID_AA64PFR1_EL1_BT (UINT64_C(0xf) << 0)
+
+bool is_arch_feat_bti_supported(void);