From 2529c7a2bdaab55991aece00fcdcb4d851814a34 Mon Sep 17 00:00:00 2001 From: Quentin Perret Date: Fri, 23 Dec 2022 10:18:06 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Expose linear map APIs to pKVM modules pKVM modules may need to temporarily map large-ish physically contiguous regions of memory when bootstrapping themselves. In order to support this use-case, introduce two new APIs in the module_ops struct allowing to map and unmap pages in pKVM's linear map range. Since pKVM's page ownership infrastructure relies on linear map PTEs, this needs to be done with special care. To avoid any problem, let's count the number of pages mapped by modules and unsure they have been unmapped before reaching the point of deprivilege. Bug: 244373730 Change-Id: I4aecb93f5c9ba08d9f830d1f0976704688b98509 Signed-off-by: Quentin Perret --- arch/arm64/include/asm/kvm_pkvm_module.h | 2 ++ arch/arm64/kvm/hyp/nvhe/modules.c | 42 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/arch/arm64/include/asm/kvm_pkvm_module.h b/arch/arm64/include/asm/kvm_pkvm_module.h index 8e1815bda2fc..6aef446e805a 100644 --- a/arch/arm64/include/asm/kvm_pkvm_module.h +++ b/arch/arm64/include/asm/kvm_pkvm_module.h @@ -23,6 +23,8 @@ struct pkvm_module_ops { void (*putx64)(u64 num); void *(*fixmap_map)(phys_addr_t phys); void (*fixmap_unmap)(void); + void *(*linear_map_early)(phys_addr_t phys, size_t size, enum kvm_pgtable_prot prot); + void (*linear_unmap_early)(void *addr, size_t size); void (*flush_dcache_to_poc)(void *addr, size_t size); int (*register_host_perm_fault_handler)(int (*cb)(struct kvm_cpu_context *ctxt, u64 esr, u64 addr)); int (*protect_host_page)(u64 pfn, enum kvm_pgtable_prot prot); diff --git a/arch/arm64/kvm/hyp/nvhe/modules.c b/arch/arm64/kvm/hyp/nvhe/modules.c index 3b033b7d6c19..5b657f80d134 100644 --- a/arch/arm64/kvm/hyp/nvhe/modules.c +++ b/arch/arm64/kvm/hyp/nvhe/modules.c @@ -36,11 +36,51 @@ bool pkvm_modules_enabled(void) return __pkvm_modules_enabled; } +static u64 early_lm_pages; +static void *__pkvm_linear_map_early(phys_addr_t phys, size_t size, enum kvm_pgtable_prot prot) +{ + void *addr = NULL; + int ret; + + if (!PAGE_ALIGNED(phys) || !PAGE_ALIGNED(size)) + return NULL; + + pkvm_modules_lock(); + if (!__pkvm_modules_enabled) + goto out; + + addr = __hyp_va(phys); + ret = pkvm_create_mappings(addr, addr + size, prot); + if (ret) + addr = NULL; + else + early_lm_pages += size >> PAGE_SHIFT; +out: + pkvm_modules_unlock(); + + return addr; +} + +static void __pkvm_linear_unmap_early(void *addr, size_t size) +{ + pkvm_modules_lock(); + pkvm_remove_mappings(addr, addr + size); + early_lm_pages -= size >> PAGE_SHIFT; + pkvm_modules_unlock(); +} + int __pkvm_close_module_registration(void) { int ret; pkvm_modules_lock(); + /* + * Page ownership tracking might go out of sync if there are stale + * entries in pKVM's linear map range, so they must really be gone by + * now. + */ + WARN_ON(early_lm_pages); + ret = __pkvm_modules_enabled ? 0 : -EACCES; if (!ret) { void *addr = hyp_fixmap_map(__hyp_pa(&__pkvm_modules_enabled)); @@ -60,6 +100,8 @@ const struct pkvm_module_ops module_ops = { .putx64 = hyp_putx64, .fixmap_map = hyp_fixmap_map, .fixmap_unmap = hyp_fixmap_unmap, + .linear_map_early = __pkvm_linear_map_early, + .linear_unmap_early = __pkvm_linear_unmap_early, .flush_dcache_to_poc = __kvm_flush_dcache_to_poc, .register_host_perm_fault_handler = hyp_register_host_perm_fault_handler, .protect_host_page = hyp_protect_host_page,