@@ -174,10 +174,19 @@ static inline bool stage2_pud_present(struct kvm *kvm, pud_t pud)
return 1;
}
-static inline void stage2_pud_populate(struct kvm *kvm, pud_t *pud, pmd_t *pmd)
+static inline int stage2_pud_populate(struct kvm *kvm, pud_t *pud, pmd_t *pmd)
{
- if (kvm_stage2_has_pmd(kvm))
- pud_populate(NULL, pud, pmd);
+ if (!kvm_stage2_has_pmd(kvm))
+ return 0;
+
+ /* paired with pgtable_pmd_page_dtor() in pmd_free() below */
+ if (!pgtable_pmd_page_ctor(virt_to_page(pmd))) {
+ free_page((unsigned long)pmd);
+ return -ENOMEM;
+ }
+
+ pud_populate(NULL, pud, pmd);
+ return 0;
}
static inline pmd_t *stage2_pmd_offset(struct kvm *kvm,
@@ -1037,6 +1037,7 @@ static pud_t *stage2_get_pud(struct kvm *kvm, struct kvm_mmu_memory_cache *cache
static pmd_t *stage2_get_pmd(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
phys_addr_t addr)
{
+ int ret;
pud_t *pud;
pmd_t *pmd;
@@ -1048,7 +1049,9 @@ static pmd_t *stage2_get_pmd(struct kvm *kvm, struct kvm_mmu_memory_cache *cache
if (!cache)
return NULL;
pmd = mmu_memory_cache_alloc(cache);
- stage2_pud_populate(kvm, pud, pmd);
+ ret = stage2_pud_populate(kvm, pud, pmd);
+ if (ret)
+ return ERR_PTR(ret);
get_page(virt_to_page(pud));
}
@@ -1061,6 +1064,9 @@ static int stage2_set_pmd_huge(struct kvm *kvm, struct kvm_mmu_memory_cache
pmd_t *pmd, old_pmd;
pmd = stage2_get_pmd(kvm, cache, addr);
+ if (IS_ERR(pmd))
+ return PTR_ERR(pmd);
+
VM_BUG_ON(!pmd);
old_pmd = *pmd;
@@ -1198,6 +1204,7 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
phys_addr_t addr, const pte_t *new_pte,
unsigned long flags)
{
+ int ret;
pud_t *pud;
pmd_t *pmd;
pte_t *pte, old_pte;
@@ -1227,7 +1234,9 @@ static int stage2_set_pte(struct kvm *kvm, struct kvm_mmu_memory_cache *cache,
if (!cache)
return 0; /* ignore calls from kvm_set_spte_hva */
pmd = mmu_memory_cache_alloc(cache);
- stage2_pud_populate(kvm, pud, pmd);
+ ret = stage2_pud_populate(kvm, pud, pmd);
+ if (ret)
+ return ret;
get_page(virt_to_page(pud));
}
Call pgtable_pmd_page_dtor() for pmd page allocated by mmu_memory_cache_alloc() so kernel won't crash when it's freed through stage2_pmd_free()->pmd_free()->pgtable_pmd_page_dtor(). This is needed if we are going to enable split pmd pt lock. Signed-off-by: Yu Zhao <yuzhao@google.com> --- arch/arm64/include/asm/stage2_pgtable.h | 15 ++++++++++++--- virt/kvm/arm/mmu.c | 13 +++++++++++-- 2 files changed, 23 insertions(+), 5 deletions(-)