[kvmarm] [PATCH v3 06/14] KVM: ARM: Memory virtualization setup

Sundaram, Senthilkumar ssundara at qti.qualcomm.com
Mon Nov 19 06:29:32 EST 2012


What is the use of MMU Notifiers in the absence of Shadow Page Table?

Thanks
Senthil
> -----Original Message-----
> From: kvmarm-bounces at lists.cs.columbia.edu [mailto:kvmarm-
> bounces at lists.cs.columbia.edu] On Behalf Of Christoffer Dall
> Sent: Monday, October 22, 2012 12:20 PM
> To: kvm at vger.kernel.org; linux-arm-kernel at lists.infradead.org;
> kvmarm at lists.cs.columbia.edu
> Cc: Marcelo Tosatti
> Subject: [kvmarm] [PATCH v3 06/14] KVM: ARM: Memory virtualization setup
> 
> This commit introduces the framework for guest memory management
> through the use of 2nd stage translation. Each VM has a pointer to a level-1
> table (the pgd field in struct kvm_arch) which is used for the 2nd stage
> translations. Entries are added when handling guest faults (later patch) and
> the table itself can be allocated and freed through the following functions
> implemented in
> arch/arm/kvm/arm_mmu.c:
>  - kvm_alloc_stage2_pgd(struct kvm *kvm);
>  - kvm_free_stage2_pgd(struct kvm *kvm);
> 
> Each entry in TLBs and caches are tagged with a VMID identifier in addition to
> ASIDs. The VMIDs are assigned consecutively to VMs in the order that VMs
> are executed, and caches and tlbs are invalidated when the VMID space has
> been used to allow for more than 255 simultaenously running guests.
> 
> The 2nd stage pgd is allocated in kvm_arch_init_vm(). The table is freed in
> kvm_arch_destroy_vm(). Both functions are called from the main KVM code.
> 
> We pre-allocate page table memory to be able to synchronize using a
> spinlock and be called under rcu_read_lock from the MMU notifiers.  We
> steal the mmu_memory_cache implementation from x86 and adapt for our
> specific usage.
> 
> We support MMU notifiers (thanks to Marc Zyngier) through
> kvm_unmap_hva and kvm_set_spte_hva.
> 
> Finally, define kvm_phys_addr_ioremap() to map a device at a guest IPA,
> which is used by VGIC support to map the virtual CPU interface registers to
> the guest. This support is added by Marc Zyngier.
> 
> Reviewed-by: Marcelo Tosatti <mtosatti at redhat.com>
> Signed-off-by: Marc Zyngier <marc.zyngier at arm.com>
> Signed-off-by: Christoffer Dall <c.dall at virtualopensystems.com>
> ---
>  arch/arm/include/asm/kvm_asm.h  |    2
>  arch/arm/include/asm/kvm_host.h |   19 ++
>  arch/arm/include/asm/kvm_mmu.h  |    9 +
>  arch/arm/kvm/Kconfig            |    1
>  arch/arm/kvm/arm.c              |   37 ++++
>  arch/arm/kvm/interrupts.S       |   10 +
>  arch/arm/kvm/mmu.c              |  393
> +++++++++++++++++++++++++++++++++++++++
>  arch/arm/kvm/trace.h            |   46 +++++
>  8 files changed, 515 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm/include/asm/kvm_asm.h
> b/arch/arm/include/asm/kvm_asm.h index 954bf7c..47a0e57 100644
> --- a/arch/arm/include/asm/kvm_asm.h
> +++ b/arch/arm/include/asm/kvm_asm.h
> @@ -57,6 +57,7 @@
>  #define ARM_EXCEPTION_HVC	  7
> 
>  #ifndef __ASSEMBLY__
> +struct kvm;
>  struct kvm_vcpu;
> 
>  extern char __kvm_hyp_init[];
> @@ -71,6 +72,7 @@ extern char __kvm_hyp_code_start[];  extern char
> __kvm_hyp_code_end[];
> 
>  extern void __kvm_flush_vm_context(void);
> +extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
> 
>  extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);  #endif diff --git
> a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h
> index 15d4c0b..68d1005 100644
> --- a/arch/arm/include/asm/kvm_host.h
> +++ b/arch/arm/include/asm/kvm_host.h
> @@ -117,4 +117,23 @@ int kvm_arm_copy_reg_indices(struct kvm_vcpu
> *vcpu, u64 __user *indices);  struct kvm_one_reg;  int
> kvm_arm_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg);
> int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg
> *reg);
> +u64 kvm_call_hyp(void *hypfn, ...);
> +
> +#define KVM_ARCH_WANT_MMU_NOTIFIER
> +struct kvm;
> +int kvm_unmap_hva(struct kvm *kvm, unsigned long hva); int
> +kvm_unmap_hva_range(struct kvm *kvm,
> +			unsigned long start, unsigned long end); void
> +kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte);
> +
> +/* We do not have shadow page tables, hence the empty hooks */ static
> +inline int kvm_age_hva(struct kvm *kvm, unsigned long hva) {
> +	return 0;
> +}
> +
> +static inline int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
> +{
> +	return 0;
> +}
>  #endif /* __ARM_KVM_HOST_H__ */
> diff --git a/arch/arm/include/asm/kvm_mmu.h
> b/arch/arm/include/asm/kvm_mmu.h index 741ab8f..9bd0508 100644
> --- a/arch/arm/include/asm/kvm_mmu.h
> +++ b/arch/arm/include/asm/kvm_mmu.h
> @@ -33,6 +33,15 @@ int create_hyp_mappings(void *from, void *to);  int
> create_hyp_io_mappings(void *from, void *to, phys_addr_t);  void
> free_hyp_pmds(void);
> 
> +int kvm_alloc_stage2_pgd(struct kvm *kvm); void
> +kvm_free_stage2_pgd(struct kvm *kvm); int
> kvm_phys_addr_ioremap(struct
> +kvm *kvm, phys_addr_t guest_ipa,
> +			  phys_addr_t pa, unsigned long size);
> +
> +int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run
> *run);
> +
> +void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu);
> +
>  unsigned long kvm_mmu_get_httbr(void);
>  int kvm_mmu_init(void);
>  void kvm_mmu_exit(void);
> diff --git a/arch/arm/kvm/Kconfig b/arch/arm/kvm/Kconfig index
> a07ddcc..47c5500 100644
> --- a/arch/arm/kvm/Kconfig
> +++ b/arch/arm/kvm/Kconfig
> @@ -36,6 +36,7 @@ config KVM_ARM_HOST
>  	depends on KVM
>  	depends on MMU
>  	depends on CPU_V7 && ARM_VIRT_EXT
> +	select	MMU_NOTIFIER
>  	---help---
>  	  Provides host support for ARM processors.
> 
> diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c index
> 8e1ea2b..5ac3132 100644
> --- a/arch/arm/kvm/arm.c
> +++ b/arch/arm/kvm/arm.c
> @@ -81,12 +81,33 @@ void kvm_arch_sync_events(struct kvm *kvm)  {  }
> 
> +/**
> + * kvm_arch_init_vm - initializes a VM data structure
> + * @kvm:	pointer to the KVM struct
> + */
>  int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)  {
> +	int ret = 0;
> +
>  	if (type)
>  		return -EINVAL;
> 
> -	return 0;
> +	ret = kvm_alloc_stage2_pgd(kvm);
> +	if (ret)
> +		goto out_fail_alloc;
> +
> +	ret = create_hyp_mappings(kvm, kvm + 1);
> +	if (ret)
> +		goto out_free_stage2_pgd;
> +
> +	/* Mark the initial VMID generation invalid */
> +	kvm->arch.vmid_gen = 0;
> +
> +	return ret;
> +out_free_stage2_pgd:
> +	kvm_free_stage2_pgd(kvm);
> +out_fail_alloc:
> +	return ret;
>  }
> 
>  int kvm_arch_vcpu_fault(struct kvm_vcpu *vcpu, struct vm_fault *vmf) @@
> -104,10 +125,16 @@ int kvm_arch_create_memslot(struct
> kvm_memory_slot *slot, unsigned long npages)
>  	return 0;
>  }
> 
> +/**
> + * kvm_arch_destroy_vm - destroy the VM data structure
> + * @kvm:	pointer to the KVM struct
> + */
>  void kvm_arch_destroy_vm(struct kvm *kvm)  {
>  	int i;
> 
> +	kvm_free_stage2_pgd(kvm);
> +
>  	for (i = 0; i < KVM_MAX_VCPUS; ++i) {
>  		if (kvm->vcpus[i]) {
>  			kvm_arch_vcpu_free(kvm->vcpus[i]);
> @@ -189,7 +216,13 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct
> kvm *kvm, unsigned int id)
>  	if (err)
>  		goto free_vcpu;
> 
> +	err = create_hyp_mappings(vcpu, vcpu + 1);
> +	if (err)
> +		goto vcpu_uninit;
> +
>  	return vcpu;
> +vcpu_uninit:
> +	kvm_vcpu_uninit(vcpu);
>  free_vcpu:
>  	kmem_cache_free(kvm_vcpu_cache, vcpu);
>  out:
> @@ -198,6 +231,8 @@ out:
> 
>  void kvm_arch_vcpu_free(struct kvm_vcpu *vcpu)  {
> +	kvm_mmu_free_memory_caches(vcpu);
> +	kmem_cache_free(kvm_vcpu_cache, vcpu);
>  }
> 
>  void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) diff --git
> a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S index
> 98a67ca..1c83022 100644
> --- a/arch/arm/kvm/interrupts.S
> +++ b/arch/arm/kvm/interrupts.S
> @@ -33,7 +33,13 @@ __kvm_hyp_code_start:
> 
> /**********************************************************
> **********
>   * Flush per-VMID TLBs
>   */
> +ENTRY(__kvm_tlb_flush_vmid)
> +	bx	lr
> +ENDPROC(__kvm_tlb_flush_vmid)
> 
> +/*********************************************************
> ***********
> + * Flush TLBs and instruction caches of current CPU for all VMIDs  */
>  ENTRY(__kvm_flush_vm_context)
>  	bx	lr
>  ENDPROC(__kvm_flush_vm_context)
> @@ -41,10 +47,12 @@ ENDPROC(__kvm_flush_vm_context)
> 
> /**********************************************************
> **********
>   *  Hypervisor world-switch code
>   */
> -
>  ENTRY(__kvm_vcpu_run)
>  	bx	lr
> 
> +ENTRY(kvm_call_hyp)
> +	bx	lr
> +
> 
> 
> /**********************************************************
> **********
>   * Hypervisor exception vector and handlers diff --git
> a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c index 17c2bf5..f45be86
> 100644
> --- a/arch/arm/kvm/mmu.c
> +++ b/arch/arm/kvm/mmu.c
> @@ -23,11 +23,52 @@
>  #include <asm/pgalloc.h>
>  #include <asm/kvm_arm.h>
>  #include <asm/kvm_mmu.h>
> +#include <asm/kvm_asm.h>
>  #include <asm/mach/map.h>
> +#include <trace/events/kvm.h>
> +
> +#include "trace.h"
> 
>  static DEFINE_MUTEX(kvm_hyp_pgd_mutex);  static pgd_t *hyp_pgd;
> 
> +static void kvm_tlb_flush_vmid(struct kvm *kvm) {
> +	kvm_call_hyp(__kvm_tlb_flush_vmid, kvm); }
> +
> +static int mmu_topup_memory_cache(struct kvm_mmu_memory_cache
> *cache,
> +				  int min, int max)
> +{
> +	void *page;
> +
> +	BUG_ON(max > KVM_NR_MEM_OBJS);
> +	if (cache->nobjs >= min)
> +		return 0;
> +	while (cache->nobjs < max) {
> +		page = (void *)__get_free_page(PGALLOC_GFP);
> +		if (!page)
> +			return -ENOMEM;
> +		cache->objects[cache->nobjs++] = page;
> +	}
> +	return 0;
> +}
> +
> +static void mmu_free_memory_cache(struct kvm_mmu_memory_cache
> *mc) {
> +	while (mc->nobjs)
> +		free_page((unsigned long)mc->objects[--mc->nobjs]); }
> +
> +static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache
> *mc) {
> +	void *p;
> +
> +	BUG_ON(!mc || !mc->nobjs);
> +	p = mc->objects[--mc->nobjs];
> +	return p;
> +}
> +
>  static void free_ptes(pmd_t *pmd, unsigned long addr)  {
>  	pte_t *pte;
> @@ -201,11 +242,363 @@ int create_hyp_io_mappings(void *from, void *to,
> phys_addr_t addr)
>  	return __create_hyp_mappings(from, to, &pfn);  }
> 
> +/**
> + * kvm_alloc_stage2_pgd - allocate level-1 table for stage-2 translation.
> + * @kvm:	The KVM struct pointer for the VM.
> + *
> + * Allocates the 1st level table only of size defined by PGD2_ORDER
> +(can
> + * support either full 40-bit input addresses or limited to 32-bit
> +input
> + * addresses). Clears the allocated pages.
> + *
> + * Note we don't need locking here as this is only called when the VM
> +is
> + * created, which can only be done once.
> + */
> +int kvm_alloc_stage2_pgd(struct kvm *kvm) {
> +	pgd_t *pgd;
> +
> +	if (kvm->arch.pgd != NULL) {
> +		kvm_err("kvm_arch already initialized?\n");
> +		return -EINVAL;
> +	}
> +
> +	pgd = (pgd_t *)__get_free_pages(GFP_KERNEL, PGD2_ORDER);
> +	if (!pgd)
> +		return -ENOMEM;
> +
> +	memset(pgd, 0, PTRS_PER_PGD2 * sizeof(pgd_t));
> +	clean_dcache_area(pgd, PTRS_PER_PGD2 * sizeof(pgd_t));
> +	kvm->arch.pgd = pgd;
> +
> +	return 0;
> +}
> +
> +static void free_guest_pages(pte_t *pte, unsigned long addr) {
> +	unsigned int i;
> +	struct page *pte_page;
> +
> +	pte_page = virt_to_page(pte);
> +
> +	for (i = 0; i < PTRS_PER_PTE; i++) {
> +		if (pte_present(*pte))
> +			put_page(pte_page);
> +		pte++;
> +	}
> +
> +	WARN_ON(page_count(pte_page) != 1);
> +}
> +
> +static void free_stage2_ptes(pmd_t *pmd, unsigned long addr) {
> +	unsigned int i;
> +	pte_t *pte;
> +	struct page *pmd_page;
> +
> +	pmd_page = virt_to_page(pmd);
> +
> +	for (i = 0; i < PTRS_PER_PMD; i++, addr += PMD_SIZE) {
> +		BUG_ON(pmd_sect(*pmd));
> +		if (!pmd_none(*pmd) && pmd_table(*pmd)) {
> +			pte = pte_offset_kernel(pmd, addr);
> +			free_guest_pages(pte, addr);
> +			pte_free_kernel(NULL, pte);
> +
> +			put_page(pmd_page);
> +		}
> +		pmd++;
> +	}
> +
> +	WARN_ON(page_count(pmd_page) != 1);
> +}
> +
> +/**
> + * kvm_free_stage2_pgd - free all stage-2 tables
> + * @kvm:	The KVM struct pointer for the VM.
> + *
> + * Walks the level-1 page table pointed to by kvm->arch.pgd and frees
> +all
> + * underlying level-2 and level-3 tables before freeing the actual
> +level-1 table
> + * and setting the struct pointer to NULL.
> + *
> + * Note we don't need locking here as this is only called when the VM
> +is
> + * destroyed, which can only be done once.
> + */
> +void kvm_free_stage2_pgd(struct kvm *kvm) {
> +	pgd_t *pgd;
> +	pud_t *pud;
> +	pmd_t *pmd;
> +	unsigned long long i, addr;
> +	struct page *pud_page;
> +
> +	if (kvm->arch.pgd == NULL)
> +		return;
> +
> +	/*
> +	 * We do this slightly different than other places, since we need more
> +	 * than 32 bits and for instance pgd_addr_end converts to unsigned
> long.
> +	 */
> +	addr = 0;
> +	for (i = 0; i < PTRS_PER_PGD2; i++) {
> +		addr = i * (unsigned long long)PGDIR_SIZE;
> +		pgd = kvm->arch.pgd + i;
> +		pud = pud_offset(pgd, addr);
> +		pud_page = virt_to_page(pud);
> +
> +		if (pud_none(*pud))
> +			continue;
> +
> +		BUG_ON(pud_bad(*pud));
> +
> +		pmd = pmd_offset(pud, addr);
> +		free_stage2_ptes(pmd, addr);
> +		pmd_free(NULL, pmd);
> +		put_page(pud_page);
> +	}
> +
> +	WARN_ON(page_count(pud_page) != 1);
> +	free_pages((unsigned long)kvm->arch.pgd, PGD2_ORDER);
> +	kvm->arch.pgd = NULL;
> +}
> +
> +/**
> + * stage2_clear_pte -- Clear a stage-2 PTE.
> + * @kvm:  The VM pointer
> + * @addr: The physical address of the PTE
> + *
> + * Clear a stage-2 PTE, lowering the various ref-counts. Also takes
> + * care of invalidating the TLBs.  Must be called while holding
> + * mmu_lock, otherwise another faulting VCPU may come in and mess
> + * things behind our back.
> + */
> +static void stage2_clear_pte(struct kvm *kvm, phys_addr_t addr) {
> +	pgd_t *pgd;
> +	pud_t *pud;
> +	pmd_t *pmd;
> +	pte_t *pte;
> +	struct page *page;
> +
> +	pgd = kvm->arch.pgd + pgd_index(addr);
> +	pud = pud_offset(pgd, addr);
> +	if (pud_none(*pud))
> +		return;
> +
> +	pmd = pmd_offset(pud, addr);
> +	if (pmd_none(*pmd))
> +		return;
> +
> +	pte = pte_offset_kernel(pmd, addr);
> +	set_pte_ext(pte, __pte(0), 0);
> +
> +	page = virt_to_page(pte);
> +	put_page(page);
> +	if (page_count(page) != 1) {
> +		kvm_tlb_flush_vmid(kvm);
> +		return;
> +	}
> +
> +	/* Need to remove pte page */
> +	pmd_clear(pmd);
> +	pte_free_kernel(NULL, (pte_t *)((unsigned long)pte &
> PAGE_MASK));
> +
> +	page = virt_to_page(pmd);
> +	put_page(page);
> +	if (page_count(page) != 1) {
> +		kvm_tlb_flush_vmid(kvm);
> +		return;
> +	}
> +
> +	pud_clear(pud);
> +	pmd_free(NULL, (pmd_t *)((unsigned long)pmd & PAGE_MASK));
> +
> +	page = virt_to_page(pud);
> +	put_page(page);
> +	kvm_tlb_flush_vmid(kvm);
> +}
> +
> +static int stage2_set_pte(struct kvm *kvm, struct
> kvm_mmu_memory_cache *cache,
> +			  phys_addr_t addr, const pte_t *new_pte, bool
> iomap) {
> +	pgd_t *pgd;
> +	pud_t *pud;
> +	pmd_t *pmd;
> +	pte_t *pte, old_pte;
> +
> +	/* Create 2nd stage page table mapping - Level 1 */
> +	pgd = kvm->arch.pgd + pgd_index(addr);
> +	pud = pud_offset(pgd, addr);
> +	if (pud_none(*pud)) {
> +		if (!cache)
> +			return 0; /* ignore calls from kvm_set_spte_hva */
> +		pmd = mmu_memory_cache_alloc(cache);
> +		pud_populate(NULL, pud, pmd);
> +		pmd += pmd_index(addr);
> +		get_page(virt_to_page(pud));
> +	} else
> +		pmd = pmd_offset(pud, addr);
> +
> +	/* Create 2nd stage page table mapping - Level 2 */
> +	if (pmd_none(*pmd)) {
> +		if (!cache)
> +			return 0; /* ignore calls from kvm_set_spte_hva */
> +		pte = mmu_memory_cache_alloc(cache);
> +		clean_pte_table(pte);
> +		pmd_populate_kernel(NULL, pmd, pte);
> +		pte += pte_index(addr);
> +		get_page(virt_to_page(pmd));
> +	} else
> +		pte = pte_offset_kernel(pmd, addr);
> +
> +	if (iomap && pte_present(*pte))
> +		return -EFAULT;
> +
> +	/* Create 2nd stage page table mapping - Level 3 */
> +	old_pte = *pte;
> +	set_pte_ext(pte, *new_pte, 0);
> +	if (pte_present(old_pte))
> +		kvm_tlb_flush_vmid(kvm);
> +	else
> +		get_page(virt_to_page(pte));
> +
> +	return 0;
> +}
> +
> +/**
> + * kvm_phys_addr_ioremap - map a device range to guest IPA
> + *
> + * @kvm:	The KVM pointer
> + * @guest_ipa:	The IPA at which to insert the mapping
> + * @pa:		The physical address of the device
> + * @size:	The size of the mapping
> + */
> +int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa,
> +			  phys_addr_t pa, unsigned long size) {
> +	phys_addr_t addr, end;
> +	int ret = 0;
> +	unsigned long pfn;
> +	struct kvm_mmu_memory_cache cache = { 0, };
> +
> +	end = (guest_ipa + size + PAGE_SIZE - 1) & PAGE_MASK;
> +	pfn = __phys_to_pfn(pa);
> +
> +	for (addr = guest_ipa; addr < end; addr += PAGE_SIZE) {
> +		pte_t pte = pfn_pte(pfn, PAGE_S2_DEVICE |
> L_PTE_S2_RDWR);
> +
> +		ret = mmu_topup_memory_cache(&cache, 2, 2);
> +		if (ret)
> +			goto out;
> +		spin_lock(&kvm->mmu_lock);
> +		ret = stage2_set_pte(kvm, &cache, addr, &pte, true);
> +		spin_unlock(&kvm->mmu_lock);
> +		if (ret)
> +			goto out;
> +
> +		pfn++;
> +	}
> +
> +out:
> +	mmu_free_memory_cache(&cache);
> +	return ret;
> +}
> +
>  int kvm_handle_guest_abort(struct kvm_vcpu *vcpu, struct kvm_run *run)
> {
>  	return -EINVAL;
>  }
> 
> +static void handle_hva_to_gpa(struct kvm *kvm,
> +			      unsigned long start,
> +			      unsigned long end,
> +			      void (*handler)(struct kvm *kvm,
> +					      gpa_t gpa, void *data),
> +			      void *data)
> +{
> +	struct kvm_memslots *slots;
> +	struct kvm_memory_slot *memslot;
> +
> +	slots = kvm_memslots(kvm);
> +
> +	/* we only care about the pages that the guest sees */
> +	kvm_for_each_memslot(memslot, slots) {
> +		unsigned long hva_start, hva_end;
> +		gfn_t gfn, gfn_end;
> +
> +		hva_start = max(start, memslot->userspace_addr);
> +		hva_end = min(end, memslot->userspace_addr +
> +					(memslot->npages << PAGE_SHIFT));
> +		if (hva_start >= hva_end)
> +			continue;
> +
> +		/*
> +		 * {gfn(page) | page intersects with [hva_start, hva_end)} =
> +		 * {gfn_start, gfn_start+1, ..., gfn_end-1}.
> +		 */
> +		gfn = hva_to_gfn_memslot(hva_start, memslot);
> +		gfn_end = hva_to_gfn_memslot(hva_end + PAGE_SIZE - 1,
> memslot);
> +
> +		for (; gfn < gfn_end; ++gfn) {
> +			gpa_t gpa = gfn << PAGE_SHIFT;
> +			handler(kvm, gpa, data);
> +		}
> +	}
> +}
> +
> +static void kvm_unmap_hva_handler(struct kvm *kvm, gpa_t gpa, void
> +*data) {
> +	stage2_clear_pte(kvm, gpa);
> +}
> +
> +int kvm_unmap_hva(struct kvm *kvm, unsigned long hva) {
> +	unsigned long end = hva + PAGE_SIZE;
> +
> +	if (!kvm->arch.pgd)
> +		return 0;
> +
> +	trace_kvm_unmap_hva(hva);
> +	handle_hva_to_gpa(kvm, hva, end, &kvm_unmap_hva_handler,
> NULL);
> +	return 0;
> +}
> +
> +int kvm_unmap_hva_range(struct kvm *kvm,
> +			unsigned long start, unsigned long end) {
> +	if (!kvm->arch.pgd)
> +		return 0;
> +
> +	trace_kvm_unmap_hva_range(start, end);
> +	handle_hva_to_gpa(kvm, start, end, &kvm_unmap_hva_handler,
> NULL);
> +	return 0;
> +}
> +
> +static void kvm_set_spte_handler(struct kvm *kvm, gpa_t gpa, void
> +*data) {
> +	pte_t *pte = (pte_t *)data;
> +
> +	stage2_set_pte(kvm, NULL, gpa, pte, false); }
> +
> +
> +void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) {
> +	unsigned long end = hva + PAGE_SIZE;
> +	pte_t stage2_pte;
> +
> +	if (!kvm->arch.pgd)
> +		return;
> +
> +	trace_kvm_set_spte_hva(hva);
> +	stage2_pte = pfn_pte(pte_pfn(pte), PAGE_S2);
> +	handle_hva_to_gpa(kvm, hva, end, &kvm_set_spte_handler,
> &stage2_pte);
> +}
> +
> +void kvm_mmu_free_memory_caches(struct kvm_vcpu *vcpu) {
> +	mmu_free_memory_cache(&vcpu->arch.mmu_page_cache);
> +}
> +
>  unsigned long kvm_mmu_get_httbr(void)
>  {
>  	return virt_to_phys(hyp_pgd);
> diff --git a/arch/arm/kvm/trace.h b/arch/arm/kvm/trace.h index
> f8869c1..862b2cc 100644
> --- a/arch/arm/kvm/trace.h
> +++ b/arch/arm/kvm/trace.h
> @@ -39,7 +39,53 @@ TRACE_EVENT(kvm_exit,
>  	TP_printk("PC: 0x%08lx", __entry->vcpu_pc)  );
> 
> +TRACE_EVENT(kvm_unmap_hva,
> +	TP_PROTO(unsigned long hva),
> +	TP_ARGS(hva),
> 
> +	TP_STRUCT__entry(
> +		__field(	unsigned long,	hva		)
> +	),
> +
> +	TP_fast_assign(
> +		__entry->hva		= hva;
> +	),
> +
> +	TP_printk("mmu notifier unmap hva: %#08lx", __entry->hva) );
> +
> +TRACE_EVENT(kvm_unmap_hva_range,
> +	TP_PROTO(unsigned long start, unsigned long end),
> +	TP_ARGS(start, end),
> +
> +	TP_STRUCT__entry(
> +		__field(	unsigned long,	start		)
> +		__field(	unsigned long,	end		)
> +	),
> +
> +	TP_fast_assign(
> +		__entry->start		= start;
> +		__entry->end		= end;
> +	),
> +
> +	TP_printk("mmu notifier unmap range: %#08lx -- %#08lx",
> +		  __entry->start, __entry->end)
> +);
> +
> +TRACE_EVENT(kvm_set_spte_hva,
> +	TP_PROTO(unsigned long hva),
> +	TP_ARGS(hva),
> +
> +	TP_STRUCT__entry(
> +		__field(	unsigned long,	hva		)
> +	),
> +
> +	TP_fast_assign(
> +		__entry->hva		= hva;
> +	),
> +
> +	TP_printk("mmu notifier set pte hva: %#08lx", __entry->hva) );
> 
>  #endif /* _TRACE_KVM_H */
> 
> 
> _______________________________________________
> kvmarm mailing list
> kvmarm at lists.cs.columbia.edu
> https://lists.cs.columbia.edu/cucslists/listinfo/kvmarm



More information about the linux-arm-kernel mailing list