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