diff --git a/arch/arm64/kvm/hyp/include/nvhe/memory.h b/arch/arm64/kvm/hyp/include/nvhe/memory.h index 0e64650e2f10..8fd0418e04ff 100644 --- a/arch/arm64/kvm/hyp/include/nvhe/memory.h +++ b/arch/arm64/kvm/hyp/include/nvhe/memory.h @@ -12,6 +12,7 @@ * page-table lock due to the lack of atomics at EL2. */ #define HOST_PAGE_NEED_POISONING BIT(0) +#define HOST_PAGE_PENDING_RECLAIM BIT(1) struct hyp_page { unsigned short refcount; diff --git a/arch/arm64/kvm/hyp/nvhe/mem_protect.c b/arch/arm64/kvm/hyp/nvhe/mem_protect.c index 771aca0fdf96..750eab9678f9 100644 --- a/arch/arm64/kvm/hyp/nvhe/mem_protect.c +++ b/arch/arm64/kvm/hyp/nvhe/mem_protect.c @@ -292,7 +292,17 @@ static int reclaim_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep, return 0; page = hyp_phys_to_page(kvm_pte_to_phys(pte)); - page->flags |= HOST_PAGE_NEED_POISONING; + switch (pkvm_getstate(kvm_pgtable_stage2_pte_prot(pte))) { + case PKVM_PAGE_OWNED: + page->flags |= HOST_PAGE_NEED_POISONING; + fallthrough; + case PKVM_PAGE_SHARED_BORROWED: + case PKVM_PAGE_SHARED_OWNED: + page->flags |= HOST_PAGE_PENDING_RECLAIM; + break; + default: + return -EPERM; + } return 0; } @@ -1873,16 +1883,23 @@ int __pkvm_host_reclaim_page(u64 pfn) goto unlock; page = hyp_phys_to_page(addr); + if (!(page->flags & HOST_PAGE_PENDING_RECLAIM)) { + ret = -EPERM; + goto unlock; + } + if (page->flags & HOST_PAGE_NEED_POISONING) { ret = hyp_zero_page(addr); if (ret) goto unlock; page->flags &= ~HOST_PAGE_NEED_POISONING; - ret = host_stage2_set_owner_locked(addr, PAGE_SIZE, pkvm_host_id); - } else { - ret = -EPERM; } + ret = host_stage2_set_owner_locked(addr, PAGE_SIZE, pkvm_host_id); + if (ret) + goto unlock; + page->flags &= ~HOST_PAGE_PENDING_RECLAIM; + unlock: host_unlock_component();