GNU Linux-libre 4.14.313-gnu1
[releases.git] / arch / arm64 / kvm / hyp / tlb.c
1 /*
2  * Copyright (C) 2015 - ARM Ltd
3  * Author: Marc Zyngier <marc.zyngier@arm.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <linux/irqflags.h>
19
20 #include <asm/kvm_hyp.h>
21 #include <asm/tlbflush.h>
22
23 static void __hyp_text __tlb_switch_to_guest_vhe(struct kvm *kvm,
24                                                  unsigned long *flags)
25 {
26         u64 val;
27
28         local_irq_save(*flags);
29
30         /*
31          * With VHE enabled, we have HCR_EL2.{E2H,TGE} = {1,1}, and
32          * most TLB operations target EL2/EL0. In order to affect the
33          * guest TLBs (EL1/EL0), we need to change one of these two
34          * bits. Changing E2H is impossible (goodbye TTBR1_EL2), so
35          * let's flip TGE before executing the TLB operation.
36          */
37         write_sysreg(kvm->arch.vttbr, vttbr_el2);
38         val = read_sysreg(hcr_el2);
39         val &= ~HCR_TGE;
40         write_sysreg(val, hcr_el2);
41         isb();
42 }
43
44 static void __hyp_text __tlb_switch_to_guest_nvhe(struct kvm *kvm,
45                                                   unsigned long *flags)
46 {
47         write_sysreg(kvm->arch.vttbr, vttbr_el2);
48         isb();
49 }
50
51 static hyp_alternate_select(__tlb_switch_to_guest,
52                             __tlb_switch_to_guest_nvhe,
53                             __tlb_switch_to_guest_vhe,
54                             ARM64_HAS_VIRT_HOST_EXTN);
55
56 static void __hyp_text __tlb_switch_to_host_vhe(struct kvm *kvm,
57                                                 unsigned long flags)
58 {
59         /*
60          * We're done with the TLB operation, let's restore the host's
61          * view of HCR_EL2.
62          */
63         write_sysreg(0, vttbr_el2);
64         write_sysreg(HCR_HOST_VHE_FLAGS, hcr_el2);
65         isb();
66         local_irq_restore(flags);
67 }
68
69 static void __hyp_text __tlb_switch_to_host_nvhe(struct kvm *kvm,
70                                                  unsigned long flags)
71 {
72         write_sysreg(0, vttbr_el2);
73 }
74
75 static hyp_alternate_select(__tlb_switch_to_host,
76                             __tlb_switch_to_host_nvhe,
77                             __tlb_switch_to_host_vhe,
78                             ARM64_HAS_VIRT_HOST_EXTN);
79
80 void __hyp_text __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa)
81 {
82         unsigned long flags;
83
84         dsb(ishst);
85
86         /* Switch to requested VMID */
87         kvm = kern_hyp_va(kvm);
88         __tlb_switch_to_guest()(kvm, &flags);
89
90         /*
91          * We could do so much better if we had the VA as well.
92          * Instead, we invalidate Stage-2 for this IPA, and the
93          * whole of Stage-1. Weep...
94          */
95         ipa >>= 12;
96         __tlbi(ipas2e1is, ipa);
97
98         /*
99          * We have to ensure completion of the invalidation at Stage-2,
100          * since a table walk on another CPU could refill a TLB with a
101          * complete (S1 + S2) walk based on the old Stage-2 mapping if
102          * the Stage-1 invalidation happened first.
103          */
104         dsb(ish);
105         __tlbi(vmalle1is);
106         dsb(ish);
107         isb();
108
109         /*
110          * If the host is running at EL1 and we have a VPIPT I-cache,
111          * then we must perform I-cache maintenance at EL2 in order for
112          * it to have an effect on the guest. Since the guest cannot hit
113          * I-cache lines allocated with a different VMID, we don't need
114          * to worry about junk out of guest reset (we nuke the I-cache on
115          * VMID rollover), but we do need to be careful when remapping
116          * executable pages for the same guest. This can happen when KSM
117          * takes a CoW fault on an executable page, copies the page into
118          * a page that was previously mapped in the guest and then needs
119          * to invalidate the guest view of the I-cache for that page
120          * from EL1. To solve this, we invalidate the entire I-cache when
121          * unmapping a page from a guest if we have a VPIPT I-cache but
122          * the host is running at EL1. As above, we could do better if
123          * we had the VA.
124          *
125          * The moral of this story is: if you have a VPIPT I-cache, then
126          * you should be running with VHE enabled.
127          */
128         if (!has_vhe() && icache_is_vpipt())
129                 __flush_icache_all();
130
131         __tlb_switch_to_host()(kvm, flags);
132 }
133
134 void __hyp_text __kvm_tlb_flush_vmid(struct kvm *kvm)
135 {
136         unsigned long flags;
137
138         dsb(ishst);
139
140         /* Switch to requested VMID */
141         kvm = kern_hyp_va(kvm);
142         __tlb_switch_to_guest()(kvm, &flags);
143
144         __tlbi(vmalls12e1is);
145         dsb(ish);
146         isb();
147
148         __tlb_switch_to_host()(kvm, flags);
149 }
150
151 void __hyp_text __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu)
152 {
153         struct kvm *kvm = kern_hyp_va(kern_hyp_va(vcpu)->kvm);
154         unsigned long flags;
155
156         /* Switch to requested VMID */
157         __tlb_switch_to_guest()(kvm, &flags);
158
159         __tlbi(vmalle1);
160         dsb(nsh);
161         isb();
162
163         __tlb_switch_to_host()(kvm, flags);
164 }
165
166 void __hyp_text __kvm_flush_vm_context(void)
167 {
168         dsb(ishst);
169         __tlbi(alle1is);
170         asm volatile("ic ialluis" : : );
171         dsb(ish);
172 }