Commit | Line | Data |
---|---|---|
73196cd3 | 1 | /* |
c7ba7771 | 2 | * Copyright (C) 2010,2012 Freescale Semiconductor, Inc. All rights reserved. |
73196cd3 SW |
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> | |
398a76c6 AG |
19 | #include <linux/miscdevice.h> |
20 | #include <linux/module.h> | |
73196cd3 SW |
21 | |
22 | #include <asm/reg.h> | |
23 | #include <asm/cputable.h> | |
24 | #include <asm/tlbflush.h> | |
25 | #include <asm/kvm_ppc.h> | |
26 | #include <asm/dbell.h> | |
27 | ||
28 | #include "booke.h" | |
29 | #include "e500.h" | |
30 | ||
31 | void kvmppc_set_pending_interrupt(struct kvm_vcpu *vcpu, enum int_class type) | |
32 | { | |
33 | enum ppc_dbell dbell_type; | |
34 | unsigned long tag; | |
35 | ||
36 | switch (type) { | |
37 | case INT_CLASS_NONCRIT: | |
38 | dbell_type = PPC_G_DBELL; | |
39 | break; | |
40 | case INT_CLASS_CRIT: | |
41 | dbell_type = PPC_G_DBELL_CRIT; | |
42 | break; | |
43 | case INT_CLASS_MC: | |
44 | dbell_type = PPC_G_DBELL_MC; | |
45 | break; | |
46 | default: | |
47 | WARN_ONCE(1, "%s: unknown int type %d\n", __func__, type); | |
48 | return; | |
49 | } | |
50 | ||
51 | ||
52 | tag = PPC_DBELL_LPID(vcpu->kvm->arch.lpid) | vcpu->vcpu_id; | |
53 | mb(); | |
54 | ppc_msgsnd(dbell_type, 0, tag); | |
55 | } | |
56 | ||
57 | /* gtlbe must not be mapped by more than one host tlb entry */ | |
58 | void kvmppc_e500_tlbil_one(struct kvmppc_vcpu_e500 *vcpu_e500, | |
59 | struct kvm_book3e_206_tlb_entry *gtlbe) | |
60 | { | |
61 | unsigned int tid, ts; | |
66c9897d MC |
62 | gva_t eaddr; |
63 | u32 val, lpid; | |
73196cd3 SW |
64 | unsigned long flags; |
65 | ||
66 | ts = get_tlb_ts(gtlbe); | |
67 | tid = get_tlb_tid(gtlbe); | |
68 | lpid = vcpu_e500->vcpu.kvm->arch.lpid; | |
69 | ||
70 | /* We search the host TLB to invalidate its shadow TLB entry */ | |
71 | val = (tid << 16) | ts; | |
72 | eaddr = get_tlb_eaddr(gtlbe); | |
73 | ||
74 | local_irq_save(flags); | |
75 | ||
76 | mtspr(SPRN_MAS6, val); | |
77 | mtspr(SPRN_MAS5, MAS5_SGS | lpid); | |
78 | ||
79 | asm volatile("tlbsx 0, %[eaddr]\n" : : [eaddr] "r" (eaddr)); | |
80 | val = mfspr(SPRN_MAS1); | |
81 | if (val & MAS1_VALID) { | |
82 | mtspr(SPRN_MAS1, val & ~MAS1_VALID); | |
83 | asm volatile("tlbwe"); | |
84 | } | |
85 | mtspr(SPRN_MAS5, 0); | |
86 | /* NOTE: tlbsx also updates mas8, so clear it for host tlbwe */ | |
87 | mtspr(SPRN_MAS8, 0); | |
88 | isync(); | |
89 | ||
90 | local_irq_restore(flags); | |
91 | } | |
92 | ||
93 | void kvmppc_e500_tlbil_all(struct kvmppc_vcpu_e500 *vcpu_e500) | |
94 | { | |
95 | unsigned long flags; | |
96 | ||
97 | local_irq_save(flags); | |
98 | mtspr(SPRN_MAS5, MAS5_SGS | vcpu_e500->vcpu.kvm->arch.lpid); | |
99 | asm volatile("tlbilxlpid"); | |
100 | mtspr(SPRN_MAS5, 0); | |
101 | local_irq_restore(flags); | |
102 | } | |
103 | ||
104 | void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 pid) | |
105 | { | |
106 | vcpu->arch.pid = pid; | |
107 | } | |
108 | ||
109 | void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr) | |
110 | { | |
111 | } | |
112 | ||
1f0eeb7e | 113 | static DEFINE_PER_CPU(struct kvm_vcpu *[KVMPPC_NR_LPIDS], last_vcpu_of_lpid); |
c5e6cb05 | 114 | |
3a167bea | 115 | static void kvmppc_core_vcpu_load_e500mc(struct kvm_vcpu *vcpu, int cpu) |
73196cd3 SW |
116 | { |
117 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | |
118 | ||
119 | kvmppc_booke_vcpu_load(vcpu, cpu); | |
120 | ||
121 | mtspr(SPRN_LPID, vcpu->kvm->arch.lpid); | |
122 | mtspr(SPRN_EPCR, vcpu->arch.shadow_epcr); | |
123 | mtspr(SPRN_GPIR, vcpu->vcpu_id); | |
124 | mtspr(SPRN_MSRP, vcpu->arch.shadow_msrp); | |
125 | mtspr(SPRN_EPLC, vcpu->arch.eplc); | |
126 | mtspr(SPRN_EPSC, vcpu->arch.epsc); | |
127 | ||
128 | mtspr(SPRN_GIVPR, vcpu->arch.ivpr); | |
129 | mtspr(SPRN_GIVOR2, vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE]); | |
130 | mtspr(SPRN_GIVOR8, vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL]); | |
131 | mtspr(SPRN_GSPRG0, (unsigned long)vcpu->arch.shared->sprg0); | |
132 | mtspr(SPRN_GSPRG1, (unsigned long)vcpu->arch.shared->sprg1); | |
133 | mtspr(SPRN_GSPRG2, (unsigned long)vcpu->arch.shared->sprg2); | |
134 | mtspr(SPRN_GSPRG3, (unsigned long)vcpu->arch.shared->sprg3); | |
135 | ||
136 | mtspr(SPRN_GSRR0, vcpu->arch.shared->srr0); | |
137 | mtspr(SPRN_GSRR1, vcpu->arch.shared->srr1); | |
138 | ||
139 | mtspr(SPRN_GEPR, vcpu->arch.epr); | |
140 | mtspr(SPRN_GDEAR, vcpu->arch.shared->dar); | |
141 | mtspr(SPRN_GESR, vcpu->arch.shared->esr); | |
142 | ||
c5e6cb05 | 143 | if (vcpu->arch.oldpir != mfspr(SPRN_PIR) || |
1f0eeb7e | 144 | __get_cpu_var(last_vcpu_of_lpid)[vcpu->kvm->arch.lpid] != vcpu) { |
73196cd3 | 145 | kvmppc_e500_tlbil_all(vcpu_e500); |
1f0eeb7e | 146 | __get_cpu_var(last_vcpu_of_lpid)[vcpu->kvm->arch.lpid] = vcpu; |
c5e6cb05 | 147 | } |
73196cd3 SW |
148 | |
149 | kvmppc_load_guest_fp(vcpu); | |
150 | } | |
151 | ||
3a167bea | 152 | static void kvmppc_core_vcpu_put_e500mc(struct kvm_vcpu *vcpu) |
73196cd3 SW |
153 | { |
154 | vcpu->arch.eplc = mfspr(SPRN_EPLC); | |
155 | vcpu->arch.epsc = mfspr(SPRN_EPSC); | |
156 | ||
157 | vcpu->arch.shared->sprg0 = mfspr(SPRN_GSPRG0); | |
158 | vcpu->arch.shared->sprg1 = mfspr(SPRN_GSPRG1); | |
159 | vcpu->arch.shared->sprg2 = mfspr(SPRN_GSPRG2); | |
160 | vcpu->arch.shared->sprg3 = mfspr(SPRN_GSPRG3); | |
161 | ||
162 | vcpu->arch.shared->srr0 = mfspr(SPRN_GSRR0); | |
163 | vcpu->arch.shared->srr1 = mfspr(SPRN_GSRR1); | |
164 | ||
165 | vcpu->arch.epr = mfspr(SPRN_GEPR); | |
166 | vcpu->arch.shared->dar = mfspr(SPRN_GDEAR); | |
167 | vcpu->arch.shared->esr = mfspr(SPRN_GESR); | |
168 | ||
169 | vcpu->arch.oldpir = mfspr(SPRN_PIR); | |
170 | ||
171 | kvmppc_booke_vcpu_put(vcpu); | |
172 | } | |
173 | ||
174 | int kvmppc_core_check_processor_compat(void) | |
175 | { | |
176 | int r; | |
177 | ||
178 | if (strcmp(cur_cpu_spec->cpu_name, "e500mc") == 0) | |
179 | r = 0; | |
180 | else if (strcmp(cur_cpu_spec->cpu_name, "e5500") == 0) | |
181 | r = 0; | |
182 | else | |
183 | r = -ENOTSUPP; | |
184 | ||
185 | return r; | |
186 | } | |
187 | ||
188 | int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu) | |
189 | { | |
190 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | |
191 | ||
192 | vcpu->arch.shadow_epcr = SPRN_EPCR_DSIGS | SPRN_EPCR_DGTMI | \ | |
193 | SPRN_EPCR_DUVD; | |
c7ba7771 MC |
194 | #ifdef CONFIG_64BIT |
195 | vcpu->arch.shadow_epcr |= SPRN_EPCR_ICM; | |
196 | #endif | |
73196cd3 SW |
197 | vcpu->arch.shadow_msrp = MSRP_UCLEP | MSRP_DEP | MSRP_PMMP; |
198 | vcpu->arch.eplc = EPC_EGS | (vcpu->kvm->arch.lpid << EPC_ELPID_SHIFT); | |
199 | vcpu->arch.epsc = vcpu->arch.eplc; | |
200 | ||
201 | vcpu->arch.pvr = mfspr(SPRN_PVR); | |
202 | vcpu_e500->svr = mfspr(SPRN_SVR); | |
203 | ||
204 | vcpu->arch.cpu_type = KVM_CPU_E500MC; | |
205 | ||
206 | return 0; | |
207 | } | |
208 | ||
3a167bea AK |
209 | static int kvmppc_core_get_sregs_e500mc(struct kvm_vcpu *vcpu, |
210 | struct kvm_sregs *sregs) | |
73196cd3 SW |
211 | { |
212 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | |
213 | ||
214 | sregs->u.e.features |= KVM_SREGS_E_ARCH206_MMU | KVM_SREGS_E_PM | | |
215 | KVM_SREGS_E_PC; | |
216 | sregs->u.e.impl_id = KVM_SREGS_E_IMPL_FSL; | |
217 | ||
218 | sregs->u.e.impl.fsl.features = 0; | |
219 | sregs->u.e.impl.fsl.svr = vcpu_e500->svr; | |
220 | sregs->u.e.impl.fsl.hid0 = vcpu_e500->hid0; | |
221 | sregs->u.e.impl.fsl.mcar = vcpu_e500->mcar; | |
222 | ||
223 | kvmppc_get_sregs_e500_tlb(vcpu, sregs); | |
224 | ||
225 | sregs->u.e.ivor_high[3] = | |
226 | vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR]; | |
227 | sregs->u.e.ivor_high[4] = vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL]; | |
228 | sregs->u.e.ivor_high[5] = vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL_CRIT]; | |
229 | ||
3a167bea | 230 | return kvmppc_get_sregs_ivor(vcpu, sregs); |
73196cd3 SW |
231 | } |
232 | ||
3a167bea AK |
233 | static int kvmppc_core_set_sregs_e500mc(struct kvm_vcpu *vcpu, |
234 | struct kvm_sregs *sregs) | |
73196cd3 SW |
235 | { |
236 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | |
237 | int ret; | |
238 | ||
239 | if (sregs->u.e.impl_id == KVM_SREGS_E_IMPL_FSL) { | |
240 | vcpu_e500->svr = sregs->u.e.impl.fsl.svr; | |
241 | vcpu_e500->hid0 = sregs->u.e.impl.fsl.hid0; | |
242 | vcpu_e500->mcar = sregs->u.e.impl.fsl.mcar; | |
243 | } | |
244 | ||
245 | ret = kvmppc_set_sregs_e500_tlb(vcpu, sregs); | |
246 | if (ret < 0) | |
247 | return ret; | |
248 | ||
249 | if (!(sregs->u.e.features & KVM_SREGS_E_IVOR)) | |
250 | return 0; | |
251 | ||
252 | if (sregs->u.e.features & KVM_SREGS_E_PM) { | |
253 | vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR] = | |
254 | sregs->u.e.ivor_high[3]; | |
255 | } | |
256 | ||
257 | if (sregs->u.e.features & KVM_SREGS_E_PC) { | |
258 | vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL] = | |
259 | sregs->u.e.ivor_high[4]; | |
260 | vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL_CRIT] = | |
261 | sregs->u.e.ivor_high[5]; | |
262 | } | |
263 | ||
264 | return kvmppc_set_sregs_ivor(vcpu, sregs); | |
265 | } | |
266 | ||
3a167bea AK |
267 | static int kvmppc_get_one_reg_e500mc(struct kvm_vcpu *vcpu, u64 id, |
268 | union kvmppc_one_reg *val) | |
35b299e2 | 269 | { |
28d2f421 BB |
270 | int r = 0; |
271 | ||
272 | switch (id) { | |
273 | case KVM_REG_PPC_SPRG9: | |
274 | *val = get_reg_val(id, vcpu->arch.sprg9); | |
275 | break; | |
276 | default: | |
277 | r = kvmppc_get_one_reg_e500_tlb(vcpu, id, val); | |
278 | } | |
279 | ||
a85d2aa2 | 280 | return r; |
35b299e2 MC |
281 | } |
282 | ||
3a167bea AK |
283 | static int kvmppc_set_one_reg_e500mc(struct kvm_vcpu *vcpu, u64 id, |
284 | union kvmppc_one_reg *val) | |
35b299e2 | 285 | { |
28d2f421 BB |
286 | int r = 0; |
287 | ||
288 | switch (id) { | |
289 | case KVM_REG_PPC_SPRG9: | |
290 | vcpu->arch.sprg9 = set_reg_val(id, *val); | |
291 | break; | |
292 | default: | |
293 | r = kvmppc_set_one_reg_e500_tlb(vcpu, id, val); | |
294 | } | |
295 | ||
a85d2aa2 | 296 | return r; |
35b299e2 MC |
297 | } |
298 | ||
3a167bea AK |
299 | static struct kvm_vcpu *kvmppc_core_vcpu_create_e500mc(struct kvm *kvm, |
300 | unsigned int id) | |
73196cd3 SW |
301 | { |
302 | struct kvmppc_vcpu_e500 *vcpu_e500; | |
303 | struct kvm_vcpu *vcpu; | |
304 | int err; | |
305 | ||
306 | vcpu_e500 = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL); | |
307 | if (!vcpu_e500) { | |
308 | err = -ENOMEM; | |
309 | goto out; | |
310 | } | |
311 | vcpu = &vcpu_e500->vcpu; | |
312 | ||
313 | /* Invalid PIR value -- this LPID dosn't have valid state on any cpu */ | |
314 | vcpu->arch.oldpir = 0xffffffff; | |
315 | ||
316 | err = kvm_vcpu_init(vcpu, kvm, id); | |
317 | if (err) | |
318 | goto free_vcpu; | |
319 | ||
320 | err = kvmppc_e500_tlb_init(vcpu_e500); | |
321 | if (err) | |
322 | goto uninit_vcpu; | |
323 | ||
324 | vcpu->arch.shared = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); | |
325 | if (!vcpu->arch.shared) | |
326 | goto uninit_tlb; | |
327 | ||
328 | return vcpu; | |
329 | ||
330 | uninit_tlb: | |
331 | kvmppc_e500_tlb_uninit(vcpu_e500); | |
332 | uninit_vcpu: | |
333 | kvm_vcpu_uninit(vcpu); | |
334 | ||
335 | free_vcpu: | |
336 | kmem_cache_free(kvm_vcpu_cache, vcpu_e500); | |
337 | out: | |
338 | return ERR_PTR(err); | |
339 | } | |
340 | ||
3a167bea | 341 | static void kvmppc_core_vcpu_free_e500mc(struct kvm_vcpu *vcpu) |
73196cd3 SW |
342 | { |
343 | struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); | |
344 | ||
345 | free_page((unsigned long)vcpu->arch.shared); | |
346 | kvmppc_e500_tlb_uninit(vcpu_e500); | |
347 | kvm_vcpu_uninit(vcpu); | |
348 | kmem_cache_free(kvm_vcpu_cache, vcpu_e500); | |
349 | } | |
350 | ||
3a167bea | 351 | static int kvmppc_core_init_vm_e500mc(struct kvm *kvm) |
73196cd3 SW |
352 | { |
353 | int lpid; | |
354 | ||
355 | lpid = kvmppc_alloc_lpid(); | |
356 | if (lpid < 0) | |
357 | return lpid; | |
358 | ||
359 | kvm->arch.lpid = lpid; | |
360 | return 0; | |
361 | } | |
362 | ||
3a167bea | 363 | static void kvmppc_core_destroy_vm_e500mc(struct kvm *kvm) |
73196cd3 SW |
364 | { |
365 | kvmppc_free_lpid(kvm->arch.lpid); | |
366 | } | |
367 | ||
3a167bea AK |
368 | static struct kvmppc_ops kvm_ops_e500mc = { |
369 | .get_sregs = kvmppc_core_get_sregs_e500mc, | |
370 | .set_sregs = kvmppc_core_set_sregs_e500mc, | |
371 | .get_one_reg = kvmppc_get_one_reg_e500mc, | |
372 | .set_one_reg = kvmppc_set_one_reg_e500mc, | |
373 | .vcpu_load = kvmppc_core_vcpu_load_e500mc, | |
374 | .vcpu_put = kvmppc_core_vcpu_put_e500mc, | |
375 | .vcpu_create = kvmppc_core_vcpu_create_e500mc, | |
376 | .vcpu_free = kvmppc_core_vcpu_free_e500mc, | |
377 | .mmu_destroy = kvmppc_mmu_destroy_e500, | |
378 | .init_vm = kvmppc_core_init_vm_e500mc, | |
379 | .destroy_vm = kvmppc_core_destroy_vm_e500mc, | |
380 | .emulate_op = kvmppc_core_emulate_op_e500, | |
381 | .emulate_mtspr = kvmppc_core_emulate_mtspr_e500, | |
382 | .emulate_mfspr = kvmppc_core_emulate_mfspr_e500, | |
383 | }; | |
384 | ||
73196cd3 SW |
385 | static int __init kvmppc_e500mc_init(void) |
386 | { | |
387 | int r; | |
388 | ||
389 | r = kvmppc_booke_init(); | |
390 | if (r) | |
3a167bea | 391 | goto err_out; |
73196cd3 SW |
392 | |
393 | kvmppc_init_lpid(64); | |
394 | kvmppc_claim_lpid(0); /* host */ | |
395 | ||
cbbc58d4 | 396 | r = kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE); |
3a167bea AK |
397 | if (r) |
398 | goto err_out; | |
cbbc58d4 AK |
399 | kvm_ops_e500mc.owner = THIS_MODULE; |
400 | kvmppc_pr_ops = &kvm_ops_e500mc; | |
401 | ||
3a167bea AK |
402 | err_out: |
403 | return r; | |
73196cd3 SW |
404 | } |
405 | ||
406 | static void __exit kvmppc_e500mc_exit(void) | |
407 | { | |
cbbc58d4 | 408 | kvmppc_pr_ops = NULL; |
73196cd3 SW |
409 | kvmppc_booke_exit(); |
410 | } | |
411 | ||
412 | module_init(kvmppc_e500mc_init); | |
413 | module_exit(kvmppc_e500mc_exit); | |
398a76c6 AG |
414 | MODULE_ALIAS_MISCDEV(KVM_MINOR); |
415 | MODULE_ALIAS("devname:kvm"); |