diff options
author | Marc Zyngier <maz@kernel.org> | 2022-01-31 11:14:41 +0000 |
---|---|---|
committer | Pierre-Clément Tosi <ptosi@google.com> | 2022-03-31 18:03:09 +0100 |
commit | 67d366081836ab6907a21a032fe0d44f35dab91e (patch) | |
tree | 98a659cba5c6578cf5d9573accaf834b4ad18501 | |
parent | ff9edd19f61ceaf690f3efb363ca2cff15541de3 (diff) | |
download | u-boot-67d366081836ab6907a21a032fe0d44f35dab91e.tar.gz |
arm64: Handle MMIO guard injected exceptions
Handle faults injected by the hypervisor as u-boot tries to
access MMIO regions that the hypervisor doesn't know about.
This relies on the platform memory map correctly exposing
the devices, and the lack of flash type R/O memslots.
Bug: 209797606
Signed-off-by: Marc Zyngier <maz@kernel.org>
Change-Id: If4b4a9012f0331a43ac77030f4d00e4f3101a73b
-rw-r--r-- | drivers/firmware/kvm-hyp-services.c | 43 |
1 files changed, 43 insertions, 0 deletions
diff --git a/drivers/firmware/kvm-hyp-services.c b/drivers/firmware/kvm-hyp-services.c index 35721efd26..ad82cea3e5 100644 --- a/drivers/firmware/kvm-hyp-services.c +++ b/drivers/firmware/kvm-hyp-services.c @@ -3,6 +3,9 @@ * Copyright (C) 2022 Google LLC */ +#include <asm/armv8/mmu.h> +#include <asm/esr.h> +#include <asm/proc-armv/ptrace.h> #include <common.h> #include <dm.h> #include <malloc.h> @@ -66,6 +69,46 @@ static int kvm_hyp_memshare_init(unsigned long features) return 0; } +static bool esr_is_external_data_abort(unsigned int esr) +{ + return esr == (ESR_ELx_FSC_EXTABT | ESR_ELx_IL | + ((unsigned int)(ESR_ELx_EC_DABT_CUR) << ESR_ELx_EC_SHIFT)); +} + +int handle_synchronous_exception(struct pt_regs *pt_regs, unsigned int esr) +{ + struct mm_region *region; + u64 far; + + /* + * HACK: Check for an external data abort, symptomatic of an + * injected exception + */ + if (!esr_is_external_data_abort(esr)) + return 0; + + asm volatile("mrs %0, far_el1" : "=r" (far)); + + for (region = mem_map; region->size; region++) { + struct arm_smccc_res res; + u64 attrs = region->attrs & PMD_ATTRINDX_MASK; + u64 size = region->size; + u64 phys = region->phys; + + if (attrs == PTE_BLOCK_MEMTYPE(MT_NORMAL) || + attrs == PTE_BLOCK_MEMTYPE(MT_NORMAL_NC)) + continue; + + if (far >= phys && far < (phys + size)) { + arm_smccc_hvc(ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_MAP_FUNC_ID, + (far & PAGE_MASK), attrs, 0, 0, 0, 0, 0, &res); + return (res.a0 == SMCCC_RET_SUCCESS); + } + } + + return 0; +} + static int kvm_hyp_services_bind(struct udevice *dev) { int ret = 0; |