summaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorPaul Mackerras <paulus@samba.org>2015-06-24 07:18:07 -0400
committerAlexander Graf <agraf@suse.de>2015-08-22 05:16:18 -0400
commitcdeee51842b55acb3e699a4083fa97e82384e456 (patch)
treef5ec12826f278325f24fa4f51ada03524e142397 /arch/powerpc
parent08fe1e7bd216339501c4eb0d0df0f413d715327a (diff)
KVM: PPC: Book3S HV: Implement H_CLEAR_REF and H_CLEAR_MOD
This adds implementations for the H_CLEAR_REF (test and clear reference bit) and H_CLEAR_MOD (test and clear changed bit) hypercalls. When clearing the reference or change bit in the guest view of the HPTE, we also have to clear it in the real HPTE so that we can detect future references or changes. When we do so, we transfer the R or C bit value to the rmap entry for the underlying host page so that kvm_age_hva_hv(), kvm_test_age_hva_hv() and kvmppc_hv_get_dirty_log() know that the page has been referenced and/or changed. These hypercalls are not used by Linux guests. These implementations have been tested using a FreeBSD guest. Signed-off-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Alexander Graf <agraf@suse.de>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/kvm/book3s_hv_rm_mmu.c126
-rw-r--r--arch/powerpc/kvm/book3s_hv_rmhandlers.S4
2 files changed, 121 insertions, 9 deletions
diff --git a/arch/powerpc/kvm/book3s_hv_rm_mmu.c b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
index c7a3ab2652d0..c1df9bb1e413 100644
--- a/arch/powerpc/kvm/book3s_hv_rm_mmu.c
+++ b/arch/powerpc/kvm/book3s_hv_rm_mmu.c
@@ -112,25 +112,38 @@ void kvmppc_update_rmap_change(unsigned long *rmap, unsigned long psize)
112} 112}
113EXPORT_SYMBOL_GPL(kvmppc_update_rmap_change); 113EXPORT_SYMBOL_GPL(kvmppc_update_rmap_change);
114 114
115/* Returns a pointer to the revmap entry for the page mapped by a HPTE */
116static unsigned long *revmap_for_hpte(struct kvm *kvm, unsigned long hpte_v,
117 unsigned long hpte_gr)
118{
119 struct kvm_memory_slot *memslot;
120 unsigned long *rmap;
121 unsigned long gfn;
122
123 gfn = hpte_rpn(hpte_gr, hpte_page_size(hpte_v, hpte_gr));
124 memslot = __gfn_to_memslot(kvm_memslots_raw(kvm), gfn);
125 if (!memslot)
126 return NULL;
127
128 rmap = real_vmalloc_addr(&memslot->arch.rmap[gfn - memslot->base_gfn]);
129 return rmap;
130}
131
115/* Remove this HPTE from the chain for a real page */ 132/* Remove this HPTE from the chain for a real page */
116static void remove_revmap_chain(struct kvm *kvm, long pte_index, 133static void remove_revmap_chain(struct kvm *kvm, long pte_index,
117 struct revmap_entry *rev, 134 struct revmap_entry *rev,
118 unsigned long hpte_v, unsigned long hpte_r) 135 unsigned long hpte_v, unsigned long hpte_r)
119{ 136{
120 struct revmap_entry *next, *prev; 137 struct revmap_entry *next, *prev;
121 unsigned long gfn, ptel, head; 138 unsigned long ptel, head;
122 struct kvm_memory_slot *memslot;
123 unsigned long *rmap; 139 unsigned long *rmap;
124 unsigned long rcbits; 140 unsigned long rcbits;
125 141
126 rcbits = hpte_r & (HPTE_R_R | HPTE_R_C); 142 rcbits = hpte_r & (HPTE_R_R | HPTE_R_C);
127 ptel = rev->guest_rpte |= rcbits; 143 ptel = rev->guest_rpte |= rcbits;
128 gfn = hpte_rpn(ptel, hpte_page_size(hpte_v, ptel)); 144 rmap = revmap_for_hpte(kvm, hpte_v, ptel);
129 memslot = __gfn_to_memslot(kvm_memslots_raw(kvm), gfn); 145 if (!rmap)
130 if (!memslot)
131 return; 146 return;
132
133 rmap = real_vmalloc_addr(&memslot->arch.rmap[gfn - memslot->base_gfn]);
134 lock_rmap(rmap); 147 lock_rmap(rmap);
135 148
136 head = *rmap & KVMPPC_RMAP_INDEX; 149 head = *rmap & KVMPPC_RMAP_INDEX;
@@ -678,6 +691,105 @@ long kvmppc_h_read(struct kvm_vcpu *vcpu, unsigned long flags,
678 return H_SUCCESS; 691 return H_SUCCESS;
679} 692}
680 693
694long kvmppc_h_clear_ref(struct kvm_vcpu *vcpu, unsigned long flags,
695 unsigned long pte_index)
696{
697 struct kvm *kvm = vcpu->kvm;
698 __be64 *hpte;
699 unsigned long v, r, gr;
700 struct revmap_entry *rev;
701 unsigned long *rmap;
702 long ret = H_NOT_FOUND;
703
704 if (pte_index >= kvm->arch.hpt_npte)
705 return H_PARAMETER;
706
707 rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
708 hpte = (__be64 *)(kvm->arch.hpt_virt + (pte_index << 4));
709 while (!try_lock_hpte(hpte, HPTE_V_HVLOCK))
710 cpu_relax();
711 v = be64_to_cpu(hpte[0]);
712 r = be64_to_cpu(hpte[1]);
713 if (!(v & (HPTE_V_VALID | HPTE_V_ABSENT)))
714 goto out;
715
716 gr = rev->guest_rpte;
717 if (rev->guest_rpte & HPTE_R_R) {
718 rev->guest_rpte &= ~HPTE_R_R;
719 note_hpte_modification(kvm, rev);
720 }
721 if (v & HPTE_V_VALID) {
722 gr |= r & (HPTE_R_R | HPTE_R_C);
723 if (r & HPTE_R_R) {
724 kvmppc_clear_ref_hpte(kvm, hpte, pte_index);
725 rmap = revmap_for_hpte(kvm, v, gr);
726 if (rmap) {
727 lock_rmap(rmap);
728 *rmap |= KVMPPC_RMAP_REFERENCED;
729 unlock_rmap(rmap);
730 }
731 }
732 }
733 vcpu->arch.gpr[4] = gr;
734 ret = H_SUCCESS;
735 out:
736 unlock_hpte(hpte, v & ~HPTE_V_HVLOCK);
737 return ret;
738}
739
740long kvmppc_h_clear_mod(struct kvm_vcpu *vcpu, unsigned long flags,
741 unsigned long pte_index)
742{
743 struct kvm *kvm = vcpu->kvm;
744 __be64 *hpte;
745 unsigned long v, r, gr;
746 struct revmap_entry *rev;
747 unsigned long *rmap;
748 long ret = H_NOT_FOUND;
749
750 if (pte_index >= kvm->arch.hpt_npte)
751 return H_PARAMETER;
752
753 rev = real_vmalloc_addr(&kvm->arch.revmap[pte_index]);
754 hpte = (__be64 *)(kvm->arch.hpt_virt + (pte_index << 4));
755 while (!try_lock_hpte(hpte, HPTE_V_HVLOCK))
756 cpu_relax();
757 v = be64_to_cpu(hpte[0]);
758 r = be64_to_cpu(hpte[1]);
759 if (!(v & (HPTE_V_VALID | HPTE_V_ABSENT)))
760 goto out;
761
762 gr = rev->guest_rpte;
763 if (gr & HPTE_R_C) {
764 rev->guest_rpte &= ~HPTE_R_C;
765 note_hpte_modification(kvm, rev);
766 }
767 if (v & HPTE_V_VALID) {
768 /* need to make it temporarily absent so C is stable */
769 hpte[0] |= cpu_to_be64(HPTE_V_ABSENT);
770 kvmppc_invalidate_hpte(kvm, hpte, pte_index);
771 r = be64_to_cpu(hpte[1]);
772 gr |= r & (HPTE_R_R | HPTE_R_C);
773 if (r & HPTE_R_C) {
774 unsigned long psize = hpte_page_size(v, r);
775 hpte[1] = cpu_to_be64(r & ~HPTE_R_C);
776 eieio();
777 rmap = revmap_for_hpte(kvm, v, gr);
778 if (rmap) {
779 lock_rmap(rmap);
780 *rmap |= KVMPPC_RMAP_CHANGED;
781 kvmppc_update_rmap_change(rmap, psize);
782 unlock_rmap(rmap);
783 }
784 }
785 }
786 vcpu->arch.gpr[4] = gr;
787 ret = H_SUCCESS;
788 out:
789 unlock_hpte(hpte, v & ~HPTE_V_HVLOCK);
790 return ret;
791}
792
681void kvmppc_invalidate_hpte(struct kvm *kvm, __be64 *hptep, 793void kvmppc_invalidate_hpte(struct kvm *kvm, __be64 *hptep,
682 unsigned long pte_index) 794 unsigned long pte_index)
683{ 795{
diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
index db2427db4471..e3477667d498 100644
--- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
+++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
@@ -1904,8 +1904,8 @@ hcall_real_table:
1904 .long DOTSYM(kvmppc_h_remove) - hcall_real_table 1904 .long DOTSYM(kvmppc_h_remove) - hcall_real_table
1905 .long DOTSYM(kvmppc_h_enter) - hcall_real_table 1905 .long DOTSYM(kvmppc_h_enter) - hcall_real_table
1906 .long DOTSYM(kvmppc_h_read) - hcall_real_table 1906 .long DOTSYM(kvmppc_h_read) - hcall_real_table
1907 .long 0 /* 0x10 - H_CLEAR_MOD */ 1907 .long DOTSYM(kvmppc_h_clear_mod) - hcall_real_table
1908 .long 0 /* 0x14 - H_CLEAR_REF */ 1908 .long DOTSYM(kvmppc_h_clear_ref) - hcall_real_table
1909 .long DOTSYM(kvmppc_h_protect) - hcall_real_table 1909 .long DOTSYM(kvmppc_h_protect) - hcall_real_table
1910 .long DOTSYM(kvmppc_h_get_tce) - hcall_real_table 1910 .long DOTSYM(kvmppc_h_get_tce) - hcall_real_table
1911 .long DOTSYM(kvmppc_h_put_tce) - hcall_real_table 1911 .long DOTSYM(kvmppc_h_put_tce) - hcall_real_table