]> Git Repo - qemu.git/blob - hw/spapr_hcall.c
pseries: More complete WIMG validation in H_ENTER code
[qemu.git] / hw / spapr_hcall.c
1 #include "sysemu.h"
2 #include "cpu.h"
3 #include "dyngen-exec.h"
4 #include "qemu-char.h"
5 #include "sysemu.h"
6 #include "qemu-char.h"
7 #include "helper_regs.h"
8 #include "hw/spapr.h"
9
10 #define HPTES_PER_GROUP 8
11
12 #define HPTE_V_SSIZE_SHIFT      62
13 #define HPTE_V_AVPN_SHIFT       7
14 #define HPTE_V_AVPN             0x3fffffffffffff80ULL
15 #define HPTE_V_AVPN_VAL(x)      (((x) & HPTE_V_AVPN) >> HPTE_V_AVPN_SHIFT)
16 #define HPTE_V_COMPARE(x, y)    (!(((x) ^ (y)) & 0xffffffffffffff80UL))
17 #define HPTE_V_BOLTED           0x0000000000000010ULL
18 #define HPTE_V_LOCK             0x0000000000000008ULL
19 #define HPTE_V_LARGE            0x0000000000000004ULL
20 #define HPTE_V_SECONDARY        0x0000000000000002ULL
21 #define HPTE_V_VALID            0x0000000000000001ULL
22
23 #define HPTE_R_PP0              0x8000000000000000ULL
24 #define HPTE_R_TS               0x4000000000000000ULL
25 #define HPTE_R_KEY_HI           0x3000000000000000ULL
26 #define HPTE_R_RPN_SHIFT        12
27 #define HPTE_R_RPN              0x3ffffffffffff000ULL
28 #define HPTE_R_FLAGS            0x00000000000003ffULL
29 #define HPTE_R_PP               0x0000000000000003ULL
30 #define HPTE_R_N                0x0000000000000004ULL
31 #define HPTE_R_G                0x0000000000000008ULL
32 #define HPTE_R_M                0x0000000000000010ULL
33 #define HPTE_R_I                0x0000000000000020ULL
34 #define HPTE_R_W                0x0000000000000040ULL
35 #define HPTE_R_WIMG             0x0000000000000078ULL
36 #define HPTE_R_C                0x0000000000000080ULL
37 #define HPTE_R_R                0x0000000000000100ULL
38 #define HPTE_R_KEY_LO           0x0000000000000e00ULL
39
40 #define HPTE_V_1TB_SEG          0x4000000000000000ULL
41 #define HPTE_V_VRMA_MASK        0x4001ffffff000000ULL
42
43 #define HPTE_V_HVLOCK           0x40ULL
44
45 static inline int lock_hpte(void *hpte, target_ulong bits)
46 {
47     uint64_t pteh;
48
49     pteh = ldq_p(hpte);
50
51     /* We're protected by qemu's global lock here */
52     if (pteh & bits) {
53         return 0;
54     }
55     stq_p(hpte, pteh | HPTE_V_HVLOCK);
56     return 1;
57 }
58
59 static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r,
60                                      target_ulong pte_index)
61 {
62     target_ulong rb, va_low;
63
64     rb = (v & ~0x7fULL) << 16; /* AVA field */
65     va_low = pte_index >> 3;
66     if (v & HPTE_V_SECONDARY) {
67         va_low = ~va_low;
68     }
69     /* xor vsid from AVA */
70     if (!(v & HPTE_V_1TB_SEG)) {
71         va_low ^= v >> 12;
72     } else {
73         va_low ^= v >> 24;
74     }
75     va_low &= 0x7ff;
76     if (v & HPTE_V_LARGE) {
77         rb |= 1;                         /* L field */
78 #if 0 /* Disable that P7 specific bit for now */
79         if (r & 0xff000) {
80             /* non-16MB large page, must be 64k */
81             /* (masks depend on page size) */
82             rb |= 0x1000;                /* page encoding in LP field */
83             rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP field */
84             rb |= (va_low & 0xfe);       /* AVAL field */
85         }
86 #endif
87     } else {
88         /* 4kB page */
89         rb |= (va_low & 0x7ff) << 12;   /* remaining 11b of AVA */
90     }
91     rb |= (v >> 54) & 0x300;            /* B field */
92     return rb;
93 }
94
95 static target_ulong h_enter(CPUState *env, sPAPREnvironment *spapr,
96                             target_ulong opcode, target_ulong *args)
97 {
98     target_ulong flags = args[0];
99     target_ulong pte_index = args[1];
100     target_ulong pteh = args[2];
101     target_ulong ptel = args[3];
102     target_ulong page_shift = 12;
103     target_ulong raddr;
104     target_ulong i;
105     uint8_t *hpte;
106
107     /* only handle 4k and 16M pages for now */
108     if (pteh & HPTE_V_LARGE) {
109 #if 0 /* We don't support 64k pages yet */
110         if ((ptel & 0xf000) == 0x1000) {
111             /* 64k page */
112         } else
113 #endif
114         if ((ptel & 0xff000) == 0) {
115             /* 16M page */
116             page_shift = 24;
117             /* lowest AVA bit must be 0 for 16M pages */
118             if (pteh & 0x80) {
119                 return H_PARAMETER;
120             }
121         } else {
122             return H_PARAMETER;
123         }
124     }
125
126     raddr = (ptel & HPTE_R_RPN) & ~((1ULL << page_shift) - 1);
127
128     if (raddr < spapr->ram_limit) {
129         /* Regular RAM - should have WIMG=0010 */
130         if ((ptel & HPTE_R_WIMG) != HPTE_R_M) {
131             return H_PARAMETER;
132         }
133     } else {
134         /* Looks like an IO address */
135         /* FIXME: What WIMG combinations could be sensible for IO?
136          * For now we allow WIMG=010x, but are there others? */
137         /* FIXME: Should we check against registered IO addresses? */
138         if ((ptel & (HPTE_R_W | HPTE_R_I | HPTE_R_M)) != HPTE_R_I) {
139             return H_PARAMETER;
140         }
141     }
142
143     pteh &= ~0x60ULL;
144
145     if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
146         return H_PARAMETER;
147     }
148     if (likely((flags & H_EXACT) == 0)) {
149         pte_index &= ~7ULL;
150         hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
151         for (i = 0; ; ++i) {
152             if (i == 8) {
153                 return H_PTEG_FULL;
154             }
155             if (((ldq_p(hpte) & HPTE_V_VALID) == 0) &&
156                 lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) {
157                 break;
158             }
159             hpte += HASH_PTE_SIZE_64;
160         }
161     } else {
162         i = 0;
163         hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
164         if (!lock_hpte(hpte, HPTE_V_HVLOCK | HPTE_V_VALID)) {
165             return H_PTEG_FULL;
166         }
167     }
168     stq_p(hpte + (HASH_PTE_SIZE_64/2), ptel);
169     /* eieio();  FIXME: need some sort of barrier for smp? */
170     stq_p(hpte, pteh);
171
172     assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
173     args[0] = pte_index + i;
174     return H_SUCCESS;
175 }
176
177 static target_ulong h_remove(CPUState *env, sPAPREnvironment *spapr,
178                              target_ulong opcode, target_ulong *args)
179 {
180     target_ulong flags = args[0];
181     target_ulong pte_index = args[1];
182     target_ulong avpn = args[2];
183     uint8_t *hpte;
184     target_ulong v, r, rb;
185
186     if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
187         return H_PARAMETER;
188     }
189
190     hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
191     while (!lock_hpte(hpte, HPTE_V_HVLOCK)) {
192         /* We have no real concurrency in qemu soft-emulation, so we
193          * will never actually have a contested lock */
194         assert(0);
195     }
196
197     v = ldq_p(hpte);
198     r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
199
200     if ((v & HPTE_V_VALID) == 0 ||
201         ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) ||
202         ((flags & H_ANDCOND) && (v & avpn) != 0)) {
203         stq_p(hpte, v & ~HPTE_V_HVLOCK);
204         assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
205         return H_NOT_FOUND;
206     }
207     args[0] = v & ~HPTE_V_HVLOCK;
208     args[1] = r;
209     stq_p(hpte, 0);
210     rb = compute_tlbie_rb(v, r, pte_index);
211     ppc_tlb_invalidate_one(env, rb);
212     assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
213     return H_SUCCESS;
214 }
215
216 static target_ulong h_protect(CPUState *env, sPAPREnvironment *spapr,
217                               target_ulong opcode, target_ulong *args)
218 {
219     target_ulong flags = args[0];
220     target_ulong pte_index = args[1];
221     target_ulong avpn = args[2];
222     uint8_t *hpte;
223     target_ulong v, r, rb;
224
225     if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) {
226         return H_PARAMETER;
227     }
228
229     hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64);
230     while (!lock_hpte(hpte, HPTE_V_HVLOCK)) {
231         /* We have no real concurrency in qemu soft-emulation, so we
232          * will never actually have a contested lock */
233         assert(0);
234     }
235
236     v = ldq_p(hpte);
237     r = ldq_p(hpte + (HASH_PTE_SIZE_64/2));
238
239     if ((v & HPTE_V_VALID) == 0 ||
240         ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) {
241         stq_p(hpte, v & ~HPTE_V_HVLOCK);
242         assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
243         return H_NOT_FOUND;
244     }
245
246     r &= ~(HPTE_R_PP0 | HPTE_R_PP | HPTE_R_N |
247            HPTE_R_KEY_HI | HPTE_R_KEY_LO);
248     r |= (flags << 55) & HPTE_R_PP0;
249     r |= (flags << 48) & HPTE_R_KEY_HI;
250     r |= flags & (HPTE_R_PP | HPTE_R_N | HPTE_R_KEY_LO);
251     rb = compute_tlbie_rb(v, r, pte_index);
252     stq_p(hpte, v & ~HPTE_V_VALID);
253     ppc_tlb_invalidate_one(env, rb);
254     stq_p(hpte + (HASH_PTE_SIZE_64/2), r);
255     /* Don't need a memory barrier, due to qemu's global lock */
256     stq_p(hpte, v & ~HPTE_V_HVLOCK);
257     assert(!(ldq_p(hpte) & HPTE_V_HVLOCK));
258     return H_SUCCESS;
259 }
260
261 static target_ulong h_set_dabr(CPUState *env, sPAPREnvironment *spapr,
262                                target_ulong opcode, target_ulong *args)
263 {
264     /* FIXME: actually implement this */
265     return H_HARDWARE;
266 }
267
268 #define FLAGS_REGISTER_VPA         0x0000200000000000ULL
269 #define FLAGS_REGISTER_DTL         0x0000400000000000ULL
270 #define FLAGS_REGISTER_SLBSHADOW   0x0000600000000000ULL
271 #define FLAGS_DEREGISTER_VPA       0x0000a00000000000ULL
272 #define FLAGS_DEREGISTER_DTL       0x0000c00000000000ULL
273 #define FLAGS_DEREGISTER_SLBSHADOW 0x0000e00000000000ULL
274
275 #define VPA_MIN_SIZE           640
276 #define VPA_SIZE_OFFSET        0x4
277 #define VPA_SHARED_PROC_OFFSET 0x9
278 #define VPA_SHARED_PROC_VAL    0x2
279
280 static target_ulong register_vpa(CPUState *env, target_ulong vpa)
281 {
282     uint16_t size;
283     uint8_t tmp;
284
285     if (vpa == 0) {
286         hcall_dprintf("Can't cope with registering a VPA at logical 0\n");
287         return H_HARDWARE;
288     }
289
290     if (vpa % env->dcache_line_size) {
291         return H_PARAMETER;
292     }
293     /* FIXME: bounds check the address */
294
295     size = lduw_be_phys(vpa + 0x4);
296
297     if (size < VPA_MIN_SIZE) {
298         return H_PARAMETER;
299     }
300
301     /* VPA is not allowed to cross a page boundary */
302     if ((vpa / 4096) != ((vpa + size - 1) / 4096)) {
303         return H_PARAMETER;
304     }
305
306     env->vpa = vpa;
307
308     tmp = ldub_phys(env->vpa + VPA_SHARED_PROC_OFFSET);
309     tmp |= VPA_SHARED_PROC_VAL;
310     stb_phys(env->vpa + VPA_SHARED_PROC_OFFSET, tmp);
311
312     return H_SUCCESS;
313 }
314
315 static target_ulong deregister_vpa(CPUState *env, target_ulong vpa)
316 {
317     if (env->slb_shadow) {
318         return H_RESOURCE;
319     }
320
321     if (env->dispatch_trace_log) {
322         return H_RESOURCE;
323     }
324
325     env->vpa = 0;
326     return H_SUCCESS;
327 }
328
329 static target_ulong register_slb_shadow(CPUState *env, target_ulong addr)
330 {
331     uint32_t size;
332
333     if (addr == 0) {
334         hcall_dprintf("Can't cope with SLB shadow at logical 0\n");
335         return H_HARDWARE;
336     }
337
338     size = ldl_be_phys(addr + 0x4);
339     if (size < 0x8) {
340         return H_PARAMETER;
341     }
342
343     if ((addr / 4096) != ((addr + size - 1) / 4096)) {
344         return H_PARAMETER;
345     }
346
347     if (!env->vpa) {
348         return H_RESOURCE;
349     }
350
351     env->slb_shadow = addr;
352
353     return H_SUCCESS;
354 }
355
356 static target_ulong deregister_slb_shadow(CPUState *env, target_ulong addr)
357 {
358     env->slb_shadow = 0;
359     return H_SUCCESS;
360 }
361
362 static target_ulong register_dtl(CPUState *env, target_ulong addr)
363 {
364     uint32_t size;
365
366     if (addr == 0) {
367         hcall_dprintf("Can't cope with DTL at logical 0\n");
368         return H_HARDWARE;
369     }
370
371     size = ldl_be_phys(addr + 0x4);
372
373     if (size < 48) {
374         return H_PARAMETER;
375     }
376
377     if (!env->vpa) {
378         return H_RESOURCE;
379     }
380
381     env->dispatch_trace_log = addr;
382     env->dtl_size = size;
383
384     return H_SUCCESS;
385 }
386
387 static target_ulong deregister_dtl(CPUState *emv, target_ulong addr)
388 {
389     env->dispatch_trace_log = 0;
390     env->dtl_size = 0;
391
392     return H_SUCCESS;
393 }
394
395 static target_ulong h_register_vpa(CPUState *env, sPAPREnvironment *spapr,
396                                    target_ulong opcode, target_ulong *args)
397 {
398     target_ulong flags = args[0];
399     target_ulong procno = args[1];
400     target_ulong vpa = args[2];
401     target_ulong ret = H_PARAMETER;
402     CPUState *tenv;
403
404     for (tenv = first_cpu; tenv; tenv = tenv->next_cpu) {
405         if (tenv->cpu_index == procno) {
406             break;
407         }
408     }
409
410     if (!tenv) {
411         return H_PARAMETER;
412     }
413
414     switch (flags) {
415     case FLAGS_REGISTER_VPA:
416         ret = register_vpa(tenv, vpa);
417         break;
418
419     case FLAGS_DEREGISTER_VPA:
420         ret = deregister_vpa(tenv, vpa);
421         break;
422
423     case FLAGS_REGISTER_SLBSHADOW:
424         ret = register_slb_shadow(tenv, vpa);
425         break;
426
427     case FLAGS_DEREGISTER_SLBSHADOW:
428         ret = deregister_slb_shadow(tenv, vpa);
429         break;
430
431     case FLAGS_REGISTER_DTL:
432         ret = register_dtl(tenv, vpa);
433         break;
434
435     case FLAGS_DEREGISTER_DTL:
436         ret = deregister_dtl(tenv, vpa);
437         break;
438     }
439
440     return ret;
441 }
442
443 static target_ulong h_cede(CPUState *env, sPAPREnvironment *spapr,
444                            target_ulong opcode, target_ulong *args)
445 {
446     env->msr |= (1ULL << MSR_EE);
447     hreg_compute_hflags(env);
448     if (!cpu_has_work(env)) {
449         env->halted = 1;
450     }
451     return H_SUCCESS;
452 }
453
454 static target_ulong h_rtas(CPUState *env, sPAPREnvironment *spapr,
455                            target_ulong opcode, target_ulong *args)
456 {
457     target_ulong rtas_r3 = args[0];
458     uint32_t token = ldl_be_phys(rtas_r3);
459     uint32_t nargs = ldl_be_phys(rtas_r3 + 4);
460     uint32_t nret = ldl_be_phys(rtas_r3 + 8);
461
462     return spapr_rtas_call(spapr, token, nargs, rtas_r3 + 12,
463                            nret, rtas_r3 + 12 + 4*nargs);
464 }
465
466 static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1];
467 static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1];
468
469 void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn)
470 {
471     spapr_hcall_fn *slot;
472
473     if (opcode <= MAX_HCALL_OPCODE) {
474         assert((opcode & 0x3) == 0);
475
476         slot = &papr_hypercall_table[opcode / 4];
477     } else {
478         assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX));
479
480
481         slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
482     }
483
484     assert(!(*slot) || (fn == *slot));
485     *slot = fn;
486 }
487
488 target_ulong spapr_hypercall(CPUState *env, target_ulong opcode,
489                              target_ulong *args)
490 {
491     if (msr_pr) {
492         hcall_dprintf("Hypercall made with MSR[PR]=1\n");
493         return H_PRIVILEGE;
494     }
495
496     if ((opcode <= MAX_HCALL_OPCODE)
497         && ((opcode & 0x3) == 0)) {
498         spapr_hcall_fn fn = papr_hypercall_table[opcode / 4];
499
500         if (fn) {
501             return fn(env, spapr, opcode, args);
502         }
503     } else if ((opcode >= KVMPPC_HCALL_BASE) &&
504                (opcode <= KVMPPC_HCALL_MAX)) {
505         spapr_hcall_fn fn = kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE];
506
507         if (fn) {
508             return fn(env, spapr, opcode, args);
509         }
510     }
511
512     hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode);
513     return H_FUNCTION;
514 }
515
516 static void hypercall_init(void)
517 {
518     /* hcall-pft */
519     spapr_register_hypercall(H_ENTER, h_enter);
520     spapr_register_hypercall(H_REMOVE, h_remove);
521     spapr_register_hypercall(H_PROTECT, h_protect);
522
523     /* hcall-dabr */
524     spapr_register_hypercall(H_SET_DABR, h_set_dabr);
525
526     /* hcall-splpar */
527     spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa);
528     spapr_register_hypercall(H_CEDE, h_cede);
529
530     /* qemu/KVM-PPC specific hcalls */
531     spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas);
532 }
533 device_init(hypercall_init);
This page took 0.054601 seconds and 4 git commands to generate.