feat: add implementation of `memcpy_trapped`
Add implementation of `memcpy_trapped` which is capable of returning
error in case any of the memory accesses from source and destination
result in a data abort.
The `sync_current_exception` was refactored such that it can infer a
GPF, and in the exact str/ldr locations of the `memcpy_trapped`.
Prepare the `sync_current_exception` handler to process the GPF.
It shall be able to change the `ELR_EL2`, and return false
signaling that `ELR_EL2` shall not be restored from stack.
Signed-off-by: J-Alves <joao.alves@arm.com>
Change-Id: Id268140441a16b346b06a4891f97e69c0d1b8e6e
diff --git a/src/arch/aarch64/hypervisor/BUILD.gn b/src/arch/aarch64/hypervisor/BUILD.gn
index d3a4e93..df36f68 100644
--- a/src/arch/aarch64/hypervisor/BUILD.gn
+++ b/src/arch/aarch64/hypervisor/BUILD.gn
@@ -27,6 +27,7 @@
sources = [
"exceptions.S",
"hypervisor_entry.S",
+ "memcpy_trapped.S",
"plat_entry.S",
]
diff --git a/src/arch/aarch64/hypervisor/exceptions.S b/src/arch/aarch64/hypervisor/exceptions.S
index 57e7e20..59026f7 100644
--- a/src/arch/aarch64/hypervisor/exceptions.S
+++ b/src/arch/aarch64/hypervisor/exceptions.S
@@ -150,7 +150,7 @@
.balign 0x800
vector_table_el2:
sync_cur_sp0:
- noreturn_current_exception_sp0 el2 sync_current_exception_noreturn
+ noreturn_current_exception_sp0 el2 sync_current_exception
.balign 0x80
irq_cur_sp0:
@@ -166,7 +166,7 @@
.balign 0x80
sync_cur_spx:
- noreturn_current_exception_spx el2 sync_current_exception_noreturn
+ current_exception_spx el2 sync_current_exception
.balign 0x80
irq_cur_spx:
@@ -214,6 +214,15 @@
.balign 0x40
+
+/**
+ * Instantiate code path for restoring stack and returning.
+ */
+restore_from_stack_and_return el2
+
+exception_handler_return:
+ eret_with_sb
+
/**
* pauth_save_vcpu_and_restore_hyp_key
*
diff --git a/src/arch/aarch64/hypervisor/handler.c b/src/arch/aarch64/hypervisor/handler.c
index 47e1a96..976fd09 100644
--- a/src/arch/aarch64/hypervisor/handler.c
+++ b/src/arch/aarch64/hypervisor/handler.c
@@ -11,6 +11,7 @@
#include "hf/arch/barriers.h"
#include "hf/arch/gicv3.h"
#include "hf/arch/init.h"
+#include "hf/arch/memcpy_trapped.h"
#include "hf/arch/mmu.h"
#include "hf/arch/plat/ffa.h"
#include "hf/arch/plat/smc.h"
@@ -232,7 +233,12 @@
panic("SError from current exception level.");
}
-noreturn void sync_current_exception_noreturn(uintreg_t elr, uintreg_t spsr)
+/**
+ * Returns true if ELR_EL2 is not to be restored from stack.
+ * Currently function doesn't return false, as for all other cases
+ * panics.
+ */
+bool sync_current_exception(uintreg_t elr, uintreg_t spsr)
{
uintreg_t esr = read_msr(esr_el2);
uintreg_t ec = GET_ESR_EC(esr);
@@ -240,21 +246,53 @@
(void)spsr;
switch (ec) {
- case EC_DATA_ABORT_SAME_EL:
+ case EC_DATA_ABORT_SAME_EL: {
+ uint64_t iss = GET_ESR_ISS(esr);
+ uint64_t dfsc = GET_ESR_ISS_DFSC(iss);
+ uint64_t far = read_msr(far_el2);
+
+ /* Handle Granule Protection Fault. */
+ if (is_arch_feat_rme_supported() && dfsc == DFSC_GPF) {
+ dlog_verbose(
+ "Granule Protection Fault: esr=%#x, ec=%#x, "
+ "far=%#x, elr=%#x\n",
+ esr, ec, far, elr);
+
+ /*
+ * Change ELR_EL2 only if failed whilst either
+ * reading or writing within 'memcpy_trapped'.
+ */
+ if (elr == (uintptr_t)memcpy_trapped_read ||
+ elr == (uintptr_t)memcpy_trapped_write) {
+ dlog_verbose(
+ "GPF due to data abort on %s.\n",
+ (elr == (uintptr_t)memcpy_trapped_read)
+ ? "read"
+ : "write");
+
+ /*
+ * Update the ELR_EL2 with the return
+ * address, to return error from the
+ * call to 'memcpy_trapped'.
+ */
+ write_msr(ELR_EL2, memcpy_trapped_aborted);
+ return true;
+ }
+ }
+
if (!(esr & (1U << 10))) { /* Check FnV bit. */
dlog_error(
"Data abort: pc=%#x, esr=%#x, ec=%#x, "
"far=%#x\n",
- elr, esr, ec, read_msr(far_el2));
+ elr, esr, ec, far);
+
} else {
dlog_error(
"Data abort: pc=%#x, esr=%#x, ec=%#x, "
"far=invalid\n",
elr, esr, ec);
}
-
- break;
-
+ } break;
default:
dlog_error(
"Unknown current sync exception pc=%#x, esr=%#x, "
diff --git a/src/arch/aarch64/hypervisor/memcpy_trapped.S b/src/arch/aarch64/hypervisor/memcpy_trapped.S
new file mode 100644
index 0000000..df4628a
--- /dev/null
+++ b/src/arch/aarch64/hypervisor/memcpy_trapped.S
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2024 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.
+ */
+
+.global memcpy_trapped
+.global memcpy_trapped_aborted
+.global memcpy_trapped_write
+.global memcpy_trapped_read
+
+/**
+ * This is a helper function to copy data from/to memory owned
+ * by partitions or another FF-A endpoint, whose access from the
+ * SPM might result in a data abort, due to accessing the wrong
+ * Physical Address. This function shall not be used to copy when
+ * source and destination are owned by hafnium.
+ * The function assumes the addresses are aligned to 8 bytes.
+ *
+ * - x0 contains the destination address.
+ * - x1 size of destination
+ * - x2 contains the source address.
+ * - x3 size of source.
+ *
+ * Returns:
+ * - x0: 0 if failed to copy, 1 otherwise.
+ */
+memcpy_trapped:
+ /* If source size is bigger than destination size, abort. */
+ cmp x3, x1
+ b.hi memcpy_trapped_aborted
+
+ /* Return error if destination size is 0. */
+ cbz x1, memcpy_trapped_aborted
+
+ /* Return error if destination is null. */
+ cbz x0, memcpy_trapped_aborted
+
+ /* Return error if source is null. */
+ cbz x2, memcpy_trapped_aborted
+
+ /* Check if source size is aligned to 8 bytes. */
+ and x4, x3, #(8-1)
+ cbz x4, memcpy_trapped_read
+
+ /* Align to 8 bytes if it isn't. */
+ add x3, x3, #8
+ sub x3, x3, x4
+
+ /*
+ * The read/write from/to memory prone to cause an
+ * exception must precisely follow the labels below.
+ * This is so we can deterministically assert that the
+ * exception is due to an access that we know to be
+ * prone to be aborted.
+ * This is enforced in the exception handler, to
+ * determine wether the link register must be overwritten
+ * with that of label `memcpy_trapped_aborted`, thus
+ * returning an error to the caller of memcpy_trapped.
+ */
+memcpy_trapped_read:
+ /* Read from the source. */
+ ldr x4, [x2], #8
+memcpy_trapped_write:
+ /* Write to destination. */
+ str x4, [x0], #8
+ sub x3, x3, #8
+ cbnz x3, memcpy_trapped_read
+
+ /* Success. */
+ mov x0, #1
+ ret
+
+/**
+ * Exit for 'memcpy_trapped' function, in case there is an error:
+ * - Argument checks in the function.
+ * - Access gets trapped due to GPF.
+ */
+memcpy_trapped_aborted:
+ mov x0, xzr
+ ret