]>
Commit | Line | Data |
---|---|---|
8ef7f78e BS |
1 | /* |
2 | * S/390 memory access helper routines | |
3 | * | |
4 | * Copyright (c) 2009 Ulrich Hecht | |
5 | * Copyright (c) 2009 Alexander Graf | |
6 | * | |
7 | * This library is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2 of the License, or (at your option) any later version. | |
11 | * | |
12 | * This library is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
9615495a | 21 | #include "qemu/osdep.h" |
8ef7f78e | 22 | #include "cpu.h" |
2ef6175a | 23 | #include "exec/helper-proto.h" |
63c91552 | 24 | #include "exec/exec-all.h" |
f08b6170 | 25 | #include "exec/cpu_ldst.h" |
741da0d3 PB |
26 | |
27 | #if !defined(CONFIG_USER_ONLY) | |
0f5f6691 | 28 | #include "hw/s390x/storage-keys.h" |
741da0d3 | 29 | #endif |
8ef7f78e BS |
30 | |
31 | /*****************************************************************************/ | |
32 | /* Softmmu support */ | |
33 | #if !defined(CONFIG_USER_ONLY) | |
8ef7f78e | 34 | |
8ef7f78e BS |
35 | /* try to fill the TLB and return an exception if error. If retaddr is |
36 | NULL, it means that the function was called in C code (i.e. not | |
37 | from generated code or from helper.c) */ | |
38 | /* XXX: fix it to restore all registers */ | |
b35399bb SS |
39 | void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type, |
40 | int mmu_idx, uintptr_t retaddr) | |
8ef7f78e | 41 | { |
8ef7f78e BS |
42 | int ret; |
43 | ||
b35399bb | 44 | ret = s390_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx); |
8ef7f78e BS |
45 | if (unlikely(ret != 0)) { |
46 | if (likely(retaddr)) { | |
47 | /* now we have a real cpu fault */ | |
3f38f309 | 48 | cpu_restore_state(cs, retaddr); |
8ef7f78e | 49 | } |
5638d180 | 50 | cpu_loop_exit(cs); |
8ef7f78e | 51 | } |
8ef7f78e BS |
52 | } |
53 | ||
54 | #endif | |
55 | ||
56 | /* #define DEBUG_HELPER */ | |
57 | #ifdef DEBUG_HELPER | |
58 | #define HELPER_LOG(x...) qemu_log(x) | |
59 | #else | |
60 | #define HELPER_LOG(x...) | |
61 | #endif | |
62 | ||
d7ce6b7a AJ |
63 | /* Reduce the length so that addr + len doesn't cross a page boundary. */ |
64 | static inline uint64_t adj_len_to_page(uint64_t len, uint64_t addr) | |
65 | { | |
66 | #ifndef CONFIG_USER_ONLY | |
67 | if ((addr & ~TARGET_PAGE_MASK) + len - 1 >= TARGET_PAGE_SIZE) { | |
68 | return -addr & ~TARGET_PAGE_MASK; | |
69 | } | |
70 | #endif | |
71 | return len; | |
72 | } | |
73 | ||
fc89efe6 AJ |
74 | static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte, |
75 | uint32_t l) | |
8ef7f78e | 76 | { |
97ed5ccd | 77 | int mmu_idx = cpu_mmu_index(env, false); |
fc89efe6 AJ |
78 | |
79 | while (l > 0) { | |
80 | void *p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx); | |
81 | if (p) { | |
82 | /* Access to the whole page in write mode granted. */ | |
83 | int l_adj = adj_len_to_page(l, dest); | |
84 | memset(p, byte, l_adj); | |
85 | dest += l_adj; | |
86 | l -= l_adj; | |
87 | } else { | |
88 | /* We failed to get access to the whole page. The next write | |
89 | access will likely fill the QEMU TLB for the next iteration. */ | |
90 | cpu_stb_data(env, dest, byte); | |
91 | dest++; | |
92 | l--; | |
93 | } | |
8ef7f78e | 94 | } |
8ef7f78e BS |
95 | } |
96 | ||
6da528d1 AJ |
97 | static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src, |
98 | uint32_t l) | |
8ef7f78e | 99 | { |
97ed5ccd | 100 | int mmu_idx = cpu_mmu_index(env, false); |
8ef7f78e | 101 | |
6da528d1 AJ |
102 | while (l > 0) { |
103 | void *src_p = tlb_vaddr_to_host(env, src, MMU_DATA_LOAD, mmu_idx); | |
104 | void *dest_p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx); | |
105 | if (src_p && dest_p) { | |
106 | /* Access to both whole pages granted. */ | |
107 | int l_adj = adj_len_to_page(l, src); | |
108 | l_adj = adj_len_to_page(l_adj, dest); | |
109 | memmove(dest_p, src_p, l_adj); | |
110 | src += l_adj; | |
111 | dest += l_adj; | |
112 | l -= l_adj; | |
113 | } else { | |
114 | /* We failed to get access to one or both whole pages. The next | |
115 | read or write access will likely fill the QEMU TLB for the | |
116 | next iteration. */ | |
117 | cpu_stb_data(env, dest, cpu_ldub_data(env, src)); | |
118 | src++; | |
119 | dest++; | |
120 | l--; | |
121 | } | |
8ef7f78e | 122 | } |
8ef7f78e | 123 | } |
8ef7f78e BS |
124 | |
125 | /* and on array */ | |
19b0516f BS |
126 | uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest, |
127 | uint64_t src) | |
8ef7f78e BS |
128 | { |
129 | int i; | |
130 | unsigned char x; | |
131 | uint32_t cc = 0; | |
132 | ||
133 | HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", | |
134 | __func__, l, dest, src); | |
135 | for (i = 0; i <= l; i++) { | |
19b0516f | 136 | x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i); |
8ef7f78e BS |
137 | if (x) { |
138 | cc = 1; | |
139 | } | |
19b0516f | 140 | cpu_stb_data(env, dest + i, x); |
8ef7f78e BS |
141 | } |
142 | return cc; | |
143 | } | |
144 | ||
145 | /* xor on array */ | |
19b0516f BS |
146 | uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest, |
147 | uint64_t src) | |
8ef7f78e BS |
148 | { |
149 | int i; | |
150 | unsigned char x; | |
151 | uint32_t cc = 0; | |
152 | ||
153 | HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", | |
154 | __func__, l, dest, src); | |
155 | ||
8ef7f78e | 156 | /* xor with itself is the same as memset(0) */ |
8ef7f78e | 157 | if (src == dest) { |
fc89efe6 | 158 | fast_memset(env, dest, 0, l + 1); |
8ef7f78e BS |
159 | return 0; |
160 | } | |
8ef7f78e BS |
161 | |
162 | for (i = 0; i <= l; i++) { | |
19b0516f | 163 | x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i); |
8ef7f78e BS |
164 | if (x) { |
165 | cc = 1; | |
166 | } | |
19b0516f | 167 | cpu_stb_data(env, dest + i, x); |
8ef7f78e BS |
168 | } |
169 | return cc; | |
170 | } | |
171 | ||
172 | /* or on array */ | |
19b0516f BS |
173 | uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest, |
174 | uint64_t src) | |
8ef7f78e BS |
175 | { |
176 | int i; | |
177 | unsigned char x; | |
178 | uint32_t cc = 0; | |
179 | ||
180 | HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", | |
181 | __func__, l, dest, src); | |
182 | for (i = 0; i <= l; i++) { | |
19b0516f | 183 | x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i); |
8ef7f78e BS |
184 | if (x) { |
185 | cc = 1; | |
186 | } | |
19b0516f | 187 | cpu_stb_data(env, dest + i, x); |
8ef7f78e BS |
188 | } |
189 | return cc; | |
190 | } | |
191 | ||
192 | /* memmove */ | |
19b0516f | 193 | void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src) |
8ef7f78e BS |
194 | { |
195 | int i = 0; | |
8ef7f78e BS |
196 | |
197 | HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n", | |
198 | __func__, l, dest, src); | |
199 | ||
fc89efe6 AJ |
200 | /* mvc with source pointing to the byte after the destination is the |
201 | same as memset with the first source byte */ | |
202 | if (dest == (src + 1)) { | |
203 | fast_memset(env, dest, cpu_ldub_data(env, src), l + 1); | |
204 | return; | |
205 | } | |
6da528d1 | 206 | |
068593de | 207 | /* mvc and memmove do not behave the same when areas overlap! */ |
fc89efe6 | 208 | if ((dest < src) || (src + l < dest)) { |
6da528d1 | 209 | fast_memmove(env, dest, src, l + 1); |
8ef7f78e BS |
210 | return; |
211 | } | |
8ef7f78e | 212 | |
068593de | 213 | /* slow version with byte accesses which always work */ |
6da528d1 | 214 | for (i = 0; i <= l; i++) { |
19b0516f | 215 | cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i)); |
8ef7f78e BS |
216 | } |
217 | } | |
218 | ||
219 | /* compare unsigned byte arrays */ | |
19b0516f | 220 | uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2) |
8ef7f78e BS |
221 | { |
222 | int i; | |
223 | unsigned char x, y; | |
224 | uint32_t cc; | |
225 | ||
226 | HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n", | |
227 | __func__, l, s1, s2); | |
228 | for (i = 0; i <= l; i++) { | |
19b0516f BS |
229 | x = cpu_ldub_data(env, s1 + i); |
230 | y = cpu_ldub_data(env, s2 + i); | |
8ef7f78e BS |
231 | HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y); |
232 | if (x < y) { | |
233 | cc = 1; | |
234 | goto done; | |
235 | } else if (x > y) { | |
236 | cc = 2; | |
237 | goto done; | |
238 | } | |
239 | } | |
240 | cc = 0; | |
241 | done: | |
242 | HELPER_LOG("\n"); | |
243 | return cc; | |
244 | } | |
245 | ||
246 | /* compare logical under mask */ | |
19b0516f BS |
247 | uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask, |
248 | uint64_t addr) | |
8ef7f78e BS |
249 | { |
250 | uint8_t r, d; | |
251 | uint32_t cc; | |
252 | ||
253 | HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1, | |
254 | mask, addr); | |
255 | cc = 0; | |
256 | while (mask) { | |
257 | if (mask & 8) { | |
19b0516f | 258 | d = cpu_ldub_data(env, addr); |
8ef7f78e BS |
259 | r = (r1 & 0xff000000UL) >> 24; |
260 | HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d, | |
261 | addr); | |
262 | if (r < d) { | |
263 | cc = 1; | |
264 | break; | |
265 | } else if (r > d) { | |
266 | cc = 2; | |
267 | break; | |
268 | } | |
269 | addr++; | |
270 | } | |
271 | mask = (mask << 1) & 0xf; | |
272 | r1 <<= 8; | |
273 | } | |
274 | HELPER_LOG("\n"); | |
275 | return cc; | |
276 | } | |
277 | ||
aa31bf60 RH |
278 | static inline uint64_t fix_address(CPUS390XState *env, uint64_t a) |
279 | { | |
280 | /* 31-Bit mode */ | |
281 | if (!(env->psw.mask & PSW_MASK_64)) { | |
282 | a &= 0x7fffffff; | |
283 | } | |
284 | return a; | |
285 | } | |
286 | ||
19b0516f | 287 | static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2) |
8ef7f78e BS |
288 | { |
289 | uint64_t r = d2; | |
8ef7f78e BS |
290 | if (x2) { |
291 | r += env->regs[x2]; | |
292 | } | |
8ef7f78e BS |
293 | if (b2) { |
294 | r += env->regs[b2]; | |
295 | } | |
aa31bf60 | 296 | return fix_address(env, r); |
8ef7f78e BS |
297 | } |
298 | ||
19b0516f | 299 | static inline uint64_t get_address_31fix(CPUS390XState *env, int reg) |
8ef7f78e | 300 | { |
aa31bf60 | 301 | return fix_address(env, env->regs[reg]); |
8ef7f78e BS |
302 | } |
303 | ||
304 | /* search string (c is byte to search, r2 is string, r1 end of string) */ | |
4600c994 RH |
305 | uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end, |
306 | uint64_t str) | |
8ef7f78e | 307 | { |
4600c994 RH |
308 | uint32_t len; |
309 | uint8_t v, c = r0; | |
8ef7f78e | 310 | |
4600c994 RH |
311 | str = fix_address(env, str); |
312 | end = fix_address(env, end); | |
8ef7f78e | 313 | |
4600c994 RH |
314 | /* Assume for now that R2 is unmodified. */ |
315 | env->retxl = str; | |
316 | ||
317 | /* Lest we fail to service interrupts in a timely manner, limit the | |
e03ba136 | 318 | amount of work we're willing to do. For now, let's cap at 8k. */ |
4600c994 RH |
319 | for (len = 0; len < 0x2000; ++len) { |
320 | if (str + len == end) { | |
321 | /* Character not found. R1 & R2 are unmodified. */ | |
322 | env->cc_op = 2; | |
323 | return end; | |
324 | } | |
325 | v = cpu_ldub_data(env, str + len); | |
326 | if (v == c) { | |
327 | /* Character found. Set R1 to the location; R2 is unmodified. */ | |
328 | env->cc_op = 1; | |
329 | return str + len; | |
8ef7f78e BS |
330 | } |
331 | } | |
332 | ||
4600c994 RH |
333 | /* CPU-determined bytes processed. Advance R2 to next byte to process. */ |
334 | env->retxl = str + len; | |
335 | env->cc_op = 3; | |
336 | return end; | |
8ef7f78e BS |
337 | } |
338 | ||
339 | /* unsigned string compare (c is string terminator) */ | |
aa31bf60 | 340 | uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2) |
8ef7f78e | 341 | { |
aa31bf60 | 342 | uint32_t len; |
8ef7f78e BS |
343 | |
344 | c = c & 0xff; | |
aa31bf60 RH |
345 | s1 = fix_address(env, s1); |
346 | s2 = fix_address(env, s2); | |
347 | ||
348 | /* Lest we fail to service interrupts in a timely manner, limit the | |
e03ba136 | 349 | amount of work we're willing to do. For now, let's cap at 8k. */ |
aa31bf60 RH |
350 | for (len = 0; len < 0x2000; ++len) { |
351 | uint8_t v1 = cpu_ldub_data(env, s1 + len); | |
352 | uint8_t v2 = cpu_ldub_data(env, s2 + len); | |
353 | if (v1 == v2) { | |
354 | if (v1 == c) { | |
355 | /* Equal. CC=0, and don't advance the registers. */ | |
356 | env->cc_op = 0; | |
357 | env->retxl = s2; | |
358 | return s1; | |
359 | } | |
360 | } else { | |
361 | /* Unequal. CC={1,2}, and advance the registers. Note that | |
362 | the terminator need not be zero, but the string that contains | |
363 | the terminator is by definition "low". */ | |
364 | env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2); | |
365 | env->retxl = s2 + len; | |
366 | return s1 + len; | |
8ef7f78e | 367 | } |
8ef7f78e BS |
368 | } |
369 | ||
aa31bf60 RH |
370 | /* CPU-determined bytes equal; advance the registers. */ |
371 | env->cc_op = 3; | |
372 | env->retxl = s2 + len; | |
373 | return s1 + len; | |
8ef7f78e BS |
374 | } |
375 | ||
376 | /* move page */ | |
19b0516f | 377 | void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2) |
8ef7f78e BS |
378 | { |
379 | /* XXX missing r0 handling */ | |
ee6c38d5 | 380 | env->cc_op = 0; |
6da528d1 | 381 | fast_memmove(env, r1, r2, TARGET_PAGE_SIZE); |
8ef7f78e BS |
382 | } |
383 | ||
384 | /* string copy (c is string terminator) */ | |
aa31bf60 | 385 | uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s) |
8ef7f78e | 386 | { |
aa31bf60 | 387 | uint32_t len; |
8ef7f78e BS |
388 | |
389 | c = c & 0xff; | |
aa31bf60 RH |
390 | d = fix_address(env, d); |
391 | s = fix_address(env, s); | |
392 | ||
393 | /* Lest we fail to service interrupts in a timely manner, limit the | |
e03ba136 | 394 | amount of work we're willing to do. For now, let's cap at 8k. */ |
aa31bf60 RH |
395 | for (len = 0; len < 0x2000; ++len) { |
396 | uint8_t v = cpu_ldub_data(env, s + len); | |
397 | cpu_stb_data(env, d + len, v); | |
8ef7f78e | 398 | if (v == c) { |
aa31bf60 RH |
399 | /* Complete. Set CC=1 and advance R1. */ |
400 | env->cc_op = 1; | |
401 | env->retxl = s; | |
402 | return d + len; | |
8ef7f78e | 403 | } |
8ef7f78e | 404 | } |
aa31bf60 RH |
405 | |
406 | /* Incomplete. Set CC=3 and signal to advance R1 and R2. */ | |
407 | env->cc_op = 3; | |
408 | env->retxl = s + len; | |
409 | return d + len; | |
8ef7f78e BS |
410 | } |
411 | ||
19b0516f BS |
412 | static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address, |
413 | uint32_t mask) | |
8ef7f78e BS |
414 | { |
415 | int pos = 24; /* top of the lower half of r1 */ | |
416 | uint64_t rmask = 0xff000000ULL; | |
417 | uint8_t val = 0; | |
418 | int ccd = 0; | |
419 | uint32_t cc = 0; | |
420 | ||
421 | while (mask) { | |
422 | if (mask & 8) { | |
423 | env->regs[r1] &= ~rmask; | |
19b0516f | 424 | val = cpu_ldub_data(env, address); |
8ef7f78e BS |
425 | if ((val & 0x80) && !ccd) { |
426 | cc = 1; | |
427 | } | |
428 | ccd = 1; | |
429 | if (val && cc == 0) { | |
430 | cc = 2; | |
431 | } | |
432 | env->regs[r1] |= (uint64_t)val << pos; | |
433 | address++; | |
434 | } | |
435 | mask = (mask << 1) & 0xf; | |
436 | pos -= 8; | |
437 | rmask >>= 8; | |
438 | } | |
439 | ||
440 | return cc; | |
441 | } | |
442 | ||
443 | /* execute instruction | |
444 | this instruction executes an insn modified with the contents of r1 | |
445 | it does not change the executed instruction in memory | |
446 | it does not change the program counter | |
447 | in other words: tricky... | |
448 | currently implemented by interpreting the cases it is most commonly used in | |
449 | */ | |
19b0516f BS |
450 | uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1, |
451 | uint64_t addr, uint64_t ret) | |
8ef7f78e | 452 | { |
a47dddd7 | 453 | S390CPU *cpu = s390_env_get_cpu(env); |
19b0516f | 454 | uint16_t insn = cpu_lduw_code(env, addr); |
8ef7f78e BS |
455 | |
456 | HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr, | |
457 | insn); | |
458 | if ((insn & 0xf0ff) == 0xd000) { | |
459 | uint32_t l, insn2, b1, b2, d1, d2; | |
460 | ||
461 | l = v1 & 0xff; | |
19b0516f | 462 | insn2 = cpu_ldl_code(env, addr + 2); |
8ef7f78e BS |
463 | b1 = (insn2 >> 28) & 0xf; |
464 | b2 = (insn2 >> 12) & 0xf; | |
465 | d1 = (insn2 >> 16) & 0xfff; | |
466 | d2 = insn2 & 0xfff; | |
467 | switch (insn & 0xf00) { | |
468 | case 0x200: | |
19b0516f BS |
469 | helper_mvc(env, l, get_address(env, 0, b1, d1), |
470 | get_address(env, 0, b2, d2)); | |
8ef7f78e | 471 | break; |
8cf02f93 PB |
472 | case 0x400: |
473 | cc = helper_nc(env, l, get_address(env, 0, b1, d1), | |
474 | get_address(env, 0, b2, d2)); | |
475 | break; | |
8ef7f78e | 476 | case 0x500: |
19b0516f BS |
477 | cc = helper_clc(env, l, get_address(env, 0, b1, d1), |
478 | get_address(env, 0, b2, d2)); | |
8ef7f78e | 479 | break; |
8cf02f93 PB |
480 | case 0x600: |
481 | cc = helper_oc(env, l, get_address(env, 0, b1, d1), | |
482 | get_address(env, 0, b2, d2)); | |
483 | break; | |
8ef7f78e | 484 | case 0x700: |
19b0516f BS |
485 | cc = helper_xc(env, l, get_address(env, 0, b1, d1), |
486 | get_address(env, 0, b2, d2)); | |
8ef7f78e BS |
487 | break; |
488 | case 0xc00: | |
19b0516f BS |
489 | helper_tr(env, l, get_address(env, 0, b1, d1), |
490 | get_address(env, 0, b2, d2)); | |
c9c19b49 | 491 | break; |
54f00775 AJ |
492 | case 0xd00: |
493 | cc = helper_trt(env, l, get_address(env, 0, b1, d1), | |
494 | get_address(env, 0, b2, d2)); | |
8ef7f78e BS |
495 | break; |
496 | default: | |
497 | goto abort; | |
8ef7f78e BS |
498 | } |
499 | } else if ((insn & 0xff00) == 0x0a00) { | |
500 | /* supervisor call */ | |
501 | HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff); | |
502 | env->psw.addr = ret - 4; | |
503 | env->int_svc_code = (insn | v1) & 0xff; | |
d5a103cd | 504 | env->int_svc_ilen = 4; |
089f5c06 | 505 | helper_exception(env, EXCP_SVC); |
8ef7f78e BS |
506 | } else if ((insn & 0xff00) == 0xbf00) { |
507 | uint32_t insn2, r1, r3, b2, d2; | |
508 | ||
19b0516f | 509 | insn2 = cpu_ldl_code(env, addr + 2); |
8ef7f78e BS |
510 | r1 = (insn2 >> 20) & 0xf; |
511 | r3 = (insn2 >> 16) & 0xf; | |
512 | b2 = (insn2 >> 12) & 0xf; | |
513 | d2 = insn2 & 0xfff; | |
19b0516f | 514 | cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3); |
8ef7f78e BS |
515 | } else { |
516 | abort: | |
a47dddd7 | 517 | cpu_abort(CPU(cpu), "EXECUTE on instruction prefix 0x%x not implemented\n", |
8ef7f78e BS |
518 | insn); |
519 | } | |
520 | return cc; | |
521 | } | |
522 | ||
8ef7f78e | 523 | /* load access registers r1 to r3 from memory at a2 */ |
19b0516f | 524 | void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) |
8ef7f78e BS |
525 | { |
526 | int i; | |
527 | ||
528 | for (i = r1;; i = (i + 1) % 16) { | |
19b0516f | 529 | env->aregs[i] = cpu_ldl_data(env, a2); |
8ef7f78e BS |
530 | a2 += 4; |
531 | ||
532 | if (i == r3) { | |
533 | break; | |
534 | } | |
535 | } | |
536 | } | |
537 | ||
538 | /* store access registers r1 to r3 in memory at a2 */ | |
19b0516f | 539 | void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) |
8ef7f78e BS |
540 | { |
541 | int i; | |
542 | ||
543 | for (i = r1;; i = (i + 1) % 16) { | |
19b0516f | 544 | cpu_stl_data(env, a2, env->aregs[i]); |
8ef7f78e BS |
545 | a2 += 4; |
546 | ||
547 | if (i == r3) { | |
548 | break; | |
549 | } | |
550 | } | |
551 | } | |
552 | ||
553 | /* move long */ | |
19b0516f | 554 | uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2) |
8ef7f78e BS |
555 | { |
556 | uint64_t destlen = env->regs[r1 + 1] & 0xffffff; | |
19b0516f | 557 | uint64_t dest = get_address_31fix(env, r1); |
8ef7f78e | 558 | uint64_t srclen = env->regs[r2 + 1] & 0xffffff; |
19b0516f | 559 | uint64_t src = get_address_31fix(env, r2); |
b5edcddd | 560 | uint8_t pad = env->regs[r2 + 1] >> 24; |
8ef7f78e BS |
561 | uint8_t v; |
562 | uint32_t cc; | |
563 | ||
564 | if (destlen == srclen) { | |
565 | cc = 0; | |
566 | } else if (destlen < srclen) { | |
567 | cc = 1; | |
568 | } else { | |
569 | cc = 2; | |
570 | } | |
571 | ||
572 | if (srclen > destlen) { | |
573 | srclen = destlen; | |
574 | } | |
575 | ||
576 | for (; destlen && srclen; src++, dest++, destlen--, srclen--) { | |
19b0516f BS |
577 | v = cpu_ldub_data(env, src); |
578 | cpu_stb_data(env, dest, v); | |
8ef7f78e BS |
579 | } |
580 | ||
581 | for (; destlen; dest++, destlen--) { | |
19b0516f | 582 | cpu_stb_data(env, dest, pad); |
8ef7f78e BS |
583 | } |
584 | ||
585 | env->regs[r1 + 1] = destlen; | |
586 | /* can't use srclen here, we trunc'ed it */ | |
587 | env->regs[r2 + 1] -= src - env->regs[r2]; | |
588 | env->regs[r1] = dest; | |
589 | env->regs[r2] = src; | |
590 | ||
591 | return cc; | |
592 | } | |
593 | ||
594 | /* move long extended another memcopy insn with more bells and whistles */ | |
19b0516f BS |
595 | uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2, |
596 | uint32_t r3) | |
8ef7f78e BS |
597 | { |
598 | uint64_t destlen = env->regs[r1 + 1]; | |
599 | uint64_t dest = env->regs[r1]; | |
600 | uint64_t srclen = env->regs[r3 + 1]; | |
601 | uint64_t src = env->regs[r3]; | |
602 | uint8_t pad = a2 & 0xff; | |
603 | uint8_t v; | |
604 | uint32_t cc; | |
605 | ||
606 | if (!(env->psw.mask & PSW_MASK_64)) { | |
607 | destlen = (uint32_t)destlen; | |
608 | srclen = (uint32_t)srclen; | |
609 | dest &= 0x7fffffff; | |
610 | src &= 0x7fffffff; | |
611 | } | |
612 | ||
613 | if (destlen == srclen) { | |
614 | cc = 0; | |
615 | } else if (destlen < srclen) { | |
616 | cc = 1; | |
617 | } else { | |
618 | cc = 2; | |
619 | } | |
620 | ||
621 | if (srclen > destlen) { | |
622 | srclen = destlen; | |
623 | } | |
624 | ||
625 | for (; destlen && srclen; src++, dest++, destlen--, srclen--) { | |
19b0516f BS |
626 | v = cpu_ldub_data(env, src); |
627 | cpu_stb_data(env, dest, v); | |
8ef7f78e BS |
628 | } |
629 | ||
630 | for (; destlen; dest++, destlen--) { | |
19b0516f | 631 | cpu_stb_data(env, dest, pad); |
8ef7f78e BS |
632 | } |
633 | ||
634 | env->regs[r1 + 1] = destlen; | |
635 | /* can't use srclen here, we trunc'ed it */ | |
636 | /* FIXME: 31-bit mode! */ | |
637 | env->regs[r3 + 1] -= src - env->regs[r3]; | |
638 | env->regs[r1] = dest; | |
639 | env->regs[r3] = src; | |
640 | ||
641 | return cc; | |
642 | } | |
643 | ||
644 | /* compare logical long extended memcompare insn with padding */ | |
19b0516f BS |
645 | uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2, |
646 | uint32_t r3) | |
8ef7f78e BS |
647 | { |
648 | uint64_t destlen = env->regs[r1 + 1]; | |
19b0516f | 649 | uint64_t dest = get_address_31fix(env, r1); |
8ef7f78e | 650 | uint64_t srclen = env->regs[r3 + 1]; |
19b0516f | 651 | uint64_t src = get_address_31fix(env, r3); |
8ef7f78e BS |
652 | uint8_t pad = a2 & 0xff; |
653 | uint8_t v1 = 0, v2 = 0; | |
654 | uint32_t cc = 0; | |
655 | ||
656 | if (!(destlen || srclen)) { | |
657 | return cc; | |
658 | } | |
659 | ||
660 | if (srclen > destlen) { | |
661 | srclen = destlen; | |
662 | } | |
663 | ||
664 | for (; destlen || srclen; src++, dest++, destlen--, srclen--) { | |
19b0516f BS |
665 | v1 = srclen ? cpu_ldub_data(env, src) : pad; |
666 | v2 = destlen ? cpu_ldub_data(env, dest) : pad; | |
8ef7f78e BS |
667 | if (v1 != v2) { |
668 | cc = (v1 < v2) ? 1 : 2; | |
669 | break; | |
670 | } | |
671 | } | |
672 | ||
673 | env->regs[r1 + 1] = destlen; | |
674 | /* can't use srclen here, we trunc'ed it */ | |
675 | env->regs[r3 + 1] -= src - env->regs[r3]; | |
676 | env->regs[r1] = dest; | |
677 | env->regs[r3] = src; | |
678 | ||
679 | return cc; | |
680 | } | |
681 | ||
682 | /* checksum */ | |
374724f9 RH |
683 | uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1, |
684 | uint64_t src, uint64_t src_len) | |
8ef7f78e | 685 | { |
374724f9 RH |
686 | uint64_t max_len, len; |
687 | uint64_t cksm = (uint32_t)r1; | |
8ef7f78e | 688 | |
374724f9 | 689 | /* Lest we fail to service interrupts in a timely manner, limit the |
e03ba136 | 690 | amount of work we're willing to do. For now, let's cap at 8k. */ |
374724f9 | 691 | max_len = (src_len > 0x2000 ? 0x2000 : src_len); |
8ef7f78e | 692 | |
374724f9 RH |
693 | /* Process full words as available. */ |
694 | for (len = 0; len + 4 <= max_len; len += 4, src += 4) { | |
695 | cksm += (uint32_t)cpu_ldl_data(env, src); | |
8ef7f78e BS |
696 | } |
697 | ||
374724f9 | 698 | switch (max_len - len) { |
8ef7f78e | 699 | case 1: |
19b0516f | 700 | cksm += cpu_ldub_data(env, src) << 24; |
374724f9 | 701 | len += 1; |
8ef7f78e BS |
702 | break; |
703 | case 2: | |
19b0516f | 704 | cksm += cpu_lduw_data(env, src) << 16; |
374724f9 | 705 | len += 2; |
8ef7f78e BS |
706 | break; |
707 | case 3: | |
19b0516f BS |
708 | cksm += cpu_lduw_data(env, src) << 16; |
709 | cksm += cpu_ldub_data(env, src + 2) << 8; | |
374724f9 | 710 | len += 3; |
8ef7f78e BS |
711 | break; |
712 | } | |
713 | ||
374724f9 RH |
714 | /* Fold the carry from the checksum. Note that we can see carry-out |
715 | during folding more than once (but probably not more than twice). */ | |
716 | while (cksm > 0xffffffffull) { | |
717 | cksm = (uint32_t)cksm + (cksm >> 32); | |
718 | } | |
719 | ||
720 | /* Indicate whether or not we've processed everything. */ | |
721 | env->cc_op = (len == src_len ? 0 : 3); | |
8ef7f78e | 722 | |
374724f9 RH |
723 | /* Return both cksm and processed length. */ |
724 | env->retxl = cksm; | |
725 | return len; | |
8ef7f78e BS |
726 | } |
727 | ||
19b0516f BS |
728 | void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest, |
729 | uint64_t src) | |
8ef7f78e BS |
730 | { |
731 | int len_dest = len >> 4; | |
732 | int len_src = len & 0xf; | |
733 | uint8_t b; | |
734 | int second_nibble = 0; | |
735 | ||
736 | dest += len_dest; | |
737 | src += len_src; | |
738 | ||
739 | /* last byte is special, it only flips the nibbles */ | |
19b0516f BS |
740 | b = cpu_ldub_data(env, src); |
741 | cpu_stb_data(env, dest, (b << 4) | (b >> 4)); | |
8ef7f78e BS |
742 | src--; |
743 | len_src--; | |
744 | ||
745 | /* now pad every nibble with 0xf0 */ | |
746 | ||
747 | while (len_dest > 0) { | |
748 | uint8_t cur_byte = 0; | |
749 | ||
750 | if (len_src > 0) { | |
19b0516f | 751 | cur_byte = cpu_ldub_data(env, src); |
8ef7f78e BS |
752 | } |
753 | ||
754 | len_dest--; | |
755 | dest--; | |
756 | ||
757 | /* only advance one nibble at a time */ | |
758 | if (second_nibble) { | |
759 | cur_byte >>= 4; | |
760 | len_src--; | |
761 | src--; | |
762 | } | |
763 | second_nibble = !second_nibble; | |
764 | ||
765 | /* digit */ | |
766 | cur_byte = (cur_byte & 0xf); | |
767 | /* zone bits */ | |
768 | cur_byte |= 0xf0; | |
769 | ||
19b0516f | 770 | cpu_stb_data(env, dest, cur_byte); |
8ef7f78e BS |
771 | } |
772 | } | |
773 | ||
19b0516f BS |
774 | void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array, |
775 | uint64_t trans) | |
8ef7f78e BS |
776 | { |
777 | int i; | |
778 | ||
779 | for (i = 0; i <= len; i++) { | |
19b0516f BS |
780 | uint8_t byte = cpu_ldub_data(env, array + i); |
781 | uint8_t new_byte = cpu_ldub_data(env, trans + byte); | |
8ef7f78e | 782 | |
19b0516f | 783 | cpu_stb_data(env, array + i, new_byte); |
8ef7f78e BS |
784 | } |
785 | } | |
786 | ||
3f4de675 AJ |
787 | uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array, |
788 | uint64_t len, uint64_t trans) | |
789 | { | |
790 | uint8_t end = env->regs[0] & 0xff; | |
791 | uint64_t l = len; | |
792 | uint64_t i; | |
793 | ||
794 | if (!(env->psw.mask & PSW_MASK_64)) { | |
795 | array &= 0x7fffffff; | |
796 | l = (uint32_t)l; | |
797 | } | |
798 | ||
799 | /* Lest we fail to service interrupts in a timely manner, limit the | |
800 | amount of work we're willing to do. For now, let's cap at 8k. */ | |
801 | if (l > 0x2000) { | |
802 | l = 0x2000; | |
803 | env->cc_op = 3; | |
804 | } else { | |
805 | env->cc_op = 0; | |
806 | } | |
807 | ||
808 | for (i = 0; i < l; i++) { | |
809 | uint8_t byte, new_byte; | |
810 | ||
811 | byte = cpu_ldub_data(env, array + i); | |
812 | ||
813 | if (byte == end) { | |
814 | env->cc_op = 1; | |
815 | break; | |
816 | } | |
817 | ||
818 | new_byte = cpu_ldub_data(env, trans + byte); | |
819 | cpu_stb_data(env, array + i, new_byte); | |
820 | } | |
821 | ||
822 | env->retxl = len - i; | |
823 | return array + i; | |
824 | } | |
825 | ||
54f00775 AJ |
826 | uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array, |
827 | uint64_t trans) | |
828 | { | |
829 | uint32_t cc = 0; | |
830 | int i; | |
831 | ||
832 | for (i = 0; i <= len; i++) { | |
833 | uint8_t byte = cpu_ldub_data(env, array + i); | |
834 | uint8_t sbyte = cpu_ldub_data(env, trans + byte); | |
835 | ||
836 | if (sbyte != 0) { | |
837 | env->regs[1] = array + i; | |
838 | env->regs[2] = (env->regs[2] & ~0xff) | sbyte; | |
839 | cc = (i == len) ? 2 : 1; | |
840 | break; | |
841 | } | |
842 | } | |
843 | ||
844 | return cc; | |
845 | } | |
846 | ||
8ef7f78e | 847 | #if !defined(CONFIG_USER_ONLY) |
19b0516f | 848 | void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) |
8ef7f78e | 849 | { |
00c8cb0a | 850 | S390CPU *cpu = s390_env_get_cpu(env); |
311918b9 | 851 | bool PERchanged = false; |
8ef7f78e BS |
852 | int i; |
853 | uint64_t src = a2; | |
311918b9 | 854 | uint64_t val; |
8ef7f78e BS |
855 | |
856 | for (i = r1;; i = (i + 1) % 16) { | |
311918b9 AJ |
857 | val = cpu_ldq_data(env, src); |
858 | if (env->cregs[i] != val && i >= 9 && i <= 11) { | |
859 | PERchanged = true; | |
860 | } | |
861 | env->cregs[i] = val; | |
8ef7f78e BS |
862 | HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n", |
863 | i, src, env->cregs[i]); | |
864 | src += sizeof(uint64_t); | |
865 | ||
866 | if (i == r3) { | |
867 | break; | |
868 | } | |
869 | } | |
870 | ||
311918b9 AJ |
871 | if (PERchanged && env->psw.mask & PSW_MASK_PER) { |
872 | s390_cpu_recompute_watchpoints(CPU(cpu)); | |
873 | } | |
874 | ||
d10eb08f | 875 | tlb_flush(CPU(cpu)); |
8ef7f78e BS |
876 | } |
877 | ||
19b0516f | 878 | void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) |
8ef7f78e | 879 | { |
00c8cb0a | 880 | S390CPU *cpu = s390_env_get_cpu(env); |
311918b9 | 881 | bool PERchanged = false; |
8ef7f78e BS |
882 | int i; |
883 | uint64_t src = a2; | |
311918b9 | 884 | uint32_t val; |
8ef7f78e BS |
885 | |
886 | for (i = r1;; i = (i + 1) % 16) { | |
311918b9 AJ |
887 | val = cpu_ldl_data(env, src); |
888 | if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) { | |
889 | PERchanged = true; | |
890 | } | |
891 | env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) | val; | |
8ef7f78e BS |
892 | src += sizeof(uint32_t); |
893 | ||
894 | if (i == r3) { | |
895 | break; | |
896 | } | |
897 | } | |
898 | ||
311918b9 AJ |
899 | if (PERchanged && env->psw.mask & PSW_MASK_PER) { |
900 | s390_cpu_recompute_watchpoints(CPU(cpu)); | |
901 | } | |
902 | ||
d10eb08f | 903 | tlb_flush(CPU(cpu)); |
8ef7f78e BS |
904 | } |
905 | ||
19b0516f | 906 | void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) |
8ef7f78e BS |
907 | { |
908 | int i; | |
909 | uint64_t dest = a2; | |
910 | ||
911 | for (i = r1;; i = (i + 1) % 16) { | |
19b0516f | 912 | cpu_stq_data(env, dest, env->cregs[i]); |
8ef7f78e BS |
913 | dest += sizeof(uint64_t); |
914 | ||
915 | if (i == r3) { | |
916 | break; | |
917 | } | |
918 | } | |
919 | } | |
920 | ||
19b0516f | 921 | void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3) |
8ef7f78e BS |
922 | { |
923 | int i; | |
924 | uint64_t dest = a2; | |
925 | ||
926 | for (i = r1;; i = (i + 1) % 16) { | |
19b0516f | 927 | cpu_stl_data(env, dest, env->cregs[i]); |
8ef7f78e BS |
928 | dest += sizeof(uint32_t); |
929 | ||
930 | if (i == r3) { | |
931 | break; | |
932 | } | |
933 | } | |
934 | } | |
935 | ||
936 | uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2) | |
937 | { | |
938 | /* XXX implement */ | |
939 | ||
940 | return 0; | |
941 | } | |
942 | ||
943 | /* insert storage key extended */ | |
19b0516f | 944 | uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2) |
8ef7f78e | 945 | { |
0f5f6691 JH |
946 | static S390SKeysState *ss; |
947 | static S390SKeysClass *skeyclass; | |
19b0516f | 948 | uint64_t addr = get_address(env, 0, 0, r2); |
0f5f6691 | 949 | uint8_t key; |
8ef7f78e BS |
950 | |
951 | if (addr > ram_size) { | |
952 | return 0; | |
953 | } | |
954 | ||
0f5f6691 JH |
955 | if (unlikely(!ss)) { |
956 | ss = s390_get_skeys_device(); | |
957 | skeyclass = S390_SKEYS_GET_CLASS(ss); | |
958 | } | |
959 | ||
960 | if (skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key)) { | |
961 | return 0; | |
962 | } | |
963 | return key; | |
8ef7f78e BS |
964 | } |
965 | ||
966 | /* set storage key extended */ | |
2bbde27f | 967 | void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2) |
8ef7f78e | 968 | { |
0f5f6691 JH |
969 | static S390SKeysState *ss; |
970 | static S390SKeysClass *skeyclass; | |
19b0516f | 971 | uint64_t addr = get_address(env, 0, 0, r2); |
0f5f6691 | 972 | uint8_t key; |
8ef7f78e BS |
973 | |
974 | if (addr > ram_size) { | |
975 | return; | |
976 | } | |
977 | ||
0f5f6691 JH |
978 | if (unlikely(!ss)) { |
979 | ss = s390_get_skeys_device(); | |
980 | skeyclass = S390_SKEYS_GET_CLASS(ss); | |
981 | } | |
982 | ||
983 | key = (uint8_t) r1; | |
984 | skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key); | |
8ef7f78e BS |
985 | } |
986 | ||
987 | /* reset reference bit extended */ | |
5cc69c54 | 988 | uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2) |
8ef7f78e | 989 | { |
0f5f6691 JH |
990 | static S390SKeysState *ss; |
991 | static S390SKeysClass *skeyclass; | |
992 | uint8_t re, key; | |
8ef7f78e BS |
993 | |
994 | if (r2 > ram_size) { | |
995 | return 0; | |
996 | } | |
997 | ||
0f5f6691 JH |
998 | if (unlikely(!ss)) { |
999 | ss = s390_get_skeys_device(); | |
1000 | skeyclass = S390_SKEYS_GET_CLASS(ss); | |
1001 | } | |
1002 | ||
1003 | if (skeyclass->get_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) { | |
1004 | return 0; | |
1005 | } | |
1006 | ||
8ef7f78e | 1007 | re = key & (SK_R | SK_C); |
0f5f6691 JH |
1008 | key &= ~SK_R; |
1009 | ||
1010 | if (skeyclass->set_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) { | |
1011 | return 0; | |
1012 | } | |
8ef7f78e BS |
1013 | |
1014 | /* | |
1015 | * cc | |
1016 | * | |
1017 | * 0 Reference bit zero; change bit zero | |
1018 | * 1 Reference bit zero; change bit one | |
1019 | * 2 Reference bit one; change bit zero | |
1020 | * 3 Reference bit one; change bit one | |
1021 | */ | |
1022 | ||
1023 | return re >> 1; | |
1024 | } | |
1025 | ||
1026 | /* compare and swap and purge */ | |
3d596f49 | 1027 | uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2) |
8ef7f78e | 1028 | { |
00c8cb0a | 1029 | S390CPU *cpu = s390_env_get_cpu(env); |
8ef7f78e BS |
1030 | uint32_t cc; |
1031 | uint32_t o1 = env->regs[r1]; | |
3d596f49 | 1032 | uint64_t a2 = r2 & ~3ULL; |
19b0516f | 1033 | uint32_t o2 = cpu_ldl_data(env, a2); |
8ef7f78e BS |
1034 | |
1035 | if (o1 == o2) { | |
19b0516f | 1036 | cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]); |
3d596f49 | 1037 | if (r2 & 0x3) { |
8ef7f78e | 1038 | /* flush TLB / ALB */ |
d10eb08f | 1039 | tlb_flush(CPU(cpu)); |
8ef7f78e BS |
1040 | } |
1041 | cc = 0; | |
1042 | } else { | |
1043 | env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2; | |
1044 | cc = 1; | |
1045 | } | |
1046 | ||
1047 | return cc; | |
1048 | } | |
1049 | ||
a3084e80 | 1050 | uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) |
8ef7f78e | 1051 | { |
a3084e80 | 1052 | int cc = 0, i; |
8ef7f78e | 1053 | |
a3084e80 AJ |
1054 | HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", |
1055 | __func__, l, a1, a2); | |
1056 | ||
1057 | if (l > 256) { | |
8ef7f78e BS |
1058 | /* max 256 */ |
1059 | l = 256; | |
1060 | cc = 3; | |
1061 | } | |
1062 | ||
8ef7f78e BS |
1063 | /* XXX replace w/ memcpy */ |
1064 | for (i = 0; i < l; i++) { | |
a3084e80 | 1065 | cpu_stb_secondary(env, a1 + i, cpu_ldub_primary(env, a2 + i)); |
8ef7f78e BS |
1066 | } |
1067 | ||
1068 | return cc; | |
1069 | } | |
1070 | ||
a3084e80 | 1071 | uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2) |
8ef7f78e | 1072 | { |
a3084e80 AJ |
1073 | int cc = 0, i; |
1074 | ||
8ef7f78e BS |
1075 | HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n", |
1076 | __func__, l, a1, a2); | |
1077 | ||
a3084e80 AJ |
1078 | if (l > 256) { |
1079 | /* max 256 */ | |
1080 | l = 256; | |
1081 | cc = 3; | |
1082 | } | |
8ef7f78e | 1083 | |
a3084e80 AJ |
1084 | /* XXX replace w/ memcpy */ |
1085 | for (i = 0; i < l; i++) { | |
1086 | cpu_stb_primary(env, a1 + i, cpu_ldub_secondary(env, a2 + i)); | |
1087 | } | |
8ef7f78e | 1088 | |
a3084e80 | 1089 | return cc; |
8ef7f78e BS |
1090 | } |
1091 | ||
1092 | /* invalidate pte */ | |
19b0516f | 1093 | void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr) |
8ef7f78e | 1094 | { |
2efc6be2 | 1095 | CPUState *cs = CPU(s390_env_get_cpu(env)); |
8ef7f78e BS |
1096 | uint64_t page = vaddr & TARGET_PAGE_MASK; |
1097 | uint64_t pte = 0; | |
1098 | ||
1099 | /* XXX broadcast to other CPUs */ | |
1100 | ||
1101 | /* XXX Linux is nice enough to give us the exact pte address. | |
1102 | According to spec we'd have to find it out ourselves */ | |
1103 | /* XXX Linux is fine with overwriting the pte, the spec requires | |
1104 | us to only set the invalid bit */ | |
f606604f | 1105 | stq_phys(cs->as, pte_addr, pte | _PAGE_INVALID); |
8ef7f78e BS |
1106 | |
1107 | /* XXX we exploit the fact that Linux passes the exact virtual | |
1108 | address here - it's not obliged to! */ | |
31b030d4 | 1109 | tlb_flush_page(cs, page); |
8ef7f78e BS |
1110 | |
1111 | /* XXX 31-bit hack */ | |
1112 | if (page & 0x80000000) { | |
31b030d4 | 1113 | tlb_flush_page(cs, page & ~0x80000000); |
8ef7f78e | 1114 | } else { |
31b030d4 | 1115 | tlb_flush_page(cs, page | 0x80000000); |
8ef7f78e BS |
1116 | } |
1117 | } | |
1118 | ||
1119 | /* flush local tlb */ | |
19b0516f | 1120 | void HELPER(ptlb)(CPUS390XState *env) |
8ef7f78e | 1121 | { |
00c8cb0a AF |
1122 | S390CPU *cpu = s390_env_get_cpu(env); |
1123 | ||
d10eb08f | 1124 | tlb_flush(CPU(cpu)); |
8ef7f78e BS |
1125 | } |
1126 | ||
9c3fd85b RH |
1127 | /* load using real address */ |
1128 | uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr) | |
1129 | { | |
1130 | CPUState *cs = CPU(s390_env_get_cpu(env)); | |
1131 | ||
1132 | return (uint32_t)ldl_phys(cs->as, get_address(env, 0, 0, addr)); | |
1133 | } | |
1134 | ||
1135 | uint64_t HELPER(lurag)(CPUS390XState *env, uint64_t addr) | |
1136 | { | |
1137 | CPUState *cs = CPU(s390_env_get_cpu(env)); | |
1138 | ||
1139 | return ldq_phys(cs->as, get_address(env, 0, 0, addr)); | |
1140 | } | |
1141 | ||
8ef7f78e | 1142 | /* store using real address */ |
204504e2 | 1143 | void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1) |
8ef7f78e | 1144 | { |
2efc6be2 AF |
1145 | CPUState *cs = CPU(s390_env_get_cpu(env)); |
1146 | ||
81822c2f | 1147 | stl_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1); |
2f543949 AJ |
1148 | |
1149 | if ((env->psw.mask & PSW_MASK_PER) && | |
1150 | (env->cregs[9] & PER_CR9_EVENT_STORE) && | |
1151 | (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { | |
1152 | /* PSW is saved just before calling the helper. */ | |
1153 | env->per_address = env->psw.addr; | |
1154 | env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); | |
1155 | } | |
8ef7f78e BS |
1156 | } |
1157 | ||
9c3fd85b RH |
1158 | void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1) |
1159 | { | |
1160 | CPUState *cs = CPU(s390_env_get_cpu(env)); | |
1161 | ||
1162 | stq_phys(cs->as, get_address(env, 0, 0, addr), v1); | |
2f543949 AJ |
1163 | |
1164 | if ((env->psw.mask & PSW_MASK_PER) && | |
1165 | (env->cregs[9] & PER_CR9_EVENT_STORE) && | |
1166 | (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) { | |
1167 | /* PSW is saved just before calling the helper. */ | |
1168 | env->per_address = env->psw.addr; | |
1169 | env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env); | |
1170 | } | |
9c3fd85b RH |
1171 | } |
1172 | ||
8ef7f78e | 1173 | /* load real address */ |
d8fe4a9c | 1174 | uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) |
8ef7f78e | 1175 | { |
27103424 | 1176 | CPUState *cs = CPU(s390_env_get_cpu(env)); |
8ef7f78e | 1177 | uint32_t cc = 0; |
27103424 | 1178 | int old_exc = cs->exception_index; |
8ef7f78e BS |
1179 | uint64_t asc = env->psw.mask & PSW_MASK_ASC; |
1180 | uint64_t ret; | |
1181 | int flags; | |
1182 | ||
1183 | /* XXX incomplete - has more corner cases */ | |
1184 | if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) { | |
1185 | program_interrupt(env, PGM_SPECIAL_OP, 2); | |
1186 | } | |
1187 | ||
27103424 | 1188 | cs->exception_index = old_exc; |
e3e09d87 | 1189 | if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) { |
8ef7f78e BS |
1190 | cc = 3; |
1191 | } | |
27103424 | 1192 | if (cs->exception_index == EXCP_PGM) { |
8ef7f78e BS |
1193 | ret = env->int_pgm_code | 0x80000000; |
1194 | } else { | |
1195 | ret |= addr & ~TARGET_PAGE_MASK; | |
1196 | } | |
27103424 | 1197 | cs->exception_index = old_exc; |
8ef7f78e | 1198 | |
d8fe4a9c RH |
1199 | env->cc_op = cc; |
1200 | return ret; | |
8ef7f78e | 1201 | } |
8ef7f78e | 1202 | #endif |