aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHollis Blanchard <hollisb@us.ibm.com>2008-12-02 16:51:55 -0500
committerAvi Kivity <avi@redhat.com>2008-12-31 09:55:09 -0500
commit7924bd41097ae8991c6d38cef8b1e4058e30d198 (patch)
treeb39629f81598739eb886126c5f3f8705656ce9cd
parentc0ca609c5f874f7d6ae8e180afe79317e1943d22 (diff)
KVM: ppc: directly insert shadow mappings into the hardware TLB
Formerly, we used to maintain a per-vcpu shadow TLB and on every entry to the guest would load this array into the hardware TLB. This consumed 1280 bytes of memory (64 entries of 16 bytes plus a struct page pointer each), and also required some assembly to loop over the array on every entry. Instead of saving a copy in memory, we can just store shadow mappings directly into the hardware TLB, accepting that the host kernel will clobber these as part of the normal 440 TLB round robin. When we do that we need less than half the memory, and we have decreased the exit handling time for all guest exits, at the cost of increased number of TLB misses because the host overwrites some guest entries. These savings will be increased on processors with larger TLBs or which implement intelligent flush instructions like tlbivax (which will avoid the need to walk arrays in software). In addition to that and to the code simplification, we have a greater chance of leaving other host userspace mappings in the TLB, instead of forcing all subsequent tasks to re-fault all their mappings. Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com> Signed-off-by: Avi Kivity <avi@redhat.com>
-rw-r--r--arch/powerpc/include/asm/kvm_44x.h24
-rw-r--r--arch/powerpc/include/asm/kvm_ppc.h3
-rw-r--r--arch/powerpc/kernel/asm-offsets.c6
-rw-r--r--arch/powerpc/kvm/44x.c19
-rw-r--r--arch/powerpc/kvm/44x_tlb.c256
-rw-r--r--arch/powerpc/kvm/44x_tlb.h7
-rw-r--r--arch/powerpc/kvm/booke.c26
-rw-r--r--arch/powerpc/kvm/booke_interrupts.S48
8 files changed, 168 insertions, 221 deletions
diff --git a/arch/powerpc/include/asm/kvm_44x.h b/arch/powerpc/include/asm/kvm_44x.h
index 72e593914adb..e770ea2bbb1c 100644
--- a/arch/powerpc/include/asm/kvm_44x.h
+++ b/arch/powerpc/include/asm/kvm_44x.h
@@ -22,19 +22,25 @@
22 22
23#include <linux/kvm_host.h> 23#include <linux/kvm_host.h>
24 24
25/* XXX Can't include mmu-44x.h because it redefines struct mm_context. */
26#define PPC44x_TLB_SIZE 64 25#define PPC44x_TLB_SIZE 64
27 26
27/* If the guest is expecting it, this can be as large as we like; we'd just
28 * need to find some way of advertising it. */
29#define KVM44x_GUEST_TLB_SIZE 64
30
31struct kvmppc_44x_shadow_ref {
32 struct page *page;
33 u16 gtlb_index;
34 u8 writeable;
35 u8 tid;
36};
37
28struct kvmppc_vcpu_44x { 38struct kvmppc_vcpu_44x {
29 /* Unmodified copy of the guest's TLB. */ 39 /* Unmodified copy of the guest's TLB. */
30 struct kvmppc_44x_tlbe guest_tlb[PPC44x_TLB_SIZE]; 40 struct kvmppc_44x_tlbe guest_tlb[KVM44x_GUEST_TLB_SIZE];
31 /* TLB that's actually used when the guest is running. */ 41
32 struct kvmppc_44x_tlbe shadow_tlb[PPC44x_TLB_SIZE]; 42 /* References to guest pages in the hardware TLB. */
33 /* Pages which are referenced in the shadow TLB. */ 43 struct kvmppc_44x_shadow_ref shadow_refs[PPC44x_TLB_SIZE];
34 struct page *shadow_pages[PPC44x_TLB_SIZE];
35
36 /* Track which TLB entries we've modified in the current exit. */
37 u8 shadow_tlb_mod[PPC44x_TLB_SIZE];
38 44
39 struct kvm_vcpu vcpu; 45 struct kvm_vcpu vcpu;
40}; 46};
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h
index 5bb29267d6a6..36d2a50a8487 100644
--- a/arch/powerpc/include/asm/kvm_ppc.h
+++ b/arch/powerpc/include/asm/kvm_ppc.h
@@ -53,7 +53,8 @@ extern int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu);
53extern void kvmppc_emulate_dec(struct kvm_vcpu *vcpu); 53extern void kvmppc_emulate_dec(struct kvm_vcpu *vcpu);
54 54
55extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr, 55extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr,
56 u64 asid, u32 flags, u32 max_bytes); 56 u64 asid, u32 flags, u32 max_bytes,
57 unsigned int gtlb_idx);
57extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode); 58extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode);
58extern void kvmppc_mmu_switch_pid(struct kvm_vcpu *vcpu, u32 pid); 59extern void kvmppc_mmu_switch_pid(struct kvm_vcpu *vcpu, u32 pid);
59 60
diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c
index 393c7f36a1e8..ba39526d3201 100644
--- a/arch/powerpc/kernel/asm-offsets.c
+++ b/arch/powerpc/kernel/asm-offsets.c
@@ -359,12 +359,6 @@ int main(void)
359#ifdef CONFIG_KVM 359#ifdef CONFIG_KVM
360 DEFINE(TLBE_BYTES, sizeof(struct kvmppc_44x_tlbe)); 360 DEFINE(TLBE_BYTES, sizeof(struct kvmppc_44x_tlbe));
361 361
362 DEFINE(VCPU_TO_44X, offsetof(struct kvmppc_vcpu_44x, vcpu));
363 DEFINE(VCPU44x_SHADOW_TLB,
364 offsetof(struct kvmppc_vcpu_44x, shadow_tlb));
365 DEFINE(VCPU44x_SHADOW_MOD,
366 offsetof(struct kvmppc_vcpu_44x, shadow_tlb_mod));
367
368 DEFINE(VCPU_HOST_STACK, offsetof(struct kvm_vcpu, arch.host_stack)); 362 DEFINE(VCPU_HOST_STACK, offsetof(struct kvm_vcpu, arch.host_stack));
369 DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid)); 363 DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid));
370 DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr)); 364 DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr));
diff --git a/arch/powerpc/kvm/44x.c b/arch/powerpc/kvm/44x.c
index 22054b164b5a..05d72fc8b478 100644
--- a/arch/powerpc/kvm/44x.c
+++ b/arch/powerpc/kvm/44x.c
@@ -96,21 +96,14 @@ void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu)
96 96
97void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) 97void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
98{ 98{
99 int i;
100
101 /* Mark every guest entry in the shadow TLB entry modified, so that they
102 * will all be reloaded on the next vcpu run (instead of being
103 * demand-faulted). */
104 for (i = 0; i <= tlb_44x_hwater; i++)
105 kvmppc_tlbe_set_modified(vcpu, i);
106} 99}
107 100
108void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu) 101void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu)
109{ 102{
110 /* Don't leave guest TLB entries resident when being de-scheduled. */ 103 /* XXX Since every guest uses TS=1 TID=0/1 mappings, we can't leave any TLB
111 /* XXX It would be nice to differentiate between heavyweight exit and 104 * entries around when we're descheduled, so we must completely flush the
112 * sched_out here, since we could avoid the TLB flush for heavyweight 105 * TLB of all guest mappings. On the other hand, if there is only one
113 * exits. */ 106 * guest, this flush is completely unnecessary. */
114 _tlbia(); 107 _tlbia();
115} 108}
116 109
@@ -130,6 +123,7 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
130{ 123{
131 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu); 124 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
132 struct kvmppc_44x_tlbe *tlbe = &vcpu_44x->guest_tlb[0]; 125 struct kvmppc_44x_tlbe *tlbe = &vcpu_44x->guest_tlb[0];
126 int i;
133 127
134 tlbe->tid = 0; 128 tlbe->tid = 0;
135 tlbe->word0 = PPC44x_TLB_16M | PPC44x_TLB_VALID; 129 tlbe->word0 = PPC44x_TLB_16M | PPC44x_TLB_VALID;
@@ -148,6 +142,9 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu)
148 * CCR1[TCS]. */ 142 * CCR1[TCS]. */
149 vcpu->arch.ccr1 = mfspr(SPRN_CCR1); 143 vcpu->arch.ccr1 = mfspr(SPRN_CCR1);
150 144
145 for (i = 0; i < ARRAY_SIZE(vcpu_44x->shadow_refs); i++)
146 vcpu_44x->shadow_refs[i].gtlb_index = -1;
147
151 return 0; 148 return 0;
152} 149}
153 150
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c
index d49dc66ab3c3..2981ebea3d1f 100644
--- a/arch/powerpc/kvm/44x_tlb.c
+++ b/arch/powerpc/kvm/44x_tlb.c
@@ -22,6 +22,8 @@
22#include <linux/kvm.h> 22#include <linux/kvm.h>
23#include <linux/kvm_host.h> 23#include <linux/kvm_host.h>
24#include <linux/highmem.h> 24#include <linux/highmem.h>
25
26#include <asm/tlbflush.h>
25#include <asm/mmu-44x.h> 27#include <asm/mmu-44x.h>
26#include <asm/kvm_ppc.h> 28#include <asm/kvm_ppc.h>
27#include <asm/kvm_44x.h> 29#include <asm/kvm_44x.h>
@@ -40,8 +42,6 @@
40#define PPC44x_TLB_USER_PERM_MASK (PPC44x_TLB_UX|PPC44x_TLB_UR|PPC44x_TLB_UW) 42#define PPC44x_TLB_USER_PERM_MASK (PPC44x_TLB_UX|PPC44x_TLB_UR|PPC44x_TLB_UW)
41#define PPC44x_TLB_SUPER_PERM_MASK (PPC44x_TLB_SX|PPC44x_TLB_SR|PPC44x_TLB_SW) 43#define PPC44x_TLB_SUPER_PERM_MASK (PPC44x_TLB_SX|PPC44x_TLB_SR|PPC44x_TLB_SW)
42 44
43static unsigned int kvmppc_tlb_44x_pos;
44
45#ifdef DEBUG 45#ifdef DEBUG
46void kvmppc_dump_tlbs(struct kvm_vcpu *vcpu) 46void kvmppc_dump_tlbs(struct kvm_vcpu *vcpu)
47{ 47{
@@ -52,24 +52,49 @@ void kvmppc_dump_tlbs(struct kvm_vcpu *vcpu)
52 printk("| %2s | %3s | %8s | %8s | %8s |\n", 52 printk("| %2s | %3s | %8s | %8s | %8s |\n",
53 "nr", "tid", "word0", "word1", "word2"); 53 "nr", "tid", "word0", "word1", "word2");
54 54
55 for (i = 0; i < PPC44x_TLB_SIZE; i++) { 55 for (i = 0; i < ARRAY_SIZE(vcpu_44x->guest_tlb); i++) {
56 tlbe = &vcpu_44x->guest_tlb[i]; 56 tlbe = &vcpu_44x->guest_tlb[i];
57 if (tlbe->word0 & PPC44x_TLB_VALID) 57 if (tlbe->word0 & PPC44x_TLB_VALID)
58 printk(" G%2d | %02X | %08X | %08X | %08X |\n", 58 printk(" G%2d | %02X | %08X | %08X | %08X |\n",
59 i, tlbe->tid, tlbe->word0, tlbe->word1, 59 i, tlbe->tid, tlbe->word0, tlbe->word1,
60 tlbe->word2); 60 tlbe->word2);
61 } 61 }
62
63 for (i = 0; i < PPC44x_TLB_SIZE; i++) {
64 tlbe = &vcpu_44x->shadow_tlb[i];
65 if (tlbe->word0 & PPC44x_TLB_VALID)
66 printk(" S%2d | %02X | %08X | %08X | %08X |\n",
67 i, tlbe->tid, tlbe->word0, tlbe->word1,
68 tlbe->word2);
69 }
70} 62}
71#endif 63#endif
72 64
65static inline void kvmppc_44x_tlbie(unsigned int index)
66{
67 /* 0 <= index < 64, so the V bit is clear and we can use the index as
68 * word0. */
69 asm volatile(
70 "tlbwe %[index], %[index], 0\n"
71 :
72 : [index] "r"(index)
73 );
74}
75
76static inline void kvmppc_44x_tlbwe(unsigned int index,
77 struct kvmppc_44x_tlbe *stlbe)
78{
79 unsigned long tmp;
80
81 asm volatile(
82 "mfspr %[tmp], %[sprn_mmucr]\n"
83 "rlwimi %[tmp], %[tid], 0, 0xff\n"
84 "mtspr %[sprn_mmucr], %[tmp]\n"
85 "tlbwe %[word0], %[index], 0\n"
86 "tlbwe %[word1], %[index], 1\n"
87 "tlbwe %[word2], %[index], 2\n"
88 : [tmp] "=&r"(tmp)
89 : [word0] "r"(stlbe->word0),
90 [word1] "r"(stlbe->word1),
91 [word2] "r"(stlbe->word2),
92 [tid] "r"(stlbe->tid),
93 [index] "r"(index),
94 [sprn_mmucr] "i"(SPRN_MMUCR)
95 );
96}
97
73static u32 kvmppc_44x_tlb_shadow_attrib(u32 attrib, int usermode) 98static u32 kvmppc_44x_tlb_shadow_attrib(u32 attrib, int usermode)
74{ 99{
75 /* We only care about the guest's permission and user bits. */ 100 /* We only care about the guest's permission and user bits. */
@@ -99,7 +124,7 @@ int kvmppc_44x_tlb_index(struct kvm_vcpu *vcpu, gva_t eaddr, unsigned int pid,
99 int i; 124 int i;
100 125
101 /* XXX Replace loop with fancy data structures. */ 126 /* XXX Replace loop with fancy data structures. */
102 for (i = 0; i < PPC44x_TLB_SIZE; i++) { 127 for (i = 0; i < ARRAY_SIZE(vcpu_44x->guest_tlb); i++) {
103 struct kvmppc_44x_tlbe *tlbe = &vcpu_44x->guest_tlb[i]; 128 struct kvmppc_44x_tlbe *tlbe = &vcpu_44x->guest_tlb[i];
104 unsigned int tid; 129 unsigned int tid;
105 130
@@ -125,65 +150,53 @@ int kvmppc_44x_tlb_index(struct kvm_vcpu *vcpu, gva_t eaddr, unsigned int pid,
125 return -1; 150 return -1;
126} 151}
127 152
128struct kvmppc_44x_tlbe *kvmppc_44x_itlb_search(struct kvm_vcpu *vcpu, 153int kvmppc_44x_itlb_index(struct kvm_vcpu *vcpu, gva_t eaddr)
129 gva_t eaddr)
130{ 154{
131 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
132 unsigned int as = !!(vcpu->arch.msr & MSR_IS); 155 unsigned int as = !!(vcpu->arch.msr & MSR_IS);
133 unsigned int index;
134 156
135 index = kvmppc_44x_tlb_index(vcpu, eaddr, vcpu->arch.pid, as); 157 return kvmppc_44x_tlb_index(vcpu, eaddr, vcpu->arch.pid, as);
136 if (index == -1)
137 return NULL;
138 return &vcpu_44x->guest_tlb[index];
139} 158}
140 159
141struct kvmppc_44x_tlbe *kvmppc_44x_dtlb_search(struct kvm_vcpu *vcpu, 160int kvmppc_44x_dtlb_index(struct kvm_vcpu *vcpu, gva_t eaddr)
142 gva_t eaddr)
143{ 161{
144 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
145 unsigned int as = !!(vcpu->arch.msr & MSR_DS); 162 unsigned int as = !!(vcpu->arch.msr & MSR_DS);
146 unsigned int index;
147 163
148 index = kvmppc_44x_tlb_index(vcpu, eaddr, vcpu->arch.pid, as); 164 return kvmppc_44x_tlb_index(vcpu, eaddr, vcpu->arch.pid, as);
149 if (index == -1)
150 return NULL;
151 return &vcpu_44x->guest_tlb[index];
152} 165}
153 166
154static int kvmppc_44x_tlbe_is_writable(struct kvmppc_44x_tlbe *tlbe) 167static void kvmppc_44x_shadow_release(struct kvmppc_vcpu_44x *vcpu_44x,
168 unsigned int stlb_index)
155{ 169{
156 return tlbe->word2 & (PPC44x_TLB_SW|PPC44x_TLB_UW); 170 struct kvmppc_44x_shadow_ref *ref = &vcpu_44x->shadow_refs[stlb_index];
157}
158 171
159static void kvmppc_44x_shadow_release(struct kvm_vcpu *vcpu, 172 if (!ref->page)
160 unsigned int index) 173 return;
161{
162 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
163 struct kvmppc_44x_tlbe *stlbe = &vcpu_44x->shadow_tlb[index];
164 struct page *page = vcpu_44x->shadow_pages[index];
165 174
166 if (get_tlb_v(stlbe)) { 175 /* Discard from the TLB. */
167 if (kvmppc_44x_tlbe_is_writable(stlbe)) 176 /* Note: we could actually invalidate a host mapping, if the host overwrote
168 kvm_release_page_dirty(page); 177 * this TLB entry since we inserted a guest mapping. */
169 else 178 kvmppc_44x_tlbie(stlb_index);
170 kvm_release_page_clean(page);
171 }
172}
173 179
174void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu) 180 /* Now release the page. */
175{ 181 if (ref->writeable)
176 int i; 182 kvm_release_page_dirty(ref->page);
183 else
184 kvm_release_page_clean(ref->page);
177 185
178 for (i = 0; i <= tlb_44x_hwater; i++) 186 ref->page = NULL;
179 kvmppc_44x_shadow_release(vcpu, i); 187
188 /* XXX set tlb_44x_index to stlb_index? */
189
190 KVMTRACE_1D(STLB_INVAL, &vcpu_44x->vcpu, stlb_index, handler);
180} 191}
181 192
182void kvmppc_tlbe_set_modified(struct kvm_vcpu *vcpu, unsigned int i) 193void kvmppc_core_destroy_mmu(struct kvm_vcpu *vcpu)
183{ 194{
184 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu); 195 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
196 int i;
185 197
186 vcpu_44x->shadow_tlb_mod[i] = 1; 198 for (i = 0; i <= tlb_44x_hwater; i++)
199 kvmppc_44x_shadow_release(vcpu_44x, i);
187} 200}
188 201
189/** 202/**
@@ -199,21 +212,24 @@ void kvmppc_tlbe_set_modified(struct kvm_vcpu *vcpu, unsigned int i)
199 * the shadow TLB. 212 * the shadow TLB.
200 */ 213 */
201void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr, u64 asid, 214void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr, u64 asid,
202 u32 flags, u32 max_bytes) 215 u32 flags, u32 max_bytes, unsigned int gtlb_index)
203{ 216{
217 struct kvmppc_44x_tlbe stlbe;
204 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu); 218 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
219 struct kvmppc_44x_shadow_ref *ref;
205 struct page *new_page; 220 struct page *new_page;
206 struct kvmppc_44x_tlbe *stlbe;
207 hpa_t hpaddr; 221 hpa_t hpaddr;
208 gfn_t gfn; 222 gfn_t gfn;
209 unsigned int victim; 223 unsigned int victim;
210 224
211 /* Future optimization: don't overwrite the TLB entry containing the 225 /* Select TLB entry to clobber. Indirectly guard against races with the TLB
212 * current PC (or stack?). */ 226 * miss handler by disabling interrupts. */
213 victim = kvmppc_tlb_44x_pos++; 227 local_irq_disable();
214 if (kvmppc_tlb_44x_pos > tlb_44x_hwater) 228 victim = ++tlb_44x_index;
215 kvmppc_tlb_44x_pos = 0; 229 if (victim > tlb_44x_hwater)
216 stlbe = &vcpu_44x->shadow_tlb[victim]; 230 victim = 0;
231 tlb_44x_index = victim;
232 local_irq_enable();
217 233
218 /* Get reference to new page. */ 234 /* Get reference to new page. */
219 gfn = gpaddr >> PAGE_SHIFT; 235 gfn = gpaddr >> PAGE_SHIFT;
@@ -225,10 +241,8 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr, u64 asid,
225 } 241 }
226 hpaddr = page_to_phys(new_page); 242 hpaddr = page_to_phys(new_page);
227 243
228 /* Drop reference to old page. */ 244 /* Invalidate any previous shadow mappings. */
229 kvmppc_44x_shadow_release(vcpu, victim); 245 kvmppc_44x_shadow_release(vcpu_44x, victim);
230
231 vcpu_44x->shadow_pages[victim] = new_page;
232 246
233 /* XXX Make sure (va, size) doesn't overlap any other 247 /* XXX Make sure (va, size) doesn't overlap any other
234 * entries. 440x6 user manual says the result would be 248 * entries. 440x6 user manual says the result would be
@@ -236,21 +250,19 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr, u64 asid,
236 250
237 /* XXX what about AS? */ 251 /* XXX what about AS? */
238 252
239 stlbe->tid = !(asid & 0xff);
240
241 /* Force TS=1 for all guest mappings. */ 253 /* Force TS=1 for all guest mappings. */
242 stlbe->word0 = PPC44x_TLB_VALID | PPC44x_TLB_TS; 254 stlbe.word0 = PPC44x_TLB_VALID | PPC44x_TLB_TS;
243 255
244 if (max_bytes >= PAGE_SIZE) { 256 if (max_bytes >= PAGE_SIZE) {
245 /* Guest mapping is larger than or equal to host page size. We can use 257 /* Guest mapping is larger than or equal to host page size. We can use
246 * a "native" host mapping. */ 258 * a "native" host mapping. */
247 stlbe->word0 |= (gvaddr & PAGE_MASK) | PPC44x_TLBE_SIZE; 259 stlbe.word0 |= (gvaddr & PAGE_MASK) | PPC44x_TLBE_SIZE;
248 } else { 260 } else {
249 /* Guest mapping is smaller than host page size. We must restrict the 261 /* Guest mapping is smaller than host page size. We must restrict the
250 * size of the mapping to be at most the smaller of the two, but for 262 * size of the mapping to be at most the smaller of the two, but for
251 * simplicity we fall back to a 4K mapping (this is probably what the 263 * simplicity we fall back to a 4K mapping (this is probably what the
252 * guest is using anyways). */ 264 * guest is using anyways). */
253 stlbe->word0 |= (gvaddr & PAGE_MASK_4K) | PPC44x_TLB_4K; 265 stlbe.word0 |= (gvaddr & PAGE_MASK_4K) | PPC44x_TLB_4K;
254 266
255 /* 'hpaddr' is a host page, which is larger than the mapping we're 267 /* 'hpaddr' is a host page, which is larger than the mapping we're
256 * inserting here. To compensate, we must add the in-page offset to the 268 * inserting here. To compensate, we must add the in-page offset to the
@@ -258,47 +270,36 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gpa_t gpaddr, u64 asid,
258 hpaddr |= gpaddr & (PAGE_MASK ^ PAGE_MASK_4K); 270 hpaddr |= gpaddr & (PAGE_MASK ^ PAGE_MASK_4K);
259 } 271 }
260 272
261 stlbe->word1 = (hpaddr & 0xfffffc00) | ((hpaddr >> 32) & 0xf); 273 stlbe.word1 = (hpaddr & 0xfffffc00) | ((hpaddr >> 32) & 0xf);
262 stlbe->word2 = kvmppc_44x_tlb_shadow_attrib(flags, 274 stlbe.word2 = kvmppc_44x_tlb_shadow_attrib(flags,
263 vcpu->arch.msr & MSR_PR); 275 vcpu->arch.msr & MSR_PR);
264 kvmppc_tlbe_set_modified(vcpu, victim); 276 stlbe.tid = !(asid & 0xff);
265 277
266 KVMTRACE_5D(STLB_WRITE, vcpu, victim, 278 /* Keep track of the reference so we can properly release it later. */
267 stlbe->tid, stlbe->word0, stlbe->word1, stlbe->word2, 279 ref = &vcpu_44x->shadow_refs[victim];
268 handler); 280 ref->page = new_page;
281 ref->gtlb_index = gtlb_index;
282 ref->writeable = !!(stlbe.word2 & PPC44x_TLB_UW);
283 ref->tid = stlbe.tid;
284
285 /* Insert shadow mapping into hardware TLB. */
286 kvmppc_44x_tlbwe(victim, &stlbe);
287 KVMTRACE_5D(STLB_WRITE, vcpu, victim, stlbe.tid, stlbe.word0, stlbe.word1,
288 stlbe.word2, handler);
269} 289}
270 290
271static void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr, 291/* For a particular guest TLB entry, invalidate the corresponding host TLB
272 gva_t eend, u32 asid) 292 * mappings and release the host pages. */
293static void kvmppc_44x_invalidate(struct kvm_vcpu *vcpu,
294 unsigned int gtlb_index)
273{ 295{
274 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu); 296 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
275 unsigned int pid = !(asid & 0xff);
276 int i; 297 int i;
277 298
278 /* XXX Replace loop with fancy data structures. */ 299 for (i = 0; i < ARRAY_SIZE(vcpu_44x->shadow_refs); i++) {
279 for (i = 0; i <= tlb_44x_hwater; i++) { 300 struct kvmppc_44x_shadow_ref *ref = &vcpu_44x->shadow_refs[i];
280 struct kvmppc_44x_tlbe *stlbe = &vcpu_44x->shadow_tlb[i]; 301 if (ref->gtlb_index == gtlb_index)
281 unsigned int tid; 302 kvmppc_44x_shadow_release(vcpu_44x, i);
282
283 if (!get_tlb_v(stlbe))
284 continue;
285
286 if (eend < get_tlb_eaddr(stlbe))
287 continue;
288
289 if (eaddr > get_tlb_end(stlbe))
290 continue;
291
292 tid = get_tlb_tid(stlbe);
293 if (tid && (tid != pid))
294 continue;
295
296 kvmppc_44x_shadow_release(vcpu, i);
297 stlbe->word0 = 0;
298 kvmppc_tlbe_set_modified(vcpu, i);
299 KVMTRACE_5D(STLB_INVAL, vcpu, i,
300 stlbe->tid, stlbe->word0, stlbe->word1,
301 stlbe->word2, handler);
302 } 303 }
303} 304}
304 305
@@ -321,14 +322,11 @@ void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 new_pid)
321 * can't access guest kernel mappings (TID=1). When we switch to a new 322 * can't access guest kernel mappings (TID=1). When we switch to a new
322 * guest PID, which will also use host PID=0, we must discard the old guest 323 * guest PID, which will also use host PID=0, we must discard the old guest
323 * userspace mappings. */ 324 * userspace mappings. */
324 for (i = 0; i < ARRAY_SIZE(vcpu_44x->shadow_tlb); i++) { 325 for (i = 0; i < ARRAY_SIZE(vcpu_44x->shadow_refs); i++) {
325 struct kvmppc_44x_tlbe *stlbe = &vcpu_44x->shadow_tlb[i]; 326 struct kvmppc_44x_shadow_ref *ref = &vcpu_44x->shadow_refs[i];
326 327
327 if (get_tlb_tid(stlbe) == 0) { 328 if (ref->tid == 0)
328 kvmppc_44x_shadow_release(vcpu, i); 329 kvmppc_44x_shadow_release(vcpu_44x, i);
329 stlbe->word0 = 0;
330 kvmppc_tlbe_set_modified(vcpu, i);
331 }
332 } 330 }
333} 331}
334 332
@@ -356,26 +354,21 @@ static int tlbe_is_host_safe(const struct kvm_vcpu *vcpu,
356int kvmppc_44x_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws) 354int kvmppc_44x_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws)
357{ 355{
358 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu); 356 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
359 gva_t eaddr;
360 u64 asid;
361 struct kvmppc_44x_tlbe *tlbe; 357 struct kvmppc_44x_tlbe *tlbe;
362 unsigned int index; 358 unsigned int gtlb_index;
363 359
364 index = vcpu->arch.gpr[ra]; 360 gtlb_index = vcpu->arch.gpr[ra];
365 if (index > PPC44x_TLB_SIZE) { 361 if (gtlb_index > KVM44x_GUEST_TLB_SIZE) {
366 printk("%s: index %d\n", __func__, index); 362 printk("%s: index %d\n", __func__, gtlb_index);
367 kvmppc_dump_vcpu(vcpu); 363 kvmppc_dump_vcpu(vcpu);
368 return EMULATE_FAIL; 364 return EMULATE_FAIL;
369 } 365 }
370 366
371 tlbe = &vcpu_44x->guest_tlb[index]; 367 tlbe = &vcpu_44x->guest_tlb[gtlb_index];
372 368
373 /* Invalidate shadow mappings for the about-to-be-clobbered TLBE. */ 369 /* Invalidate shadow mappings for the about-to-be-clobbered TLB entry. */
374 if (tlbe->word0 & PPC44x_TLB_VALID) { 370 if (tlbe->word0 & PPC44x_TLB_VALID)
375 eaddr = get_tlb_eaddr(tlbe); 371 kvmppc_44x_invalidate(vcpu, gtlb_index);
376 asid = (tlbe->word0 & PPC44x_TLB_TS) | tlbe->tid;
377 kvmppc_mmu_invalidate(vcpu, eaddr, get_tlb_end(tlbe), asid);
378 }
379 372
380 switch (ws) { 373 switch (ws) {
381 case PPC44x_TLB_PAGEID: 374 case PPC44x_TLB_PAGEID:
@@ -396,6 +389,8 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws)
396 } 389 }
397 390
398 if (tlbe_is_host_safe(vcpu, tlbe)) { 391 if (tlbe_is_host_safe(vcpu, tlbe)) {
392 u64 asid;
393 gva_t eaddr;
399 gpa_t gpaddr; 394 gpa_t gpaddr;
400 u32 flags; 395 u32 flags;
401 u32 bytes; 396 u32 bytes;
@@ -411,12 +406,11 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws)
411 asid = (tlbe->word0 & PPC44x_TLB_TS) | tlbe->tid; 406 asid = (tlbe->word0 & PPC44x_TLB_TS) | tlbe->tid;
412 flags = tlbe->word2 & 0xffff; 407 flags = tlbe->word2 & 0xffff;
413 408
414 kvmppc_mmu_map(vcpu, eaddr, gpaddr, asid, flags, bytes); 409 kvmppc_mmu_map(vcpu, eaddr, gpaddr, asid, flags, bytes, gtlb_index);
415 } 410 }
416 411
417 KVMTRACE_5D(GTLB_WRITE, vcpu, index, 412 KVMTRACE_5D(GTLB_WRITE, vcpu, gtlb_index, tlbe->tid, tlbe->word0,
418 tlbe->tid, tlbe->word0, tlbe->word1, tlbe->word2, 413 tlbe->word1, tlbe->word2, handler);
419 handler);
420 414
421 return EMULATE_DONE; 415 return EMULATE_DONE;
422} 416}
@@ -424,7 +418,7 @@ int kvmppc_44x_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws)
424int kvmppc_44x_emul_tlbsx(struct kvm_vcpu *vcpu, u8 rt, u8 ra, u8 rb, u8 rc) 418int kvmppc_44x_emul_tlbsx(struct kvm_vcpu *vcpu, u8 rt, u8 ra, u8 rb, u8 rc)
425{ 419{
426 u32 ea; 420 u32 ea;
427 int index; 421 int gtlb_index;
428 unsigned int as = get_mmucr_sts(vcpu); 422 unsigned int as = get_mmucr_sts(vcpu);
429 unsigned int pid = get_mmucr_stid(vcpu); 423 unsigned int pid = get_mmucr_stid(vcpu);
430 424
@@ -432,14 +426,14 @@ int kvmppc_44x_emul_tlbsx(struct kvm_vcpu *vcpu, u8 rt, u8 ra, u8 rb, u8 rc)
432 if (ra) 426 if (ra)
433 ea += vcpu->arch.gpr[ra]; 427 ea += vcpu->arch.gpr[ra];
434 428
435 index = kvmppc_44x_tlb_index(vcpu, ea, pid, as); 429 gtlb_index = kvmppc_44x_tlb_index(vcpu, ea, pid, as);
436 if (rc) { 430 if (rc) {
437 if (index < 0) 431 if (gtlb_index < 0)
438 vcpu->arch.cr &= ~0x20000000; 432 vcpu->arch.cr &= ~0x20000000;
439 else 433 else
440 vcpu->arch.cr |= 0x20000000; 434 vcpu->arch.cr |= 0x20000000;
441 } 435 }
442 vcpu->arch.gpr[rt] = index; 436 vcpu->arch.gpr[rt] = gtlb_index;
443 437
444 return EMULATE_DONE; 438 return EMULATE_DONE;
445} 439}
diff --git a/arch/powerpc/kvm/44x_tlb.h b/arch/powerpc/kvm/44x_tlb.h
index b1029af3de20..772191f29e62 100644
--- a/arch/powerpc/kvm/44x_tlb.h
+++ b/arch/powerpc/kvm/44x_tlb.h
@@ -25,11 +25,8 @@
25 25
26extern int kvmppc_44x_tlb_index(struct kvm_vcpu *vcpu, gva_t eaddr, 26extern int kvmppc_44x_tlb_index(struct kvm_vcpu *vcpu, gva_t eaddr,
27 unsigned int pid, unsigned int as); 27 unsigned int pid, unsigned int as);
28extern struct kvmppc_44x_tlbe *kvmppc_44x_dtlb_search(struct kvm_vcpu *vcpu, 28extern int kvmppc_44x_dtlb_index(struct kvm_vcpu *vcpu, gva_t eaddr);
29 gva_t eaddr); 29extern int kvmppc_44x_itlb_index(struct kvm_vcpu *vcpu, gva_t eaddr);
30extern struct kvmppc_44x_tlbe *kvmppc_44x_itlb_search(struct kvm_vcpu *vcpu,
31 gva_t eaddr);
32extern void kvmppc_tlbe_set_modified(struct kvm_vcpu *vcpu, unsigned int i);
33 30
34extern int kvmppc_44x_emul_tlbsx(struct kvm_vcpu *vcpu, u8 rt, u8 ra, u8 rb, 31extern int kvmppc_44x_emul_tlbsx(struct kvm_vcpu *vcpu, u8 rt, u8 ra, u8 rb,
35 u8 rc); 32 u8 rc);
diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c
index 924c7b4b1107..eb24383c87d2 100644
--- a/arch/powerpc/kvm/booke.c
+++ b/arch/powerpc/kvm/booke.c
@@ -24,10 +24,12 @@
24#include <linux/module.h> 24#include <linux/module.h>
25#include <linux/vmalloc.h> 25#include <linux/vmalloc.h>
26#include <linux/fs.h> 26#include <linux/fs.h>
27
27#include <asm/cputable.h> 28#include <asm/cputable.h>
28#include <asm/uaccess.h> 29#include <asm/uaccess.h>
29#include <asm/kvm_ppc.h> 30#include <asm/kvm_ppc.h>
30#include <asm/cacheflush.h> 31#include <asm/cacheflush.h>
32#include <asm/kvm_44x.h>
31 33
32#include "booke.h" 34#include "booke.h"
33#include "44x_tlb.h" 35#include "44x_tlb.h"
@@ -207,10 +209,6 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
207 * handled this interrupt the moment we enabled interrupts. 209 * handled this interrupt the moment we enabled interrupts.
208 * Now we just offer it a chance to reschedule the guest. */ 210 * Now we just offer it a chance to reschedule the guest. */
209 211
210 /* XXX At this point the TLB still holds our shadow TLB, so if
211 * we do reschedule the host will fault over it. Perhaps we
212 * should politely restore the host's entries to minimize
213 * misses before ceding control. */
214 vcpu->stat.dec_exits++; 212 vcpu->stat.dec_exits++;
215 if (need_resched()) 213 if (need_resched())
216 cond_resched(); 214 cond_resched();
@@ -281,14 +279,17 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
281 r = RESUME_GUEST; 279 r = RESUME_GUEST;
282 break; 280 break;
283 281
282 /* XXX move to a 440-specific file. */
284 case BOOKE_INTERRUPT_DTLB_MISS: { 283 case BOOKE_INTERRUPT_DTLB_MISS: {
284 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
285 struct kvmppc_44x_tlbe *gtlbe; 285 struct kvmppc_44x_tlbe *gtlbe;
286 unsigned long eaddr = vcpu->arch.fault_dear; 286 unsigned long eaddr = vcpu->arch.fault_dear;
287 int gtlb_index;
287 gfn_t gfn; 288 gfn_t gfn;
288 289
289 /* Check the guest TLB. */ 290 /* Check the guest TLB. */
290 gtlbe = kvmppc_44x_dtlb_search(vcpu, eaddr); 291 gtlb_index = kvmppc_44x_dtlb_index(vcpu, eaddr);
291 if (!gtlbe) { 292 if (gtlb_index < 0) {
292 /* The guest didn't have a mapping for it. */ 293 /* The guest didn't have a mapping for it. */
293 kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS); 294 kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_DTLB_MISS);
294 vcpu->arch.dear = vcpu->arch.fault_dear; 295 vcpu->arch.dear = vcpu->arch.fault_dear;
@@ -298,6 +299,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
298 break; 299 break;
299 } 300 }
300 301
302 gtlbe = &vcpu_44x->guest_tlb[gtlb_index];
301 vcpu->arch.paddr_accessed = tlb_xlate(gtlbe, eaddr); 303 vcpu->arch.paddr_accessed = tlb_xlate(gtlbe, eaddr);
302 gfn = vcpu->arch.paddr_accessed >> PAGE_SHIFT; 304 gfn = vcpu->arch.paddr_accessed >> PAGE_SHIFT;
303 305
@@ -309,7 +311,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
309 * Either way, we need to satisfy the fault without 311 * Either way, we need to satisfy the fault without
310 * invoking the guest. */ 312 * invoking the guest. */
311 kvmppc_mmu_map(vcpu, eaddr, vcpu->arch.paddr_accessed, gtlbe->tid, 313 kvmppc_mmu_map(vcpu, eaddr, vcpu->arch.paddr_accessed, gtlbe->tid,
312 gtlbe->word2, get_tlb_bytes(gtlbe)); 314 gtlbe->word2, get_tlb_bytes(gtlbe), gtlb_index);
313 vcpu->stat.dtlb_virt_miss_exits++; 315 vcpu->stat.dtlb_virt_miss_exits++;
314 r = RESUME_GUEST; 316 r = RESUME_GUEST;
315 } else { 317 } else {
@@ -322,17 +324,20 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
322 break; 324 break;
323 } 325 }
324 326
327 /* XXX move to a 440-specific file. */
325 case BOOKE_INTERRUPT_ITLB_MISS: { 328 case BOOKE_INTERRUPT_ITLB_MISS: {
329 struct kvmppc_vcpu_44x *vcpu_44x = to_44x(vcpu);
326 struct kvmppc_44x_tlbe *gtlbe; 330 struct kvmppc_44x_tlbe *gtlbe;
327 unsigned long eaddr = vcpu->arch.pc; 331 unsigned long eaddr = vcpu->arch.pc;
328 gpa_t gpaddr; 332 gpa_t gpaddr;
329 gfn_t gfn; 333 gfn_t gfn;
334 int gtlb_index;
330 335
331 r = RESUME_GUEST; 336 r = RESUME_GUEST;
332 337
333 /* Check the guest TLB. */ 338 /* Check the guest TLB. */
334 gtlbe = kvmppc_44x_itlb_search(vcpu, eaddr); 339 gtlb_index = kvmppc_44x_itlb_index(vcpu, eaddr);
335 if (!gtlbe) { 340 if (gtlb_index < 0) {
336 /* The guest didn't have a mapping for it. */ 341 /* The guest didn't have a mapping for it. */
337 kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ITLB_MISS); 342 kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_ITLB_MISS);
338 vcpu->stat.itlb_real_miss_exits++; 343 vcpu->stat.itlb_real_miss_exits++;
@@ -341,6 +346,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
341 346
342 vcpu->stat.itlb_virt_miss_exits++; 347 vcpu->stat.itlb_virt_miss_exits++;
343 348
349 gtlbe = &vcpu_44x->guest_tlb[gtlb_index];
344 gpaddr = tlb_xlate(gtlbe, eaddr); 350 gpaddr = tlb_xlate(gtlbe, eaddr);
345 gfn = gpaddr >> PAGE_SHIFT; 351 gfn = gpaddr >> PAGE_SHIFT;
346 352
@@ -352,7 +358,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu,
352 * Either way, we need to satisfy the fault without 358 * Either way, we need to satisfy the fault without
353 * invoking the guest. */ 359 * invoking the guest. */
354 kvmppc_mmu_map(vcpu, eaddr, gpaddr, gtlbe->tid, 360 kvmppc_mmu_map(vcpu, eaddr, gpaddr, gtlbe->tid,
355 gtlbe->word2, get_tlb_bytes(gtlbe)); 361 gtlbe->word2, get_tlb_bytes(gtlbe), gtlb_index);
356 } else { 362 } else {
357 /* Guest mapped and leaped at non-RAM! */ 363 /* Guest mapped and leaped at non-RAM! */
358 kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_MACHINE_CHECK); 364 kvmppc_booke_queue_irqprio(vcpu, BOOKE_IRQPRIO_MACHINE_CHECK);
diff --git a/arch/powerpc/kvm/booke_interrupts.S b/arch/powerpc/kvm/booke_interrupts.S
index 8d6929b7fdb6..eb2186823e4e 100644
--- a/arch/powerpc/kvm/booke_interrupts.S
+++ b/arch/powerpc/kvm/booke_interrupts.S
@@ -335,54 +335,6 @@ lightweight_exit:
335 lwz r3, VCPU_SHADOW_PID(r4) 335 lwz r3, VCPU_SHADOW_PID(r4)
336 mtspr SPRN_PID, r3 336 mtspr SPRN_PID, r3
337 337
338 /* Prevent all asynchronous TLB updates. */
339 mfmsr r5
340 lis r6, (MSR_EE|MSR_CE|MSR_ME|MSR_DE)@h
341 ori r6, r6, (MSR_EE|MSR_CE|MSR_ME|MSR_DE)@l
342 andc r6, r5, r6
343 mtmsr r6
344
345 /* Load the guest mappings, leaving the host's "pinned" kernel mappings
346 * in place. */
347 mfspr r10, SPRN_MMUCR /* Save host MMUCR. */
348 li r5, PPC44x_TLB_SIZE
349 lis r5, tlb_44x_hwater@ha
350 lwz r5, tlb_44x_hwater@l(r5)
351 mtctr r5
352 addi r9, r4, -VCPU_TO_44X + VCPU44x_SHADOW_TLB
353 addi r5, r4, -VCPU_TO_44X + VCPU44x_SHADOW_MOD
354 li r3, 0
3551:
356 lbzx r7, r3, r5
357 cmpwi r7, 0
358 beq 3f
359
360 /* Load guest entry. */
361 mulli r11, r3, TLBE_BYTES
362 add r11, r11, r9
363 lwz r7, 0(r11)
364 mtspr SPRN_MMUCR, r7
365 lwz r7, 4(r11)
366 tlbwe r7, r3, PPC44x_TLB_PAGEID
367 lwz r7, 8(r11)
368 tlbwe r7, r3, PPC44x_TLB_XLAT
369 lwz r7, 12(r11)
370 tlbwe r7, r3, PPC44x_TLB_ATTRIB
3713:
372 addi r3, r3, 1 /* Increment index. */
373 bdnz 1b
374
375 mtspr SPRN_MMUCR, r10 /* Restore host MMUCR. */
376
377 /* Clear bitmap of modified TLB entries */
378 li r5, PPC44x_TLB_SIZE>>2
379 mtctr r5
380 addi r5, r4, -VCPU_TO_44X + VCPU44x_SHADOW_MOD - 4
381 li r6, 0
3821:
383 stwu r6, 4(r5)
384 bdnz 1b
385
386 iccci 0, 0 /* XXX hack */ 338 iccci 0, 0 /* XXX hack */
387 339
388 /* Load some guest volatiles. */ 340 /* Load some guest volatiles. */