diff options
author | Scott Wood <scottwood@freescale.com> | 2011-12-20 10:34:47 -0500 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2012-04-08 05:54:33 -0400 |
commit | 73196cd364a2d972d73fa08da9d81ca3215bed68 (patch) | |
tree | 058b8487bb512b5cde4e827d8aa2525680e6519b /arch/powerpc/kvm/e500mc.c | |
parent | 8fae845f4956de0becc115e926d33eff46722e94 (diff) |
KVM: PPC: e500mc support
Add processor support for e500mc, using hardware virtualization support
(GS-mode).
Current issues include:
- No support for external proxy (coreint) interrupt mode in the guest.
Includes work by Ashish Kalra <Ashish.Kalra@freescale.com>,
Varun Sethi <Varun.Sethi@freescale.com>, and
Liu Yu <yu.liu@freescale.com>.
Signed-off-by: Scott Wood <scottwood@freescale.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Avi Kivity <avi@redhat.com>
Diffstat (limited to 'arch/powerpc/kvm/e500mc.c')
-rw-r--r-- | arch/powerpc/kvm/e500mc.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/arch/powerpc/kvm/e500mc.c b/arch/powerpc/kvm/e500mc.c new file mode 100644 index 000000000000..fe6c1de6b701 --- /dev/null +++ b/arch/powerpc/kvm/e500mc.c | |||
@@ -0,0 +1,342 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2010 Freescale Semiconductor, Inc. All rights reserved. | ||
3 | * | ||
4 | * Author: Varun Sethi, <varun.sethi@freescale.com> | ||
5 | * | ||
6 | * Description: | ||
7 | * This file is derived from arch/powerpc/kvm/e500.c, | ||
8 | * by Yu Liu <yu.liu@freescale.com>. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License, version 2, as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/kvm_host.h> | ||
16 | #include <linux/slab.h> | ||
17 | #include <linux/err.h> | ||
18 | #include <linux/export.h> | ||
19 | |||
20 | #include <asm/reg.h> | ||
21 | #include <asm/cputable.h> | ||
22 | #include <asm/tlbflush.h> | ||
23 | #include <asm/kvm_ppc.h> | ||
24 | #include <asm/dbell.h> | ||
25 | |||
26 | #include "booke.h" | ||
27 | #include "e500.h" | ||
28 | |||
29 | void kvmppc_set_pending_interrupt(struct kvm_vcpu *vcpu, enum int_class type) | ||
30 | { | ||
31 | enum ppc_dbell dbell_type; | ||
32 | unsigned long tag; | ||
33 | |||
34 | switch (type) { | ||
35 | case INT_CLASS_NONCRIT: | ||
36 | dbell_type = PPC_G_DBELL; | ||
37 | break; | ||
38 | case INT_CLASS_CRIT: | ||
39 | dbell_type = PPC_G_DBELL_CRIT; | ||
40 | break; | ||
41 | case INT_CLASS_MC: | ||
42 | dbell_type = PPC_G_DBELL_MC; | ||
43 | break; | ||
44 | default: | ||
45 | WARN_ONCE(1, "%s: unknown int type %d\n", __func__, type); | ||
46 | return; | ||
47 | } | ||
48 | |||
49 | |||
50 | tag = PPC_DBELL_LPID(vcpu->kvm->arch.lpid) | vcpu->vcpu_id; | ||
51 | mb(); | ||
52 | ppc_msgsnd(dbell_type, 0, tag); | ||
53 | } | ||
54 | |||
55 | /* gtlbe must not be mapped by more than one host tlb entry */ | ||
56 | void kvmppc_e500_tlbil_one(struct kvmppc_vcpu_e500 *vcpu_e500, | ||
57 | struct kvm_book3e_206_tlb_entry *gtlbe) | ||
58 | { | ||
59 | unsigned int tid, ts; | ||
60 | u32 val, eaddr, lpid; | ||
61 | unsigned long flags; | ||
62 | |||
63 | ts = get_tlb_ts(gtlbe); | ||
64 | tid = get_tlb_tid(gtlbe); | ||
65 | lpid = vcpu_e500->vcpu.kvm->arch.lpid; | ||
66 | |||
67 | /* We search the host TLB to invalidate its shadow TLB entry */ | ||
68 | val = (tid << 16) | ts; | ||
69 | eaddr = get_tlb_eaddr(gtlbe); | ||
70 | |||
71 | local_irq_save(flags); | ||
72 | |||
73 | mtspr(SPRN_MAS6, val); | ||
74 | mtspr(SPRN_MAS5, MAS5_SGS | lpid); | ||
75 | |||
76 | asm volatile("tlbsx 0, %[eaddr]\n" : : [eaddr] "r" (eaddr)); | ||
77 | val = mfspr(SPRN_MAS1); | ||
78 | if (val & MAS1_VALID) { | ||
79 | mtspr(SPRN_MAS1, val & ~MAS1_VALID); | ||
80 | asm volatile("tlbwe"); | ||
81 | } | ||
82 | mtspr(SPRN_MAS5, 0); | ||
83 | /* NOTE: tlbsx also updates mas8, so clear it for host tlbwe */ | ||
84 | mtspr(SPRN_MAS8, 0); | ||
85 | isync(); | ||
86 | |||
87 | local_irq_restore(flags); | ||
88 | } | ||
89 | |||
90 | void kvmppc_e500_tlbil_all(struct kvmppc_vcpu_e500 *vcpu_e500) | ||
91 | { | ||
92 | unsigned long flags; | ||
93 | |||
94 | local_irq_save(flags); | ||
95 | mtspr(SPRN_MAS5, MAS5_SGS | vcpu_e500->vcpu.kvm->arch.lpid); | ||
96 | asm volatile("tlbilxlpid"); | ||
97 | mtspr(SPRN_MAS5, 0); | ||
98 | local_irq_restore(flags); | ||
99 | } | ||
100 | |||
101 | void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 pid) | ||
102 | { | ||
103 | vcpu->arch.pid = pid; | ||
104 | } | ||
105 | |||
106 | void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr) | ||
107 | { | ||
108 | } | ||
109 | |||
110 | void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) | ||
111 | { | ||
112 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | ||
113 | |||
114 | kvmppc_booke_vcpu_load(vcpu, cpu); | ||
115 | |||
116 | mtspr(SPRN_LPID, vcpu->kvm->arch.lpid); | ||
117 | mtspr(SPRN_EPCR, vcpu->arch.shadow_epcr); | ||
118 | mtspr(SPRN_GPIR, vcpu->vcpu_id); | ||
119 | mtspr(SPRN_MSRP, vcpu->arch.shadow_msrp); | ||
120 | mtspr(SPRN_EPLC, vcpu->arch.eplc); | ||
121 | mtspr(SPRN_EPSC, vcpu->arch.epsc); | ||
122 | |||
123 | mtspr(SPRN_GIVPR, vcpu->arch.ivpr); | ||
124 | mtspr(SPRN_GIVOR2, vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE]); | ||
125 | mtspr(SPRN_GIVOR8, vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL]); | ||
126 | mtspr(SPRN_GSPRG0, (unsigned long)vcpu->arch.shared->sprg0); | ||
127 | mtspr(SPRN_GSPRG1, (unsigned long)vcpu->arch.shared->sprg1); | ||
128 | mtspr(SPRN_GSPRG2, (unsigned long)vcpu->arch.shared->sprg2); | ||
129 | mtspr(SPRN_GSPRG3, (unsigned long)vcpu->arch.shared->sprg3); | ||
130 | |||
131 | mtspr(SPRN_GSRR0, vcpu->arch.shared->srr0); | ||
132 | mtspr(SPRN_GSRR1, vcpu->arch.shared->srr1); | ||
133 | |||
134 | mtspr(SPRN_GEPR, vcpu->arch.epr); | ||
135 | mtspr(SPRN_GDEAR, vcpu->arch.shared->dar); | ||
136 | mtspr(SPRN_GESR, vcpu->arch.shared->esr); | ||
137 | |||
138 | if (vcpu->arch.oldpir != mfspr(SPRN_PIR)) | ||
139 | kvmppc_e500_tlbil_all(vcpu_e500); | ||
140 | |||
141 | kvmppc_load_guest_fp(vcpu); | ||
142 | } | ||
143 | |||
144 | void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu) | ||
145 | { | ||
146 | vcpu->arch.eplc = mfspr(SPRN_EPLC); | ||
147 | vcpu->arch.epsc = mfspr(SPRN_EPSC); | ||
148 | |||
149 | vcpu->arch.shared->sprg0 = mfspr(SPRN_GSPRG0); | ||
150 | vcpu->arch.shared->sprg1 = mfspr(SPRN_GSPRG1); | ||
151 | vcpu->arch.shared->sprg2 = mfspr(SPRN_GSPRG2); | ||
152 | vcpu->arch.shared->sprg3 = mfspr(SPRN_GSPRG3); | ||
153 | |||
154 | vcpu->arch.shared->srr0 = mfspr(SPRN_GSRR0); | ||
155 | vcpu->arch.shared->srr1 = mfspr(SPRN_GSRR1); | ||
156 | |||
157 | vcpu->arch.epr = mfspr(SPRN_GEPR); | ||
158 | vcpu->arch.shared->dar = mfspr(SPRN_GDEAR); | ||
159 | vcpu->arch.shared->esr = mfspr(SPRN_GESR); | ||
160 | |||
161 | vcpu->arch.oldpir = mfspr(SPRN_PIR); | ||
162 | |||
163 | kvmppc_booke_vcpu_put(vcpu); | ||
164 | } | ||
165 | |||
166 | int kvmppc_core_check_processor_compat(void) | ||
167 | { | ||
168 | int r; | ||
169 | |||
170 | if (strcmp(cur_cpu_spec->cpu_name, "e500mc") == 0) | ||
171 | r = 0; | ||
172 | else if (strcmp(cur_cpu_spec->cpu_name, "e5500") == 0) | ||
173 | r = 0; | ||
174 | else | ||
175 | r = -ENOTSUPP; | ||
176 | |||
177 | return r; | ||
178 | } | ||
179 | |||
180 | int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu) | ||
181 | { | ||
182 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | ||
183 | |||
184 | vcpu->arch.shadow_epcr = SPRN_EPCR_DSIGS | SPRN_EPCR_DGTMI | \ | ||
185 | SPRN_EPCR_DUVD; | ||
186 | vcpu->arch.shadow_msrp = MSRP_UCLEP | MSRP_DEP | MSRP_PMMP; | ||
187 | vcpu->arch.eplc = EPC_EGS | (vcpu->kvm->arch.lpid << EPC_ELPID_SHIFT); | ||
188 | vcpu->arch.epsc = vcpu->arch.eplc; | ||
189 | |||
190 | vcpu->arch.pvr = mfspr(SPRN_PVR); | ||
191 | vcpu_e500->svr = mfspr(SPRN_SVR); | ||
192 | |||
193 | vcpu->arch.cpu_type = KVM_CPU_E500MC; | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) | ||
199 | { | ||
200 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | ||
201 | |||
202 | sregs->u.e.features |= KVM_SREGS_E_ARCH206_MMU | KVM_SREGS_E_PM | | ||
203 | KVM_SREGS_E_PC; | ||
204 | sregs->u.e.impl_id = KVM_SREGS_E_IMPL_FSL; | ||
205 | |||
206 | sregs->u.e.impl.fsl.features = 0; | ||
207 | sregs->u.e.impl.fsl.svr = vcpu_e500->svr; | ||
208 | sregs->u.e.impl.fsl.hid0 = vcpu_e500->hid0; | ||
209 | sregs->u.e.impl.fsl.mcar = vcpu_e500->mcar; | ||
210 | |||
211 | kvmppc_get_sregs_e500_tlb(vcpu, sregs); | ||
212 | |||
213 | sregs->u.e.ivor_high[3] = | ||
214 | vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR]; | ||
215 | sregs->u.e.ivor_high[4] = vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL]; | ||
216 | sregs->u.e.ivor_high[5] = vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL_CRIT]; | ||
217 | |||
218 | kvmppc_get_sregs_ivor(vcpu, sregs); | ||
219 | } | ||
220 | |||
221 | int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) | ||
222 | { | ||
223 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | ||
224 | int ret; | ||
225 | |||
226 | if (sregs->u.e.impl_id == KVM_SREGS_E_IMPL_FSL) { | ||
227 | vcpu_e500->svr = sregs->u.e.impl.fsl.svr; | ||
228 | vcpu_e500->hid0 = sregs->u.e.impl.fsl.hid0; | ||
229 | vcpu_e500->mcar = sregs->u.e.impl.fsl.mcar; | ||
230 | } | ||
231 | |||
232 | ret = kvmppc_set_sregs_e500_tlb(vcpu, sregs); | ||
233 | if (ret < 0) | ||
234 | return ret; | ||
235 | |||
236 | if (!(sregs->u.e.features & KVM_SREGS_E_IVOR)) | ||
237 | return 0; | ||
238 | |||
239 | if (sregs->u.e.features & KVM_SREGS_E_PM) { | ||
240 | vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR] = | ||
241 | sregs->u.e.ivor_high[3]; | ||
242 | } | ||
243 | |||
244 | if (sregs->u.e.features & KVM_SREGS_E_PC) { | ||
245 | vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL] = | ||
246 | sregs->u.e.ivor_high[4]; | ||
247 | vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL_CRIT] = | ||
248 | sregs->u.e.ivor_high[5]; | ||
249 | } | ||
250 | |||
251 | return kvmppc_set_sregs_ivor(vcpu, sregs); | ||
252 | } | ||
253 | |||
254 | struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id) | ||
255 | { | ||
256 | struct kvmppc_vcpu_e500 *vcpu_e500; | ||
257 | struct kvm_vcpu *vcpu; | ||
258 | int err; | ||
259 | |||
260 | vcpu_e500 = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL); | ||
261 | if (!vcpu_e500) { | ||
262 | err = -ENOMEM; | ||
263 | goto out; | ||
264 | } | ||
265 | vcpu = &vcpu_e500->vcpu; | ||
266 | |||
267 | /* Invalid PIR value -- this LPID dosn't have valid state on any cpu */ | ||
268 | vcpu->arch.oldpir = 0xffffffff; | ||
269 | |||
270 | err = kvm_vcpu_init(vcpu, kvm, id); | ||
271 | if (err) | ||
272 | goto free_vcpu; | ||
273 | |||
274 | err = kvmppc_e500_tlb_init(vcpu_e500); | ||
275 | if (err) | ||
276 | goto uninit_vcpu; | ||
277 | |||
278 | vcpu->arch.shared = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); | ||
279 | if (!vcpu->arch.shared) | ||
280 | goto uninit_tlb; | ||
281 | |||
282 | return vcpu; | ||
283 | |||
284 | uninit_tlb: | ||
285 | kvmppc_e500_tlb_uninit(vcpu_e500); | ||
286 | uninit_vcpu: | ||
287 | kvm_vcpu_uninit(vcpu); | ||
288 | |||
289 | free_vcpu: | ||
290 | kmem_cache_free(kvm_vcpu_cache, vcpu_e500); | ||
291 | out: | ||
292 | return ERR_PTR(err); | ||
293 | } | ||
294 | |||
295 | void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu) | ||
296 | { | ||
297 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | ||
298 | |||
299 | free_page((unsigned long)vcpu->arch.shared); | ||
300 | kvmppc_e500_tlb_uninit(vcpu_e500); | ||
301 | kvm_vcpu_uninit(vcpu); | ||
302 | kmem_cache_free(kvm_vcpu_cache, vcpu_e500); | ||
303 | } | ||
304 | |||
305 | int kvmppc_core_init_vm(struct kvm *kvm) | ||
306 | { | ||
307 | int lpid; | ||
308 | |||
309 | lpid = kvmppc_alloc_lpid(); | ||
310 | if (lpid < 0) | ||
311 | return lpid; | ||
312 | |||
313 | kvm->arch.lpid = lpid; | ||
314 | return 0; | ||
315 | } | ||
316 | |||
317 | void kvmppc_core_destroy_vm(struct kvm *kvm) | ||
318 | { | ||
319 | kvmppc_free_lpid(kvm->arch.lpid); | ||
320 | } | ||
321 | |||
322 | static int __init kvmppc_e500mc_init(void) | ||
323 | { | ||
324 | int r; | ||
325 | |||
326 | r = kvmppc_booke_init(); | ||
327 | if (r) | ||
328 | return r; | ||
329 | |||
330 | kvmppc_init_lpid(64); | ||
331 | kvmppc_claim_lpid(0); /* host */ | ||
332 | |||
333 | return kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE); | ||
334 | } | ||
335 | |||
336 | static void __exit kvmppc_e500mc_exit(void) | ||
337 | { | ||
338 | kvmppc_booke_exit(); | ||
339 | } | ||
340 | |||
341 | module_init(kvmppc_e500mc_init); | ||
342 | module_exit(kvmppc_e500mc_exit); | ||