From 8c2b04dc1bb738cf1201e9cb804bc0c3ab1049a4 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Wed, 13 Oct 2021 11:13:00 +0100 Subject: [PATCH] ANDROID: KVM: arm64: Unmap S2MPU MMIO registers from host stage-2 The S2MPU driver needs to protect its MMIO registers from the host. Implement the host_stage2_adjust_mmio_range callback and restrict the address range that is about to be mapped in to avoid the known S2MPU MMIO regions. Test: builds, boots Bug: 190463801 Change-Id: Ib46f5dd651b9368c31940035e4c28a7324fc4160 Signed-off-by: David Brazdil (cherry picked from commit 8f234061533dc798ac96fdb6b49b6b66584a8d0f) Signed-off-by: Mostafa Saleh Signed-off-by: Quentin Perret --- arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c | 29 +++++++++++++++++++++++++++ arch/arm64/kvm/iommu/s2mpu.c | 11 ++++++++++ 2 files changed, 40 insertions(+) diff --git a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c index f9db44b07985..fb9ce31a7ae0 100644 --- a/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c +++ b/arch/arm64/kvm/hyp/nvhe/iommu/s2mpu.c @@ -270,6 +270,34 @@ static void s2mpu_host_stage2_set_owner(phys_addr_t addr, size_t size, u32 owner hyp_spin_unlock(&s2mpu_lock); } +static int s2mpu_host_stage2_adjust_mmio_range(phys_addr_t addr, phys_addr_t *start, + phys_addr_t *end) +{ + struct s2mpu *dev; + phys_addr_t dev_start, dev_end, int_start, int_end; + + /* Find the PA interval in the non-empty, sorted list of S2MPUs. */ + int_start = 0; + for_each_s2mpu(dev) { + dev_start = dev->pa; + dev_end = dev_start + S2MPU_MMIO_SIZE; + int_end = dev_start; + + if (dev_start <= addr && addr < dev_end) + return -EPERM; + + if (int_start <= addr && addr < int_end) + break; + + int_start = dev_end; + int_end = PA_MAX; + } + + *start = max(*start, int_start); + *end = min(*end, int_end); + return 0; +} + static bool s2mpu_host_smc_handler(struct kvm_cpu_context *host_ctxt) { DECLARE_REG(u64, fn, host_ctxt, 0); @@ -432,4 +460,5 @@ const struct kvm_iommu_ops kvm_s2mpu_ops = (struct kvm_iommu_ops){ .host_smc_handler = s2mpu_host_smc_handler, .host_mmio_dabt_handler = s2mpu_host_mmio_dabt_handler, .host_stage2_set_owner = s2mpu_host_stage2_set_owner, + .host_stage2_adjust_mmio_range = s2mpu_host_stage2_adjust_mmio_range, }; diff --git a/arch/arm64/kvm/iommu/s2mpu.c b/arch/arm64/kvm/iommu/s2mpu.c index ce4b32453b7c..12b387b351bc 100644 --- a/arch/arm64/kvm/iommu/s2mpu.c +++ b/arch/arm64/kvm/iommu/s2mpu.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -279,6 +280,13 @@ static void free_s2mpu_array(struct s2mpu *array) free_pages((unsigned long)array, order); } +static int cmp_s2mpu(const void *p1, const void *p2) +{ + const struct s2mpu *a = p1, *b = p2; + + return (a->pa > b->pa) - (a->pa < b->pa); +} + static int create_s2mpu_array(struct s2mpu **array) { struct s2mpu_list_entry *entry, *tmp; @@ -297,6 +305,9 @@ static int create_s2mpu_array(struct s2mpu **array) } WARN_ON(i != kvm_hyp_nr_s2mpus); + /* Searching through the list assumes that it is sorted. */ + sort(*array, kvm_hyp_nr_s2mpus, sizeof(struct s2mpu), cmp_s2mpu, NULL); + kvm_hyp_s2mpus = kern_hyp_va(*array); return 0; }