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