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