[kvmarm] [PATCH v3 06/14] KVM: ARM: Memory virtualization setup
Christoffer Dall
c.dall at virtualopensystems.com
Mon Nov 19 09:42:42 EST 2012
On Mon, Nov 19, 2012 at 6:29 AM, Sundaram, Senthilkumar
<ssundara at qti.qualcomm.com> wrote:
> What is the use of MMU Notifiers in the absence of Shadow Page Table?
>
> Thanks
> Senthil
MMU notifiers are used to manage the stage-2 page tables in the event
of swapping or KSM. Please don't top-top post to this list.
-Christoffer
>> -----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