From: Vincent Donnefort Date: Thu, 21 May 2026 14:36:24 +0000 (+0100) Subject: KVM: arm64: Reset page order in pKVM hyp_pool X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=6835fbed39bb329744a3f44e8e6a39e24079af10;p=thirdparty%2Fkernel%2Flinux.git KVM: arm64: Reset page order in pKVM hyp_pool When a VM fails to initialise after its stage-2 hyp_pool has been initialised, that stage-2 must be torn down entirely. This requires resetting both the refcount and the order of its pages back to 0. Currently, reclaim_pgtable_pages() implicitly resets the page order by allocating the entire pool with order-0 granularity. However, in the VM initialisation error path, the addresses of the donated memory (the PGD) are already known, making it unnecessary to iterate over all pages in the pool. Since the vmemmap page order is a hyp_pool-specific field, leaving a non-zero order on hyp_pool destruction is harmless until another pool attempts to admit the page. Instead of resetting this field during destruction, reset it during pool initialization in hyp_pool_init(). For 'external' pages, we can't trust the order either as they bypass hyp_pool_init(). Since we never coalesce them, enforce order-0 to ensure safe insertion into the pool. This leaves no vmemmap order users outside of hyp_pool. Fixes: 256b4668cd89 ("KVM: arm64: Introduce separate hypercalls for pKVM VM reservation and initialization") Reported-by: Sashiko Signed-off-by: Vincent Donnefort Reviewed-by: Fuad Tabba Tested-by: Fuad Tabba Link: https://patch.msgid.link/20260521143626.1005660-2-vdonnefort@google.com Signed-off-by: Marc Zyngier --- diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 28a471d1927cd..5c1e1742db4f0 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -202,7 +202,6 @@ static void *guest_s2_zalloc_page(void *mc) memset(addr, 0, PAGE_SIZE); p = hyp_virt_to_page(addr); p->refcount = 1; - p->order = 0; return addr; } @@ -307,7 +306,6 @@ void reclaim_pgtable_pages(struct pkvm_hyp_vm *vm, struct kvm_hyp_memcache *mc) while (addr) { page = hyp_virt_to_page(addr); page->refcount = 0; - page->order = 0; push_hyp_memcache(mc, addr, hyp_virt_to_phys); WARN_ON(__pkvm_hyp_donate_host(hyp_virt_to_pfn(addr), 1)); addr = hyp_alloc_pages(&vm->pool, 0); diff --git a/arch/arm64/kvm/hyp/nvhe/page_alloc.c b/arch/arm64/kvm/hyp/nvhe/page_alloc.c index a1eb27a1a7477..57f86aa0f82fc 100644 --- a/arch/arm64/kvm/hyp/nvhe/page_alloc.c +++ b/arch/arm64/kvm/hyp/nvhe/page_alloc.c @@ -94,13 +94,22 @@ static void __hyp_attach_page(struct hyp_pool *pool, struct hyp_page *p) { phys_addr_t phys = hyp_page_to_phys(p); - u8 order = p->order; struct hyp_page *buddy; + bool coalesce = true; + u8 order = p->order; - memset(hyp_page_to_virt(p), 0, PAGE_SIZE << p->order); + /* + * 'external' pages are never coalesced and their ->order field + * untrusted as they bypass hyp_pool_init(). Enforce order-0. + */ + if (phys < pool->range_start || phys >= pool->range_end) { + order = 0; + coalesce = false; + } + + memset(hyp_page_to_virt(p), 0, PAGE_SIZE << order); - /* Skip coalescing for 'external' pages being freed into the pool. */ - if (phys < pool->range_start || phys >= pool->range_end) + if (!coalesce) goto insert; /* @@ -237,8 +246,10 @@ int hyp_pool_init(struct hyp_pool *pool, u64 pfn, unsigned int nr_pages, /* Init the vmemmap portion */ p = hyp_phys_to_page(phys); - for (i = 0; i < nr_pages; i++) + for (i = 0; i < nr_pages; i++) { hyp_set_page_refcounted(&p[i]); + p[i].order = 0; + } /* Attach the unused pages to the buddy tree */ for (i = reserved_pages; i < nr_pages; i++)