Skip to content

Commit 82bb024

Browse files
willdeaconMarc Zyngier
authored andcommitted
KVM: arm64: Implement kvm_pgtable_hyp_unmap() at EL2
Implement kvm_pgtable_hyp_unmap() which can be used to remove hypervisor stage-1 mappings at EL2. Signed-off-by: Will Deacon <[email protected]> Signed-off-by: Quentin Perret <[email protected]> Signed-off-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 34ec7cb commit 82bb024

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

arch/arm64/include/asm/kvm_pgtable.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,27 @@ void kvm_pgtable_hyp_destroy(struct kvm_pgtable *pgt);
251251
int kvm_pgtable_hyp_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys,
252252
enum kvm_pgtable_prot prot);
253253

254+
/**
255+
* kvm_pgtable_hyp_unmap() - Remove a mapping from a hypervisor stage-1 page-table.
256+
* @pgt: Page-table structure initialised by kvm_pgtable_hyp_init().
257+
* @addr: Virtual address from which to remove the mapping.
258+
* @size: Size of the mapping.
259+
*
260+
* The offset of @addr within a page is ignored, @size is rounded-up to
261+
* the next page boundary and @phys is rounded-down to the previous page
262+
* boundary.
263+
*
264+
* TLB invalidation is performed for each page-table entry cleared during the
265+
* unmapping operation and the reference count for the page-table page
266+
* containing the cleared entry is decremented, with unreferenced pages being
267+
* freed. The unmapping operation will stop early if it encounters either an
268+
* invalid page-table entry or a valid block mapping which maps beyond the range
269+
* being unmapped.
270+
*
271+
* Return: Number of bytes unmapped, which may be 0.
272+
*/
273+
u64 kvm_pgtable_hyp_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size);
274+
254275
/**
255276
* kvm_get_vtcr() - Helper to construct VTCR_EL2
256277
* @mmfr0: Sanitized value of SYS_ID_AA64MMFR0_EL1 register.

arch/arm64/kvm/hyp/pgtable.c

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,6 +451,69 @@ int kvm_pgtable_hyp_map(struct kvm_pgtable *pgt, u64 addr, u64 size, u64 phys,
451451
return ret;
452452
}
453453

454+
struct hyp_unmap_data {
455+
u64 unmapped;
456+
struct kvm_pgtable_mm_ops *mm_ops;
457+
};
458+
459+
static int hyp_unmap_walker(u64 addr, u64 end, u32 level, kvm_pte_t *ptep,
460+
enum kvm_pgtable_walk_flags flag, void * const arg)
461+
{
462+
kvm_pte_t pte = *ptep, *childp = NULL;
463+
u64 granule = kvm_granule_size(level);
464+
struct hyp_unmap_data *data = arg;
465+
struct kvm_pgtable_mm_ops *mm_ops = data->mm_ops;
466+
467+
if (!kvm_pte_valid(pte))
468+
return -EINVAL;
469+
470+
if (kvm_pte_table(pte, level)) {
471+
childp = kvm_pte_follow(pte, mm_ops);
472+
473+
if (mm_ops->page_count(childp) != 1)
474+
return 0;
475+
476+
kvm_clear_pte(ptep);
477+
dsb(ishst);
478+
__tlbi_level(vae2is, __TLBI_VADDR(addr, 0), level);
479+
} else {
480+
if (end - addr < granule)
481+
return -EINVAL;
482+
483+
kvm_clear_pte(ptep);
484+
dsb(ishst);
485+
__tlbi_level(vale2is, __TLBI_VADDR(addr, 0), level);
486+
data->unmapped += granule;
487+
}
488+
489+
dsb(ish);
490+
isb();
491+
mm_ops->put_page(ptep);
492+
493+
if (childp)
494+
mm_ops->put_page(childp);
495+
496+
return 0;
497+
}
498+
499+
u64 kvm_pgtable_hyp_unmap(struct kvm_pgtable *pgt, u64 addr, u64 size)
500+
{
501+
struct hyp_unmap_data unmap_data = {
502+
.mm_ops = pgt->mm_ops,
503+
};
504+
struct kvm_pgtable_walker walker = {
505+
.cb = hyp_unmap_walker,
506+
.arg = &unmap_data,
507+
.flags = KVM_PGTABLE_WALK_LEAF | KVM_PGTABLE_WALK_TABLE_POST,
508+
};
509+
510+
if (!pgt->mm_ops->page_count)
511+
return 0;
512+
513+
kvm_pgtable_walk(pgt, addr, size, &walker);
514+
return unmap_data.unmapped;
515+
}
516+
454517
int kvm_pgtable_hyp_init(struct kvm_pgtable *pgt, u32 va_bits,
455518
struct kvm_pgtable_mm_ops *mm_ops)
456519
{

0 commit comments

Comments
 (0)