From eb0f5f7486f113af2b2ece434edfa831b47e4fa9 Mon Sep 17 00:00:00 2001 From: Fuad Tabba Date: Wed, 9 Nov 2022 14:40:29 +0000 Subject: [PATCH] ANDROID: arm64: ioremap/iounmap use stage-2 granule size When MMIO guard is queried, it advertises the guard granule size it uses. Use that value. Fixes: arm64: Implement ioremap/iounmap hooks calling into KVM's MMIO guard Bug: 251432016 Change-Id: Iff4dcb6229bf89aef832a29a98fecc041a1aec1b Signed-off-by: Fuad Tabba Signed-off-by: Quentin Perret --- arch/arm64/mm/ioremap.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/arch/arm64/mm/ioremap.c b/arch/arm64/mm/ioremap.c index e0cc7b5bf4a3..0d05e91491a2 100644 --- a/arch/arm64/mm/ioremap.c +++ b/arch/arm64/mm/ioremap.c @@ -59,6 +59,8 @@ static DEFINE_STATIC_KEY_FALSE(ioremap_guard_key); static DEFINE_XARRAY(ioremap_guard_array); static DEFINE_MUTEX(ioremap_guard_lock); +static size_t guard_granule; + static bool ioremap_guard; static int __init ioremap_guard_setup(char *str) { @@ -82,6 +84,7 @@ static void fixup_fixmap(void) void kvm_init_ioremap_services(void) { struct arm_smccc_res res; + size_t granule; if (!ioremap_guard) return; @@ -95,12 +98,18 @@ void kvm_init_ioremap_services(void) arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_INFO_FUNC_ID, 0, 0, 0, &res); - if (res.a0 != PAGE_SIZE) + granule = res.a0; + if (granule > PAGE_SIZE || !granule || (granule & (granule - 1))) { + pr_warn("KVM MMIO guard initialization failed: " + "guard granule (%lu), page size (%lu)\n", + granule, PAGE_SIZE); return; + } arm_smccc_1_1_invoke(ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_ENROLL_FUNC_ID, &res); if (res.a0 == SMCCC_RET_SUCCESS) { + guard_granule = granule; static_branch_enable(&ioremap_guard_key); fixup_fixmap(); pr_info("Using KVM MMIO guard for ioremap\n"); @@ -111,20 +120,24 @@ void kvm_init_ioremap_services(void) void ioremap_phys_range_hook(phys_addr_t phys_addr, size_t size, pgprot_t prot) { + int guard_shift; + if (!static_branch_unlikely(&ioremap_guard_key)) return; + guard_shift = __builtin_ctzl(guard_granule); + mutex_lock(&ioremap_guard_lock); while (size) { - u64 pfn = phys_addr >> PAGE_SHIFT; + u64 guard_fn = phys_addr >> guard_shift; struct ioremap_guard_ref *ref; struct arm_smccc_res res; if (pfn_valid(__phys_to_pfn(phys_addr))) goto next; - ref = xa_load(&ioremap_guard_array, pfn); + ref = xa_load(&ioremap_guard_array, guard_fn); if (ref) { refcount_inc(&ref->count); goto next; @@ -141,7 +154,7 @@ void ioremap_phys_range_hook(phys_addr_t phys_addr, size_t size, pgprot_t prot) ref = kzalloc(sizeof(*ref), GFP_KERNEL); if (ref) { refcount_set(&ref->count, 1); - if (xa_err(xa_store(&ioremap_guard_array, pfn, ref, + if (xa_err(xa_store(&ioremap_guard_array, guard_fn, ref, GFP_KERNEL))) { kfree(ref); ref = NULL; @@ -153,14 +166,14 @@ void ioremap_phys_range_hook(phys_addr_t phys_addr, size_t size, pgprot_t prot) if (res.a0 != SMCCC_RET_SUCCESS) { pr_warn_ratelimited("Failed to register %llx\n", phys_addr); - xa_erase(&ioremap_guard_array, pfn); + xa_erase(&ioremap_guard_array, guard_fn); kfree(ref); goto out; } next: - size -= PAGE_SIZE; - phys_addr += PAGE_SIZE; + size -= guard_granule; + phys_addr += guard_granule; } out: mutex_unlock(&ioremap_guard_lock); @@ -168,19 +181,22 @@ out: void iounmap_phys_range_hook(phys_addr_t phys_addr, size_t size) { + int guard_shift; + if (!static_branch_unlikely(&ioremap_guard_key)) return; VM_BUG_ON(phys_addr & ~PAGE_MASK || size & ~PAGE_MASK); + guard_shift = __builtin_ctzl(guard_granule); mutex_lock(&ioremap_guard_lock); while (size) { - u64 pfn = phys_addr >> PAGE_SHIFT; + u64 guard_fn = phys_addr >> guard_shift; struct ioremap_guard_ref *ref; struct arm_smccc_res res; - ref = xa_load(&ioremap_guard_array, pfn); + ref = xa_load(&ioremap_guard_array, guard_fn); if (!ref) { pr_warn_ratelimited("%llx not tracked, left mapped\n", phys_addr); @@ -190,7 +206,7 @@ void iounmap_phys_range_hook(phys_addr_t phys_addr, size_t size) if (!refcount_dec_and_test(&ref->count)) goto next; - xa_erase(&ioremap_guard_array, pfn); + xa_erase(&ioremap_guard_array, guard_fn); kfree(ref); arm_smccc_1_1_hvc(ARM_SMCCC_VENDOR_HYP_KVM_MMIO_GUARD_UNMAP_FUNC_ID, @@ -202,8 +218,8 @@ void iounmap_phys_range_hook(phys_addr_t phys_addr, size_t size) } next: - size -= PAGE_SIZE; - phys_addr += PAGE_SIZE; + size -= guard_granule; + phys_addr += guard_granule; } out: mutex_unlock(&ioremap_guard_lock);