]>
Commit | Line | Data |
---|---|---|
9c17d615 | 1 | #include "sysemu/sysemu.h" |
9fdf0c29 | 2 | #include "cpu.h" |
ed120055 | 3 | #include "helper_regs.h" |
0d09e41a | 4 | #include "hw/ppc/spapr.h" |
d5aea6f3 | 5 | #include "mmu-hash64.h" |
f43e3525 | 6 | |
f43e3525 DG |
7 | static target_ulong compute_tlbie_rb(target_ulong v, target_ulong r, |
8 | target_ulong pte_index) | |
9 | { | |
10 | target_ulong rb, va_low; | |
11 | ||
12 | rb = (v & ~0x7fULL) << 16; /* AVA field */ | |
13 | va_low = pte_index >> 3; | |
d5aea6f3 | 14 | if (v & HPTE64_V_SECONDARY) { |
f43e3525 DG |
15 | va_low = ~va_low; |
16 | } | |
17 | /* xor vsid from AVA */ | |
d5aea6f3 | 18 | if (!(v & HPTE64_V_1TB_SEG)) { |
f43e3525 DG |
19 | va_low ^= v >> 12; |
20 | } else { | |
21 | va_low ^= v >> 24; | |
22 | } | |
23 | va_low &= 0x7ff; | |
d5aea6f3 | 24 | if (v & HPTE64_V_LARGE) { |
f43e3525 DG |
25 | rb |= 1; /* L field */ |
26 | #if 0 /* Disable that P7 specific bit for now */ | |
27 | if (r & 0xff000) { | |
28 | /* non-16MB large page, must be 64k */ | |
29 | /* (masks depend on page size) */ | |
30 | rb |= 0x1000; /* page encoding in LP field */ | |
31 | rb |= (va_low & 0x7f) << 16; /* 7b of VA in AVA/LP field */ | |
32 | rb |= (va_low & 0xfe); /* AVAL field */ | |
33 | } | |
34 | #endif | |
35 | } else { | |
36 | /* 4kB page */ | |
37 | rb |= (va_low & 0x7ff) << 12; /* remaining 11b of AVA */ | |
38 | } | |
39 | rb |= (v >> 54) & 0x300; /* B field */ | |
40 | return rb; | |
41 | } | |
42 | ||
b13ce26d | 43 | static target_ulong h_enter(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
f43e3525 DG |
44 | target_ulong opcode, target_ulong *args) |
45 | { | |
b13ce26d | 46 | CPUPPCState *env = &cpu->env; |
f43e3525 DG |
47 | target_ulong flags = args[0]; |
48 | target_ulong pte_index = args[1]; | |
49 | target_ulong pteh = args[2]; | |
50 | target_ulong ptel = args[3]; | |
f73a2575 DG |
51 | target_ulong page_shift = 12; |
52 | target_ulong raddr; | |
1235a9cf | 53 | target_ulong i; |
dffdaf61 | 54 | hwaddr hpte; |
f43e3525 DG |
55 | |
56 | /* only handle 4k and 16M pages for now */ | |
d5aea6f3 | 57 | if (pteh & HPTE64_V_LARGE) { |
f43e3525 DG |
58 | #if 0 /* We don't support 64k pages yet */ |
59 | if ((ptel & 0xf000) == 0x1000) { | |
60 | /* 64k page */ | |
f43e3525 DG |
61 | } else |
62 | #endif | |
63 | if ((ptel & 0xff000) == 0) { | |
64 | /* 16M page */ | |
f73a2575 | 65 | page_shift = 24; |
f43e3525 DG |
66 | /* lowest AVA bit must be 0 for 16M pages */ |
67 | if (pteh & 0x80) { | |
68 | return H_PARAMETER; | |
69 | } | |
70 | } else { | |
71 | return H_PARAMETER; | |
72 | } | |
73 | } | |
74 | ||
d5aea6f3 | 75 | raddr = (ptel & HPTE64_R_RPN) & ~((1ULL << page_shift) - 1); |
f43e3525 | 76 | |
f73a2575 DG |
77 | if (raddr < spapr->ram_limit) { |
78 | /* Regular RAM - should have WIMG=0010 */ | |
d5aea6f3 | 79 | if ((ptel & HPTE64_R_WIMG) != HPTE64_R_M) { |
f73a2575 DG |
80 | return H_PARAMETER; |
81 | } | |
82 | } else { | |
83 | /* Looks like an IO address */ | |
84 | /* FIXME: What WIMG combinations could be sensible for IO? | |
85 | * For now we allow WIMG=010x, but are there others? */ | |
86 | /* FIXME: Should we check against registered IO addresses? */ | |
d5aea6f3 | 87 | if ((ptel & (HPTE64_R_W | HPTE64_R_I | HPTE64_R_M)) != HPTE64_R_I) { |
f73a2575 DG |
88 | return H_PARAMETER; |
89 | } | |
f43e3525 | 90 | } |
f73a2575 | 91 | |
f43e3525 DG |
92 | pteh &= ~0x60ULL; |
93 | ||
94 | if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { | |
95 | return H_PARAMETER; | |
96 | } | |
97 | if (likely((flags & H_EXACT) == 0)) { | |
98 | pte_index &= ~7ULL; | |
dffdaf61 | 99 | hpte = pte_index * HASH_PTE_SIZE_64; |
f43e3525 DG |
100 | for (i = 0; ; ++i) { |
101 | if (i == 8) { | |
102 | return H_PTEG_FULL; | |
103 | } | |
dffdaf61 | 104 | if ((ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) == 0) { |
f43e3525 DG |
105 | break; |
106 | } | |
107 | hpte += HASH_PTE_SIZE_64; | |
108 | } | |
109 | } else { | |
110 | i = 0; | |
dffdaf61 DG |
111 | hpte = pte_index * HASH_PTE_SIZE_64; |
112 | if (ppc_hash64_load_hpte0(env, hpte) & HPTE64_V_VALID) { | |
f43e3525 DG |
113 | return H_PTEG_FULL; |
114 | } | |
115 | } | |
dffdaf61 | 116 | ppc_hash64_store_hpte1(env, hpte, ptel); |
f43e3525 | 117 | /* eieio(); FIXME: need some sort of barrier for smp? */ |
dffdaf61 | 118 | ppc_hash64_store_hpte0(env, hpte, pteh); |
f43e3525 | 119 | |
f43e3525 DG |
120 | args[0] = pte_index + i; |
121 | return H_SUCCESS; | |
122 | } | |
123 | ||
a3d0abae DG |
124 | enum { |
125 | REMOVE_SUCCESS = 0, | |
126 | REMOVE_NOT_FOUND = 1, | |
127 | REMOVE_PARM = 2, | |
128 | REMOVE_HW = 3, | |
129 | }; | |
130 | ||
e2684c0b | 131 | static target_ulong remove_hpte(CPUPPCState *env, target_ulong ptex, |
a3d0abae DG |
132 | target_ulong avpn, |
133 | target_ulong flags, | |
134 | target_ulong *vp, target_ulong *rp) | |
f43e3525 | 135 | { |
dffdaf61 | 136 | hwaddr hpte; |
f43e3525 DG |
137 | target_ulong v, r, rb; |
138 | ||
a3d0abae DG |
139 | if ((ptex * HASH_PTE_SIZE_64) & ~env->htab_mask) { |
140 | return REMOVE_PARM; | |
f43e3525 DG |
141 | } |
142 | ||
dffdaf61 | 143 | hpte = ptex * HASH_PTE_SIZE_64; |
f43e3525 | 144 | |
dffdaf61 DG |
145 | v = ppc_hash64_load_hpte0(env, hpte); |
146 | r = ppc_hash64_load_hpte1(env, hpte); | |
f43e3525 | 147 | |
d5aea6f3 | 148 | if ((v & HPTE64_V_VALID) == 0 || |
f43e3525 DG |
149 | ((flags & H_AVPN) && (v & ~0x7fULL) != avpn) || |
150 | ((flags & H_ANDCOND) && (v & avpn) != 0)) { | |
a3d0abae | 151 | return REMOVE_NOT_FOUND; |
f43e3525 | 152 | } |
35f9304d | 153 | *vp = v; |
a3d0abae | 154 | *rp = r; |
dffdaf61 | 155 | ppc_hash64_store_hpte0(env, hpte, 0); |
a3d0abae | 156 | rb = compute_tlbie_rb(v, r, ptex); |
f43e3525 | 157 | ppc_tlb_invalidate_one(env, rb); |
a3d0abae DG |
158 | return REMOVE_SUCCESS; |
159 | } | |
160 | ||
b13ce26d | 161 | static target_ulong h_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
a3d0abae DG |
162 | target_ulong opcode, target_ulong *args) |
163 | { | |
b13ce26d | 164 | CPUPPCState *env = &cpu->env; |
a3d0abae DG |
165 | target_ulong flags = args[0]; |
166 | target_ulong pte_index = args[1]; | |
167 | target_ulong avpn = args[2]; | |
168 | int ret; | |
169 | ||
170 | ret = remove_hpte(env, pte_index, avpn, flags, | |
171 | &args[0], &args[1]); | |
172 | ||
173 | switch (ret) { | |
174 | case REMOVE_SUCCESS: | |
175 | return H_SUCCESS; | |
176 | ||
177 | case REMOVE_NOT_FOUND: | |
178 | return H_NOT_FOUND; | |
179 | ||
180 | case REMOVE_PARM: | |
181 | return H_PARAMETER; | |
182 | ||
183 | case REMOVE_HW: | |
184 | return H_HARDWARE; | |
185 | } | |
186 | ||
187 | assert(0); | |
188 | } | |
189 | ||
190 | #define H_BULK_REMOVE_TYPE 0xc000000000000000ULL | |
191 | #define H_BULK_REMOVE_REQUEST 0x4000000000000000ULL | |
192 | #define H_BULK_REMOVE_RESPONSE 0x8000000000000000ULL | |
193 | #define H_BULK_REMOVE_END 0xc000000000000000ULL | |
194 | #define H_BULK_REMOVE_CODE 0x3000000000000000ULL | |
195 | #define H_BULK_REMOVE_SUCCESS 0x0000000000000000ULL | |
196 | #define H_BULK_REMOVE_NOT_FOUND 0x1000000000000000ULL | |
197 | #define H_BULK_REMOVE_PARM 0x2000000000000000ULL | |
198 | #define H_BULK_REMOVE_HW 0x3000000000000000ULL | |
199 | #define H_BULK_REMOVE_RC 0x0c00000000000000ULL | |
200 | #define H_BULK_REMOVE_FLAGS 0x0300000000000000ULL | |
201 | #define H_BULK_REMOVE_ABSOLUTE 0x0000000000000000ULL | |
202 | #define H_BULK_REMOVE_ANDCOND 0x0100000000000000ULL | |
203 | #define H_BULK_REMOVE_AVPN 0x0200000000000000ULL | |
204 | #define H_BULK_REMOVE_PTEX 0x00ffffffffffffffULL | |
205 | ||
206 | #define H_BULK_REMOVE_MAX_BATCH 4 | |
207 | ||
b13ce26d | 208 | static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
a3d0abae DG |
209 | target_ulong opcode, target_ulong *args) |
210 | { | |
b13ce26d | 211 | CPUPPCState *env = &cpu->env; |
a3d0abae DG |
212 | int i; |
213 | ||
214 | for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { | |
215 | target_ulong *tsh = &args[i*2]; | |
216 | target_ulong tsl = args[i*2 + 1]; | |
217 | target_ulong v, r, ret; | |
218 | ||
219 | if ((*tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) { | |
220 | break; | |
221 | } else if ((*tsh & H_BULK_REMOVE_TYPE) != H_BULK_REMOVE_REQUEST) { | |
222 | return H_PARAMETER; | |
223 | } | |
224 | ||
225 | *tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS; | |
226 | *tsh |= H_BULK_REMOVE_RESPONSE; | |
227 | ||
228 | if ((*tsh & H_BULK_REMOVE_ANDCOND) && (*tsh & H_BULK_REMOVE_AVPN)) { | |
229 | *tsh |= H_BULK_REMOVE_PARM; | |
230 | return H_PARAMETER; | |
231 | } | |
232 | ||
233 | ret = remove_hpte(env, *tsh & H_BULK_REMOVE_PTEX, tsl, | |
234 | (*tsh & H_BULK_REMOVE_FLAGS) >> 26, | |
235 | &v, &r); | |
236 | ||
237 | *tsh |= ret << 60; | |
238 | ||
239 | switch (ret) { | |
240 | case REMOVE_SUCCESS: | |
d5aea6f3 | 241 | *tsh |= (r & (HPTE64_R_C | HPTE64_R_R)) << 43; |
a3d0abae DG |
242 | break; |
243 | ||
244 | case REMOVE_PARM: | |
245 | return H_PARAMETER; | |
246 | ||
247 | case REMOVE_HW: | |
248 | return H_HARDWARE; | |
249 | } | |
250 | } | |
251 | ||
f43e3525 DG |
252 | return H_SUCCESS; |
253 | } | |
254 | ||
b13ce26d | 255 | static target_ulong h_protect(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
f43e3525 DG |
256 | target_ulong opcode, target_ulong *args) |
257 | { | |
b13ce26d | 258 | CPUPPCState *env = &cpu->env; |
f43e3525 DG |
259 | target_ulong flags = args[0]; |
260 | target_ulong pte_index = args[1]; | |
261 | target_ulong avpn = args[2]; | |
dffdaf61 | 262 | hwaddr hpte; |
f43e3525 DG |
263 | target_ulong v, r, rb; |
264 | ||
265 | if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { | |
266 | return H_PARAMETER; | |
267 | } | |
268 | ||
dffdaf61 | 269 | hpte = pte_index * HASH_PTE_SIZE_64; |
f43e3525 | 270 | |
dffdaf61 DG |
271 | v = ppc_hash64_load_hpte0(env, hpte); |
272 | r = ppc_hash64_load_hpte1(env, hpte); | |
f43e3525 | 273 | |
d5aea6f3 | 274 | if ((v & HPTE64_V_VALID) == 0 || |
f43e3525 | 275 | ((flags & H_AVPN) && (v & ~0x7fULL) != avpn)) { |
f43e3525 DG |
276 | return H_NOT_FOUND; |
277 | } | |
278 | ||
d5aea6f3 DG |
279 | r &= ~(HPTE64_R_PP0 | HPTE64_R_PP | HPTE64_R_N | |
280 | HPTE64_R_KEY_HI | HPTE64_R_KEY_LO); | |
281 | r |= (flags << 55) & HPTE64_R_PP0; | |
282 | r |= (flags << 48) & HPTE64_R_KEY_HI; | |
283 | r |= flags & (HPTE64_R_PP | HPTE64_R_N | HPTE64_R_KEY_LO); | |
f43e3525 | 284 | rb = compute_tlbie_rb(v, r, pte_index); |
dffdaf61 | 285 | ppc_hash64_store_hpte0(env, hpte, v & ~HPTE64_V_VALID); |
f43e3525 | 286 | ppc_tlb_invalidate_one(env, rb); |
dffdaf61 | 287 | ppc_hash64_store_hpte1(env, hpte, r); |
f43e3525 | 288 | /* Don't need a memory barrier, due to qemu's global lock */ |
dffdaf61 | 289 | ppc_hash64_store_hpte0(env, hpte, v); |
f43e3525 DG |
290 | return H_SUCCESS; |
291 | } | |
292 | ||
6bbd5dde EC |
293 | static target_ulong h_read(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
294 | target_ulong opcode, target_ulong *args) | |
295 | { | |
296 | CPUPPCState *env = &cpu->env; | |
297 | target_ulong flags = args[0]; | |
298 | target_ulong pte_index = args[1]; | |
299 | uint8_t *hpte; | |
300 | int i, ridx, n_entries = 1; | |
301 | ||
302 | if ((pte_index * HASH_PTE_SIZE_64) & ~env->htab_mask) { | |
303 | return H_PARAMETER; | |
304 | } | |
305 | ||
306 | if (flags & H_READ_4) { | |
307 | /* Clear the two low order bits */ | |
308 | pte_index &= ~(3ULL); | |
309 | n_entries = 4; | |
310 | } | |
311 | ||
312 | hpte = env->external_htab + (pte_index * HASH_PTE_SIZE_64); | |
313 | ||
314 | for (i = 0, ridx = 0; i < n_entries; i++) { | |
315 | args[ridx++] = ldq_p(hpte); | |
316 | args[ridx++] = ldq_p(hpte + (HASH_PTE_SIZE_64/2)); | |
317 | hpte += HASH_PTE_SIZE_64; | |
318 | } | |
319 | ||
320 | return H_SUCCESS; | |
321 | } | |
322 | ||
b13ce26d | 323 | static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
821303f5 DG |
324 | target_ulong opcode, target_ulong *args) |
325 | { | |
326 | /* FIXME: actually implement this */ | |
327 | return H_HARDWARE; | |
328 | } | |
329 | ||
ed120055 DG |
330 | #define FLAGS_REGISTER_VPA 0x0000200000000000ULL |
331 | #define FLAGS_REGISTER_DTL 0x0000400000000000ULL | |
332 | #define FLAGS_REGISTER_SLBSHADOW 0x0000600000000000ULL | |
333 | #define FLAGS_DEREGISTER_VPA 0x0000a00000000000ULL | |
334 | #define FLAGS_DEREGISTER_DTL 0x0000c00000000000ULL | |
335 | #define FLAGS_DEREGISTER_SLBSHADOW 0x0000e00000000000ULL | |
336 | ||
337 | #define VPA_MIN_SIZE 640 | |
338 | #define VPA_SIZE_OFFSET 0x4 | |
339 | #define VPA_SHARED_PROC_OFFSET 0x9 | |
340 | #define VPA_SHARED_PROC_VAL 0x2 | |
341 | ||
e2684c0b | 342 | static target_ulong register_vpa(CPUPPCState *env, target_ulong vpa) |
ed120055 DG |
343 | { |
344 | uint16_t size; | |
345 | uint8_t tmp; | |
346 | ||
347 | if (vpa == 0) { | |
348 | hcall_dprintf("Can't cope with registering a VPA at logical 0\n"); | |
349 | return H_HARDWARE; | |
350 | } | |
351 | ||
352 | if (vpa % env->dcache_line_size) { | |
353 | return H_PARAMETER; | |
354 | } | |
355 | /* FIXME: bounds check the address */ | |
356 | ||
06c46bba | 357 | size = lduw_be_phys(vpa + 0x4); |
ed120055 DG |
358 | |
359 | if (size < VPA_MIN_SIZE) { | |
360 | return H_PARAMETER; | |
361 | } | |
362 | ||
363 | /* VPA is not allowed to cross a page boundary */ | |
364 | if ((vpa / 4096) != ((vpa + size - 1) / 4096)) { | |
365 | return H_PARAMETER; | |
366 | } | |
367 | ||
1bfb37d1 | 368 | env->vpa_addr = vpa; |
ed120055 | 369 | |
1bfb37d1 | 370 | tmp = ldub_phys(env->vpa_addr + VPA_SHARED_PROC_OFFSET); |
ed120055 | 371 | tmp |= VPA_SHARED_PROC_VAL; |
1bfb37d1 | 372 | stb_phys(env->vpa_addr + VPA_SHARED_PROC_OFFSET, tmp); |
ed120055 DG |
373 | |
374 | return H_SUCCESS; | |
375 | } | |
376 | ||
e2684c0b | 377 | static target_ulong deregister_vpa(CPUPPCState *env, target_ulong vpa) |
ed120055 | 378 | { |
1bfb37d1 | 379 | if (env->slb_shadow_addr) { |
ed120055 DG |
380 | return H_RESOURCE; |
381 | } | |
382 | ||
1bfb37d1 | 383 | if (env->dtl_addr) { |
ed120055 DG |
384 | return H_RESOURCE; |
385 | } | |
386 | ||
1bfb37d1 | 387 | env->vpa_addr = 0; |
ed120055 DG |
388 | return H_SUCCESS; |
389 | } | |
390 | ||
e2684c0b | 391 | static target_ulong register_slb_shadow(CPUPPCState *env, target_ulong addr) |
ed120055 DG |
392 | { |
393 | uint32_t size; | |
394 | ||
395 | if (addr == 0) { | |
396 | hcall_dprintf("Can't cope with SLB shadow at logical 0\n"); | |
397 | return H_HARDWARE; | |
398 | } | |
399 | ||
06c46bba | 400 | size = ldl_be_phys(addr + 0x4); |
ed120055 DG |
401 | if (size < 0x8) { |
402 | return H_PARAMETER; | |
403 | } | |
404 | ||
405 | if ((addr / 4096) != ((addr + size - 1) / 4096)) { | |
406 | return H_PARAMETER; | |
407 | } | |
408 | ||
1bfb37d1 | 409 | if (!env->vpa_addr) { |
ed120055 DG |
410 | return H_RESOURCE; |
411 | } | |
412 | ||
1bfb37d1 DG |
413 | env->slb_shadow_addr = addr; |
414 | env->slb_shadow_size = size; | |
ed120055 DG |
415 | |
416 | return H_SUCCESS; | |
417 | } | |
418 | ||
e2684c0b | 419 | static target_ulong deregister_slb_shadow(CPUPPCState *env, target_ulong addr) |
ed120055 | 420 | { |
1bfb37d1 DG |
421 | env->slb_shadow_addr = 0; |
422 | env->slb_shadow_size = 0; | |
ed120055 DG |
423 | return H_SUCCESS; |
424 | } | |
425 | ||
e2684c0b | 426 | static target_ulong register_dtl(CPUPPCState *env, target_ulong addr) |
ed120055 DG |
427 | { |
428 | uint32_t size; | |
429 | ||
430 | if (addr == 0) { | |
431 | hcall_dprintf("Can't cope with DTL at logical 0\n"); | |
432 | return H_HARDWARE; | |
433 | } | |
434 | ||
06c46bba | 435 | size = ldl_be_phys(addr + 0x4); |
ed120055 DG |
436 | |
437 | if (size < 48) { | |
438 | return H_PARAMETER; | |
439 | } | |
440 | ||
1bfb37d1 | 441 | if (!env->vpa_addr) { |
ed120055 DG |
442 | return H_RESOURCE; |
443 | } | |
444 | ||
1bfb37d1 | 445 | env->dtl_addr = addr; |
ed120055 DG |
446 | env->dtl_size = size; |
447 | ||
448 | return H_SUCCESS; | |
449 | } | |
450 | ||
73f7821b | 451 | static target_ulong deregister_dtl(CPUPPCState *env, target_ulong addr) |
ed120055 | 452 | { |
1bfb37d1 | 453 | env->dtl_addr = 0; |
ed120055 DG |
454 | env->dtl_size = 0; |
455 | ||
456 | return H_SUCCESS; | |
457 | } | |
458 | ||
b13ce26d | 459 | static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
ed120055 DG |
460 | target_ulong opcode, target_ulong *args) |
461 | { | |
462 | target_ulong flags = args[0]; | |
463 | target_ulong procno = args[1]; | |
464 | target_ulong vpa = args[2]; | |
465 | target_ulong ret = H_PARAMETER; | |
e2684c0b | 466 | CPUPPCState *tenv; |
55e5c285 | 467 | CPUState *tcpu; |
ed120055 | 468 | |
5353d03d AF |
469 | tcpu = qemu_get_cpu(procno); |
470 | if (!tcpu) { | |
ed120055 DG |
471 | return H_PARAMETER; |
472 | } | |
5353d03d | 473 | tenv = tcpu->env_ptr; |
ed120055 DG |
474 | |
475 | switch (flags) { | |
476 | case FLAGS_REGISTER_VPA: | |
477 | ret = register_vpa(tenv, vpa); | |
478 | break; | |
479 | ||
480 | case FLAGS_DEREGISTER_VPA: | |
481 | ret = deregister_vpa(tenv, vpa); | |
482 | break; | |
483 | ||
484 | case FLAGS_REGISTER_SLBSHADOW: | |
485 | ret = register_slb_shadow(tenv, vpa); | |
486 | break; | |
487 | ||
488 | case FLAGS_DEREGISTER_SLBSHADOW: | |
489 | ret = deregister_slb_shadow(tenv, vpa); | |
490 | break; | |
491 | ||
492 | case FLAGS_REGISTER_DTL: | |
493 | ret = register_dtl(tenv, vpa); | |
494 | break; | |
495 | ||
496 | case FLAGS_DEREGISTER_DTL: | |
497 | ret = deregister_dtl(tenv, vpa); | |
498 | break; | |
499 | } | |
500 | ||
501 | return ret; | |
502 | } | |
503 | ||
b13ce26d | 504 | static target_ulong h_cede(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
ed120055 DG |
505 | target_ulong opcode, target_ulong *args) |
506 | { | |
b13ce26d | 507 | CPUPPCState *env = &cpu->env; |
fcd7d003 | 508 | CPUState *cs = CPU(cpu); |
b13ce26d | 509 | |
ed120055 DG |
510 | env->msr |= (1ULL << MSR_EE); |
511 | hreg_compute_hflags(env); | |
fcd7d003 | 512 | if (!cpu_has_work(cs)) { |
259186a7 | 513 | cs->halted = 1; |
1dd08894 | 514 | env->exception_index = EXCP_HLT; |
fcd7d003 | 515 | cs->exit_request = 1; |
ed120055 DG |
516 | } |
517 | return H_SUCCESS; | |
518 | } | |
519 | ||
b13ce26d | 520 | static target_ulong h_rtas(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
39ac8455 DG |
521 | target_ulong opcode, target_ulong *args) |
522 | { | |
523 | target_ulong rtas_r3 = args[0]; | |
06c46bba AG |
524 | uint32_t token = ldl_be_phys(rtas_r3); |
525 | uint32_t nargs = ldl_be_phys(rtas_r3 + 4); | |
526 | uint32_t nret = ldl_be_phys(rtas_r3 + 8); | |
39ac8455 DG |
527 | |
528 | return spapr_rtas_call(spapr, token, nargs, rtas_r3 + 12, | |
529 | nret, rtas_r3 + 12 + 4*nargs); | |
530 | } | |
531 | ||
b13ce26d | 532 | static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
827200a2 DG |
533 | target_ulong opcode, target_ulong *args) |
534 | { | |
535 | target_ulong size = args[0]; | |
536 | target_ulong addr = args[1]; | |
537 | ||
538 | switch (size) { | |
539 | case 1: | |
540 | args[0] = ldub_phys(addr); | |
541 | return H_SUCCESS; | |
542 | case 2: | |
543 | args[0] = lduw_phys(addr); | |
544 | return H_SUCCESS; | |
545 | case 4: | |
546 | args[0] = ldl_phys(addr); | |
547 | return H_SUCCESS; | |
548 | case 8: | |
549 | args[0] = ldq_phys(addr); | |
550 | return H_SUCCESS; | |
551 | } | |
552 | return H_PARAMETER; | |
553 | } | |
554 | ||
b13ce26d | 555 | static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
827200a2 DG |
556 | target_ulong opcode, target_ulong *args) |
557 | { | |
558 | target_ulong size = args[0]; | |
559 | target_ulong addr = args[1]; | |
560 | target_ulong val = args[2]; | |
561 | ||
562 | switch (size) { | |
563 | case 1: | |
564 | stb_phys(addr, val); | |
565 | return H_SUCCESS; | |
566 | case 2: | |
567 | stw_phys(addr, val); | |
568 | return H_SUCCESS; | |
569 | case 4: | |
570 | stl_phys(addr, val); | |
571 | return H_SUCCESS; | |
572 | case 8: | |
573 | stq_phys(addr, val); | |
574 | return H_SUCCESS; | |
575 | } | |
576 | return H_PARAMETER; | |
577 | } | |
578 | ||
b13ce26d | 579 | static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
c73e3771 BH |
580 | target_ulong opcode, target_ulong *args) |
581 | { | |
582 | target_ulong dst = args[0]; /* Destination address */ | |
583 | target_ulong src = args[1]; /* Source address */ | |
584 | target_ulong esize = args[2]; /* Element size (0=1,1=2,2=4,3=8) */ | |
585 | target_ulong count = args[3]; /* Element count */ | |
586 | target_ulong op = args[4]; /* 0 = copy, 1 = invert */ | |
587 | uint64_t tmp; | |
588 | unsigned int mask = (1 << esize) - 1; | |
589 | int step = 1 << esize; | |
590 | ||
591 | if (count > 0x80000000) { | |
592 | return H_PARAMETER; | |
593 | } | |
594 | ||
595 | if ((dst & mask) || (src & mask) || (op > 1)) { | |
596 | return H_PARAMETER; | |
597 | } | |
598 | ||
599 | if (dst >= src && dst < (src + (count << esize))) { | |
600 | dst = dst + ((count - 1) << esize); | |
601 | src = src + ((count - 1) << esize); | |
602 | step = -step; | |
603 | } | |
604 | ||
605 | while (count--) { | |
606 | switch (esize) { | |
607 | case 0: | |
608 | tmp = ldub_phys(src); | |
609 | break; | |
610 | case 1: | |
611 | tmp = lduw_phys(src); | |
612 | break; | |
613 | case 2: | |
614 | tmp = ldl_phys(src); | |
615 | break; | |
616 | case 3: | |
617 | tmp = ldq_phys(src); | |
618 | break; | |
619 | default: | |
620 | return H_PARAMETER; | |
621 | } | |
622 | if (op == 1) { | |
623 | tmp = ~tmp; | |
624 | } | |
625 | switch (esize) { | |
626 | case 0: | |
627 | stb_phys(dst, tmp); | |
628 | break; | |
629 | case 1: | |
630 | stw_phys(dst, tmp); | |
631 | break; | |
632 | case 2: | |
633 | stl_phys(dst, tmp); | |
634 | break; | |
635 | case 3: | |
636 | stq_phys(dst, tmp); | |
637 | break; | |
638 | } | |
639 | dst = dst + step; | |
640 | src = src + step; | |
641 | } | |
642 | ||
643 | return H_SUCCESS; | |
644 | } | |
645 | ||
b13ce26d | 646 | static target_ulong h_logical_icbi(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
827200a2 DG |
647 | target_ulong opcode, target_ulong *args) |
648 | { | |
649 | /* Nothing to do on emulation, KVM will trap this in the kernel */ | |
650 | return H_SUCCESS; | |
651 | } | |
652 | ||
b13ce26d | 653 | static target_ulong h_logical_dcbf(PowerPCCPU *cpu, sPAPREnvironment *spapr, |
827200a2 DG |
654 | target_ulong opcode, target_ulong *args) |
655 | { | |
656 | /* Nothing to do on emulation, KVM will trap this in the kernel */ | |
657 | return H_SUCCESS; | |
658 | } | |
659 | ||
7d7ba3fe DG |
660 | static spapr_hcall_fn papr_hypercall_table[(MAX_HCALL_OPCODE / 4) + 1]; |
661 | static spapr_hcall_fn kvmppc_hypercall_table[KVMPPC_HCALL_MAX - KVMPPC_HCALL_BASE + 1]; | |
9fdf0c29 DG |
662 | |
663 | void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn) | |
664 | { | |
39ac8455 DG |
665 | spapr_hcall_fn *slot; |
666 | ||
667 | if (opcode <= MAX_HCALL_OPCODE) { | |
668 | assert((opcode & 0x3) == 0); | |
9fdf0c29 | 669 | |
39ac8455 DG |
670 | slot = &papr_hypercall_table[opcode / 4]; |
671 | } else { | |
672 | assert((opcode >= KVMPPC_HCALL_BASE) && (opcode <= KVMPPC_HCALL_MAX)); | |
9fdf0c29 | 673 | |
39ac8455 DG |
674 | slot = &kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE]; |
675 | } | |
9fdf0c29 | 676 | |
c89d5299 | 677 | assert(!(*slot)); |
39ac8455 | 678 | *slot = fn; |
9fdf0c29 DG |
679 | } |
680 | ||
aa100fa4 | 681 | target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, |
9fdf0c29 DG |
682 | target_ulong *args) |
683 | { | |
9fdf0c29 DG |
684 | if ((opcode <= MAX_HCALL_OPCODE) |
685 | && ((opcode & 0x3) == 0)) { | |
39ac8455 DG |
686 | spapr_hcall_fn fn = papr_hypercall_table[opcode / 4]; |
687 | ||
688 | if (fn) { | |
b13ce26d | 689 | return fn(cpu, spapr, opcode, args); |
39ac8455 DG |
690 | } |
691 | } else if ((opcode >= KVMPPC_HCALL_BASE) && | |
692 | (opcode <= KVMPPC_HCALL_MAX)) { | |
693 | spapr_hcall_fn fn = kvmppc_hypercall_table[opcode - KVMPPC_HCALL_BASE]; | |
9fdf0c29 DG |
694 | |
695 | if (fn) { | |
b13ce26d | 696 | return fn(cpu, spapr, opcode, args); |
9fdf0c29 DG |
697 | } |
698 | } | |
699 | ||
700 | hcall_dprintf("Unimplemented hcall 0x" TARGET_FMT_lx "\n", opcode); | |
701 | return H_FUNCTION; | |
702 | } | |
f43e3525 | 703 | |
83f7d43a | 704 | static void hypercall_register_types(void) |
f43e3525 DG |
705 | { |
706 | /* hcall-pft */ | |
707 | spapr_register_hypercall(H_ENTER, h_enter); | |
708 | spapr_register_hypercall(H_REMOVE, h_remove); | |
709 | spapr_register_hypercall(H_PROTECT, h_protect); | |
6bbd5dde | 710 | spapr_register_hypercall(H_READ, h_read); |
39ac8455 | 711 | |
a3d0abae DG |
712 | /* hcall-bulk */ |
713 | spapr_register_hypercall(H_BULK_REMOVE, h_bulk_remove); | |
714 | ||
821303f5 DG |
715 | /* hcall-dabr */ |
716 | spapr_register_hypercall(H_SET_DABR, h_set_dabr); | |
717 | ||
ed120055 DG |
718 | /* hcall-splpar */ |
719 | spapr_register_hypercall(H_REGISTER_VPA, h_register_vpa); | |
720 | spapr_register_hypercall(H_CEDE, h_cede); | |
721 | ||
827200a2 DG |
722 | /* "debugger" hcalls (also used by SLOF). Note: We do -not- differenciate |
723 | * here between the "CI" and the "CACHE" variants, they will use whatever | |
724 | * mapping attributes qemu is using. When using KVM, the kernel will | |
725 | * enforce the attributes more strongly | |
726 | */ | |
727 | spapr_register_hypercall(H_LOGICAL_CI_LOAD, h_logical_load); | |
728 | spapr_register_hypercall(H_LOGICAL_CI_STORE, h_logical_store); | |
729 | spapr_register_hypercall(H_LOGICAL_CACHE_LOAD, h_logical_load); | |
730 | spapr_register_hypercall(H_LOGICAL_CACHE_STORE, h_logical_store); | |
731 | spapr_register_hypercall(H_LOGICAL_ICBI, h_logical_icbi); | |
732 | spapr_register_hypercall(H_LOGICAL_DCBF, h_logical_dcbf); | |
c73e3771 | 733 | spapr_register_hypercall(KVMPPC_H_LOGICAL_MEMOP, h_logical_memop); |
827200a2 | 734 | |
39ac8455 DG |
735 | /* qemu/KVM-PPC specific hcalls */ |
736 | spapr_register_hypercall(KVMPPC_H_RTAS, h_rtas); | |
f43e3525 | 737 | } |
83f7d43a AF |
738 | |
739 | type_init(hypercall_register_types) |