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