diff options
Diffstat (limited to 'arch/powerpc')
-rw-r--r-- | arch/powerpc/include/asm/kvm_ppc.h | 4 | ||||
-rw-r--r-- | arch/powerpc/kvm/44x_tlb.c | 138 | ||||
-rw-r--r-- | arch/powerpc/kvm/booke_guest.c | 26 | ||||
-rw-r--r-- | arch/powerpc/kvm/emulate.c | 120 |
4 files changed, 145 insertions, 143 deletions
diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index bb62ad876de3..4adb4a397508 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h | |||
@@ -58,11 +58,11 @@ extern int kvmppc_handle_store(struct kvm_run *run, struct kvm_vcpu *vcpu, | |||
58 | extern int kvmppc_emulate_instruction(struct kvm_run *run, | 58 | extern int kvmppc_emulate_instruction(struct kvm_run *run, |
59 | struct kvm_vcpu *vcpu); | 59 | struct kvm_vcpu *vcpu); |
60 | extern int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu); | 60 | extern int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu); |
61 | extern int kvmppc_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws); | ||
62 | extern int kvmppc_emul_tlbsx(struct kvm_vcpu *vcpu, u8 rt, u8 ra, u8 rb, u8 rc); | ||
61 | 63 | ||
62 | extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, | 64 | extern void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, |
63 | u64 asid, u32 flags); | 65 | u64 asid, u32 flags); |
64 | extern void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr, | ||
65 | gva_t eend, u32 asid); | ||
66 | extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode); | 66 | extern void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode); |
67 | extern void kvmppc_mmu_switch_pid(struct kvm_vcpu *vcpu, u32 pid); | 67 | extern void kvmppc_mmu_switch_pid(struct kvm_vcpu *vcpu, u32 pid); |
68 | 68 | ||
diff --git a/arch/powerpc/kvm/44x_tlb.c b/arch/powerpc/kvm/44x_tlb.c index ad72c6f9811f..dd75ab84e04e 100644 --- a/arch/powerpc/kvm/44x_tlb.c +++ b/arch/powerpc/kvm/44x_tlb.c | |||
@@ -32,6 +32,34 @@ | |||
32 | 32 | ||
33 | static unsigned int kvmppc_tlb_44x_pos; | 33 | static unsigned int kvmppc_tlb_44x_pos; |
34 | 34 | ||
35 | #ifdef DEBUG | ||
36 | void kvmppc_dump_tlbs(struct kvm_vcpu *vcpu) | ||
37 | { | ||
38 | struct kvmppc_44x_tlbe *tlbe; | ||
39 | int i; | ||
40 | |||
41 | printk("vcpu %d TLB dump:\n", vcpu->vcpu_id); | ||
42 | printk("| %2s | %3s | %8s | %8s | %8s |\n", | ||
43 | "nr", "tid", "word0", "word1", "word2"); | ||
44 | |||
45 | for (i = 0; i < PPC44x_TLB_SIZE; i++) { | ||
46 | tlbe = &vcpu->arch.guest_tlb[i]; | ||
47 | if (tlbe->word0 & PPC44x_TLB_VALID) | ||
48 | printk(" G%2d | %02X | %08X | %08X | %08X |\n", | ||
49 | i, tlbe->tid, tlbe->word0, tlbe->word1, | ||
50 | tlbe->word2); | ||
51 | } | ||
52 | |||
53 | for (i = 0; i < PPC44x_TLB_SIZE; i++) { | ||
54 | tlbe = &vcpu->arch.shadow_tlb[i]; | ||
55 | if (tlbe->word0 & PPC44x_TLB_VALID) | ||
56 | printk(" S%2d | %02X | %08X | %08X | %08X |\n", | ||
57 | i, tlbe->tid, tlbe->word0, tlbe->word1, | ||
58 | tlbe->word2); | ||
59 | } | ||
60 | } | ||
61 | #endif | ||
62 | |||
35 | static u32 kvmppc_44x_tlb_shadow_attrib(u32 attrib, int usermode) | 63 | static u32 kvmppc_44x_tlb_shadow_attrib(u32 attrib, int usermode) |
36 | { | 64 | { |
37 | /* Mask off reserved bits. */ | 65 | /* Mask off reserved bits. */ |
@@ -191,8 +219,8 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 gvaddr, gfn_t gfn, u64 asid, | |||
191 | handler); | 219 | handler); |
192 | } | 220 | } |
193 | 221 | ||
194 | void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr, | 222 | static void kvmppc_mmu_invalidate(struct kvm_vcpu *vcpu, gva_t eaddr, |
195 | gva_t eend, u32 asid) | 223 | gva_t eend, u32 asid) |
196 | { | 224 | { |
197 | unsigned int pid = !(asid & 0xff); | 225 | unsigned int pid = !(asid & 0xff); |
198 | int i; | 226 | int i; |
@@ -249,3 +277,109 @@ void kvmppc_mmu_priv_switch(struct kvm_vcpu *vcpu, int usermode) | |||
249 | 277 | ||
250 | vcpu->arch.shadow_pid = !usermode; | 278 | vcpu->arch.shadow_pid = !usermode; |
251 | } | 279 | } |
280 | |||
281 | static int tlbe_is_host_safe(const struct kvm_vcpu *vcpu, | ||
282 | const struct tlbe *tlbe) | ||
283 | { | ||
284 | gpa_t gpa; | ||
285 | |||
286 | if (!get_tlb_v(tlbe)) | ||
287 | return 0; | ||
288 | |||
289 | /* Does it match current guest AS? */ | ||
290 | /* XXX what about IS != DS? */ | ||
291 | if (get_tlb_ts(tlbe) != !!(vcpu->arch.msr & MSR_IS)) | ||
292 | return 0; | ||
293 | |||
294 | gpa = get_tlb_raddr(tlbe); | ||
295 | if (!gfn_to_memslot(vcpu->kvm, gpa >> PAGE_SHIFT)) | ||
296 | /* Mapping is not for RAM. */ | ||
297 | return 0; | ||
298 | |||
299 | return 1; | ||
300 | } | ||
301 | |||
302 | int kvmppc_emul_tlbwe(struct kvm_vcpu *vcpu, u8 ra, u8 rs, u8 ws) | ||
303 | { | ||
304 | u64 eaddr; | ||
305 | u64 raddr; | ||
306 | u64 asid; | ||
307 | u32 flags; | ||
308 | struct tlbe *tlbe; | ||
309 | unsigned int index; | ||
310 | |||
311 | index = vcpu->arch.gpr[ra]; | ||
312 | if (index > PPC44x_TLB_SIZE) { | ||
313 | printk("%s: index %d\n", __func__, index); | ||
314 | kvmppc_dump_vcpu(vcpu); | ||
315 | return EMULATE_FAIL; | ||
316 | } | ||
317 | |||
318 | tlbe = &vcpu->arch.guest_tlb[index]; | ||
319 | |||
320 | /* Invalidate shadow mappings for the about-to-be-clobbered TLBE. */ | ||
321 | if (tlbe->word0 & PPC44x_TLB_VALID) { | ||
322 | eaddr = get_tlb_eaddr(tlbe); | ||
323 | asid = (tlbe->word0 & PPC44x_TLB_TS) | tlbe->tid; | ||
324 | kvmppc_mmu_invalidate(vcpu, eaddr, get_tlb_end(tlbe), asid); | ||
325 | } | ||
326 | |||
327 | switch (ws) { | ||
328 | case PPC44x_TLB_PAGEID: | ||
329 | tlbe->tid = vcpu->arch.mmucr & 0xff; | ||
330 | tlbe->word0 = vcpu->arch.gpr[rs]; | ||
331 | break; | ||
332 | |||
333 | case PPC44x_TLB_XLAT: | ||
334 | tlbe->word1 = vcpu->arch.gpr[rs]; | ||
335 | break; | ||
336 | |||
337 | case PPC44x_TLB_ATTRIB: | ||
338 | tlbe->word2 = vcpu->arch.gpr[rs]; | ||
339 | break; | ||
340 | |||
341 | default: | ||
342 | return EMULATE_FAIL; | ||
343 | } | ||
344 | |||
345 | if (tlbe_is_host_safe(vcpu, tlbe)) { | ||
346 | eaddr = get_tlb_eaddr(tlbe); | ||
347 | raddr = get_tlb_raddr(tlbe); | ||
348 | asid = (tlbe->word0 & PPC44x_TLB_TS) | tlbe->tid; | ||
349 | flags = tlbe->word2 & 0xffff; | ||
350 | |||
351 | /* Create a 4KB mapping on the host. If the guest wanted a | ||
352 | * large page, only the first 4KB is mapped here and the rest | ||
353 | * are mapped on the fly. */ | ||
354 | kvmppc_mmu_map(vcpu, eaddr, raddr >> PAGE_SHIFT, asid, flags); | ||
355 | } | ||
356 | |||
357 | KVMTRACE_5D(GTLB_WRITE, vcpu, index, | ||
358 | tlbe->tid, tlbe->word0, tlbe->word1, tlbe->word2, | ||
359 | handler); | ||
360 | |||
361 | return EMULATE_DONE; | ||
362 | } | ||
363 | |||
364 | int kvmppc_emul_tlbsx(struct kvm_vcpu *vcpu, u8 rt, u8 ra, u8 rb, u8 rc) | ||
365 | { | ||
366 | u32 ea; | ||
367 | int index; | ||
368 | unsigned int as = get_mmucr_sts(vcpu); | ||
369 | unsigned int pid = get_mmucr_stid(vcpu); | ||
370 | |||
371 | ea = vcpu->arch.gpr[rb]; | ||
372 | if (ra) | ||
373 | ea += vcpu->arch.gpr[ra]; | ||
374 | |||
375 | index = kvmppc_44x_tlb_index(vcpu, ea, pid, as); | ||
376 | if (rc) { | ||
377 | if (index < 0) | ||
378 | vcpu->arch.cr &= ~0x20000000; | ||
379 | else | ||
380 | vcpu->arch.cr |= 0x20000000; | ||
381 | } | ||
382 | vcpu->arch.gpr[rt] = index; | ||
383 | |||
384 | return EMULATE_DONE; | ||
385 | } | ||
diff --git a/arch/powerpc/kvm/booke_guest.c b/arch/powerpc/kvm/booke_guest.c index 7b2591e26bae..c0f8532630ec 100644 --- a/arch/powerpc/kvm/booke_guest.c +++ b/arch/powerpc/kvm/booke_guest.c | |||
@@ -111,32 +111,6 @@ const unsigned char priority_exception[] = { | |||
111 | }; | 111 | }; |
112 | 112 | ||
113 | 113 | ||
114 | void kvmppc_dump_tlbs(struct kvm_vcpu *vcpu) | ||
115 | { | ||
116 | struct tlbe *tlbe; | ||
117 | int i; | ||
118 | |||
119 | printk("vcpu %d TLB dump:\n", vcpu->vcpu_id); | ||
120 | printk("| %2s | %3s | %8s | %8s | %8s |\n", | ||
121 | "nr", "tid", "word0", "word1", "word2"); | ||
122 | |||
123 | for (i = 0; i < PPC44x_TLB_SIZE; i++) { | ||
124 | tlbe = &vcpu->arch.guest_tlb[i]; | ||
125 | if (tlbe->word0 & PPC44x_TLB_VALID) | ||
126 | printk(" G%2d | %02X | %08X | %08X | %08X |\n", | ||
127 | i, tlbe->tid, tlbe->word0, tlbe->word1, | ||
128 | tlbe->word2); | ||
129 | } | ||
130 | |||
131 | for (i = 0; i < PPC44x_TLB_SIZE; i++) { | ||
132 | tlbe = &vcpu->arch.shadow_tlb[i]; | ||
133 | if (tlbe->word0 & PPC44x_TLB_VALID) | ||
134 | printk(" S%2d | %02X | %08X | %08X | %08X |\n", | ||
135 | i, tlbe->tid, tlbe->word0, tlbe->word1, | ||
136 | tlbe->word2); | ||
137 | } | ||
138 | } | ||
139 | |||
140 | /* TODO: use vcpu_printf() */ | 114 | /* TODO: use vcpu_printf() */ |
141 | void kvmppc_dump_vcpu(struct kvm_vcpu *vcpu) | 115 | void kvmppc_dump_vcpu(struct kvm_vcpu *vcpu) |
142 | { | 116 | { |
diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c index 0fce4fbdc20d..0ce8ed539bae 100644 --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c | |||
@@ -29,8 +29,6 @@ | |||
29 | #include <asm/byteorder.h> | 29 | #include <asm/byteorder.h> |
30 | #include <asm/kvm_ppc.h> | 30 | #include <asm/kvm_ppc.h> |
31 | 31 | ||
32 | #include "44x_tlb.h" | ||
33 | |||
34 | /* Instruction decoding */ | 32 | /* Instruction decoding */ |
35 | static inline unsigned int get_op(u32 inst) | 33 | static inline unsigned int get_op(u32 inst) |
36 | { | 34 | { |
@@ -87,96 +85,6 @@ static inline unsigned int get_d(u32 inst) | |||
87 | return inst & 0xffff; | 85 | return inst & 0xffff; |
88 | } | 86 | } |
89 | 87 | ||
90 | static int tlbe_is_host_safe(const struct kvm_vcpu *vcpu, | ||
91 | const struct tlbe *tlbe) | ||
92 | { | ||
93 | gpa_t gpa; | ||
94 | |||
95 | if (!get_tlb_v(tlbe)) | ||
96 | return 0; | ||
97 | |||
98 | /* Does it match current guest AS? */ | ||
99 | /* XXX what about IS != DS? */ | ||
100 | if (get_tlb_ts(tlbe) != !!(vcpu->arch.msr & MSR_IS)) | ||
101 | return 0; | ||
102 | |||
103 | gpa = get_tlb_raddr(tlbe); | ||
104 | if (!gfn_to_memslot(vcpu->kvm, gpa >> PAGE_SHIFT)) | ||
105 | /* Mapping is not for RAM. */ | ||
106 | return 0; | ||
107 | |||
108 | return 1; | ||
109 | } | ||
110 | |||
111 | static int kvmppc_emul_tlbwe(struct kvm_vcpu *vcpu, u32 inst) | ||
112 | { | ||
113 | u64 eaddr; | ||
114 | u64 raddr; | ||
115 | u64 asid; | ||
116 | u32 flags; | ||
117 | struct tlbe *tlbe; | ||
118 | unsigned int ra; | ||
119 | unsigned int rs; | ||
120 | unsigned int ws; | ||
121 | unsigned int index; | ||
122 | |||
123 | ra = get_ra(inst); | ||
124 | rs = get_rs(inst); | ||
125 | ws = get_ws(inst); | ||
126 | |||
127 | index = vcpu->arch.gpr[ra]; | ||
128 | if (index > PPC44x_TLB_SIZE) { | ||
129 | printk("%s: index %d\n", __func__, index); | ||
130 | kvmppc_dump_vcpu(vcpu); | ||
131 | return EMULATE_FAIL; | ||
132 | } | ||
133 | |||
134 | tlbe = &vcpu->arch.guest_tlb[index]; | ||
135 | |||
136 | /* Invalidate shadow mappings for the about-to-be-clobbered TLBE. */ | ||
137 | if (tlbe->word0 & PPC44x_TLB_VALID) { | ||
138 | eaddr = get_tlb_eaddr(tlbe); | ||
139 | asid = (tlbe->word0 & PPC44x_TLB_TS) | tlbe->tid; | ||
140 | kvmppc_mmu_invalidate(vcpu, eaddr, get_tlb_end(tlbe), asid); | ||
141 | } | ||
142 | |||
143 | switch (ws) { | ||
144 | case PPC44x_TLB_PAGEID: | ||
145 | tlbe->tid = vcpu->arch.mmucr & 0xff; | ||
146 | tlbe->word0 = vcpu->arch.gpr[rs]; | ||
147 | break; | ||
148 | |||
149 | case PPC44x_TLB_XLAT: | ||
150 | tlbe->word1 = vcpu->arch.gpr[rs]; | ||
151 | break; | ||
152 | |||
153 | case PPC44x_TLB_ATTRIB: | ||
154 | tlbe->word2 = vcpu->arch.gpr[rs]; | ||
155 | break; | ||
156 | |||
157 | default: | ||
158 | return EMULATE_FAIL; | ||
159 | } | ||
160 | |||
161 | if (tlbe_is_host_safe(vcpu, tlbe)) { | ||
162 | eaddr = get_tlb_eaddr(tlbe); | ||
163 | raddr = get_tlb_raddr(tlbe); | ||
164 | asid = (tlbe->word0 & PPC44x_TLB_TS) | tlbe->tid; | ||
165 | flags = tlbe->word2 & 0xffff; | ||
166 | |||
167 | /* Create a 4KB mapping on the host. If the guest wanted a | ||
168 | * large page, only the first 4KB is mapped here and the rest | ||
169 | * are mapped on the fly. */ | ||
170 | kvmppc_mmu_map(vcpu, eaddr, raddr >> PAGE_SHIFT, asid, flags); | ||
171 | } | ||
172 | |||
173 | KVMTRACE_5D(GTLB_WRITE, vcpu, index, | ||
174 | tlbe->tid, tlbe->word0, tlbe->word1, tlbe->word2, | ||
175 | handler); | ||
176 | |||
177 | return EMULATE_DONE; | ||
178 | } | ||
179 | |||
180 | static void kvmppc_emulate_dec(struct kvm_vcpu *vcpu) | 88 | static void kvmppc_emulate_dec(struct kvm_vcpu *vcpu) |
181 | { | 89 | { |
182 | if (vcpu->arch.tcr & TCR_DIE) { | 90 | if (vcpu->arch.tcr & TCR_DIE) { |
@@ -222,6 +130,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) | |||
222 | int rc; | 130 | int rc; |
223 | int rs; | 131 | int rs; |
224 | int rt; | 132 | int rt; |
133 | int ws; | ||
225 | int sprn; | 134 | int sprn; |
226 | int dcrn; | 135 | int dcrn; |
227 | enum emulation_result emulated = EMULATE_DONE; | 136 | enum emulation_result emulated = EMULATE_DONE; |
@@ -630,33 +539,18 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) | |||
630 | break; | 539 | break; |
631 | 540 | ||
632 | case 978: /* tlbwe */ | 541 | case 978: /* tlbwe */ |
633 | emulated = kvmppc_emul_tlbwe(vcpu, inst); | 542 | ra = get_ra(inst); |
543 | rs = get_rs(inst); | ||
544 | ws = get_ws(inst); | ||
545 | emulated = kvmppc_emul_tlbwe(vcpu, ra, rs, ws); | ||
634 | break; | 546 | break; |
635 | 547 | ||
636 | case 914: { /* tlbsx */ | 548 | case 914: /* tlbsx */ |
637 | int index; | ||
638 | unsigned int as = get_mmucr_sts(vcpu); | ||
639 | unsigned int pid = get_mmucr_stid(vcpu); | ||
640 | |||
641 | rt = get_rt(inst); | 549 | rt = get_rt(inst); |
642 | ra = get_ra(inst); | 550 | ra = get_ra(inst); |
643 | rb = get_rb(inst); | 551 | rb = get_rb(inst); |
644 | rc = get_rc(inst); | 552 | rc = get_rc(inst); |
645 | 553 | emulated = kvmppc_emul_tlbsx(vcpu, rt, ra, rb, rc); | |
646 | ea = vcpu->arch.gpr[rb]; | ||
647 | if (ra) | ||
648 | ea += vcpu->arch.gpr[ra]; | ||
649 | |||
650 | index = kvmppc_44x_tlb_index(vcpu, ea, pid, as); | ||
651 | if (rc) { | ||
652 | if (index < 0) | ||
653 | vcpu->arch.cr &= ~0x20000000; | ||
654 | else | ||
655 | vcpu->arch.cr |= 0x20000000; | ||
656 | } | ||
657 | vcpu->arch.gpr[rt] = index; | ||
658 | |||
659 | } | ||
660 | break; | 554 | break; |
661 | 555 | ||
662 | case 790: /* lhbrx */ | 556 | case 790: /* lhbrx */ |