target-s390: Convert COMPARE AND SWAP
[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 /* store character under mask */
308 void HELPER(stcm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
309                   uint64_t addr)
310 {
311     uint8_t r;
312
313     HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%lx\n", __func__, r1, mask,
314                addr);
315     while (mask) {
316         if (mask & 8) {
317             r = (r1 & 0xff000000UL) >> 24;
318             cpu_stb_data(env, addr, r);
319             HELPER_LOG("mask 0x%x %02x (0x%lx) ", mask, r, addr);
320             addr++;
321         }
322         mask = (mask << 1) & 0xf;
323         r1 <<= 8;
324     }
325     HELPER_LOG("\n");
326 }
327
328 static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
329 {
330     uint64_t r = d2;
331
332     if (x2) {
333         r += env->regs[x2];
334     }
335
336     if (b2) {
337         r += env->regs[b2];
338     }
339
340     /* 31-Bit mode */
341     if (!(env->psw.mask & PSW_MASK_64)) {
342         r &= 0x7fffffff;
343     }
344
345     return r;
346 }
347
348 static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
349 {
350     uint64_t r = env->regs[reg];
351
352     /* 31-Bit mode */
353     if (!(env->psw.mask & PSW_MASK_64)) {
354         r &= 0x7fffffff;
355     }
356
357     return r;
358 }
359
360 /* search string (c is byte to search, r2 is string, r1 end of string) */
361 uint32_t HELPER(srst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
362 {
363     uint64_t i;
364     uint32_t cc = 2;
365     uint64_t str = get_address_31fix(env, r2);
366     uint64_t end = get_address_31fix(env, r1);
367
368     HELPER_LOG("%s: c %d *r1 0x%" PRIx64 " *r2 0x%" PRIx64 "\n", __func__,
369                c, env->regs[r1], env->regs[r2]);
370
371     for (i = str; i != end; i++) {
372         if (cpu_ldub_data(env, i) == c) {
373             env->regs[r1] = i;
374             cc = 1;
375             break;
376         }
377     }
378
379     return cc;
380 }
381
382 /* unsigned string compare (c is string terminator) */
383 uint32_t HELPER(clst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
384 {
385     uint64_t s1 = get_address_31fix(env, r1);
386     uint64_t s2 = get_address_31fix(env, r2);
387     uint8_t v1, v2;
388     uint32_t cc;
389
390     c = c & 0xff;
391 #ifdef CONFIG_USER_ONLY
392     if (!c) {
393         HELPER_LOG("%s: comparing '%s' and '%s'\n",
394                    __func__, (char *)g2h(s1), (char *)g2h(s2));
395     }
396 #endif
397     for (;;) {
398         v1 = cpu_ldub_data(env, s1);
399         v2 = cpu_ldub_data(env, s2);
400         if ((v1 == c || v2 == c) || (v1 != v2)) {
401             break;
402         }
403         s1++;
404         s2++;
405     }
406
407     if (v1 == v2) {
408         cc = 0;
409     } else {
410         cc = (v1 < v2) ? 1 : 2;
411         /* FIXME: 31-bit mode! */
412         env->regs[r1] = s1;
413         env->regs[r2] = s2;
414     }
415     return cc;
416 }
417
418 /* move page */
419 void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
420 {
421     /* XXX missing r0 handling */
422 #ifdef CONFIG_USER_ONLY
423     int i;
424
425     for (i = 0; i < TARGET_PAGE_SIZE; i++) {
426         cpu_stb_data(env, r1 + i, cpu_ldub_data(env, r2 + i));
427     }
428 #else
429     mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
430 #endif
431 }
432
433 /* string copy (c is string terminator) */
434 void HELPER(mvst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
435 {
436     uint64_t dest = get_address_31fix(env, r1);
437     uint64_t src = get_address_31fix(env, r2);
438     uint8_t v;
439
440     c = c & 0xff;
441 #ifdef CONFIG_USER_ONLY
442     if (!c) {
443         HELPER_LOG("%s: copy '%s' to 0x%lx\n", __func__, (char *)g2h(src),
444                    dest);
445     }
446 #endif
447     for (;;) {
448         v = cpu_ldub_data(env, src);
449         cpu_stb_data(env, dest, v);
450         if (v == c) {
451             break;
452         }
453         src++;
454         dest++;
455     }
456     env->regs[r1] = dest; /* FIXME: 31-bit mode! */
457 }
458
459 /* compare and swap 64-bit */
460 uint64_t HELPER(csg)(CPUS390XState *env, uint64_t r1, uint64_t a2, uint64_t r3)
461 {
462     /* FIXME: locking? */
463     uint64_t v2 = cpu_ldq_data(env, a2);
464     if (r1 == v2) {
465         cpu_stq_data(env, a2, r3);
466         env->cc_op = 0;
467         return r1;
468     } else {
469         env->cc_op = 1;
470         return v2;
471     }
472 }
473
474 /* compare double and swap 64-bit */
475 uint32_t HELPER(cdsg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
476 {
477     /* FIXME: locking? */
478     uint32_t cc;
479     uint64_t v2_hi = cpu_ldq_data(env, a2);
480     uint64_t v2_lo = cpu_ldq_data(env, a2 + 8);
481     uint64_t v1_hi = env->regs[r1];
482     uint64_t v1_lo = env->regs[r1 + 1];
483
484     if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) {
485         cc = 0;
486         cpu_stq_data(env, a2, env->regs[r3]);
487         cpu_stq_data(env, a2 + 8, env->regs[r3 + 1]);
488     } else {
489         cc = 1;
490         env->regs[r1] = v2_hi;
491         env->regs[r1 + 1] = v2_lo;
492     }
493
494     return cc;
495 }
496
497 /* compare and swap 32-bit */
498 uint64_t HELPER(cs)(CPUS390XState *env, uint64_t r1, uint64_t a2, uint64_t r3)
499 {
500     /* FIXME: locking? */
501     uint32_t v2 = cpu_ldl_data(env, a2);
502     if ((uint32_t)r1 == v2) {
503         cpu_stl_data(env, a2, (uint32_t)r3);
504         env->cc_op = 0;
505         return r1;
506     } else {
507         env->cc_op = 1;
508         return v2;
509     }
510 }
511
512 static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
513                            uint32_t mask)
514 {
515     int pos = 24; /* top of the lower half of r1 */
516     uint64_t rmask = 0xff000000ULL;
517     uint8_t val = 0;
518     int ccd = 0;
519     uint32_t cc = 0;
520
521     while (mask) {
522         if (mask & 8) {
523             env->regs[r1] &= ~rmask;
524             val = cpu_ldub_data(env, address);
525             if ((val & 0x80) && !ccd) {
526                 cc = 1;
527             }
528             ccd = 1;
529             if (val && cc == 0) {
530                 cc = 2;
531             }
532             env->regs[r1] |= (uint64_t)val << pos;
533             address++;
534         }
535         mask = (mask << 1) & 0xf;
536         pos -= 8;
537         rmask >>= 8;
538     }
539
540     return cc;
541 }
542
543 /* execute instruction
544    this instruction executes an insn modified with the contents of r1
545    it does not change the executed instruction in memory
546    it does not change the program counter
547    in other words: tricky...
548    currently implemented by interpreting the cases it is most commonly used in
549 */
550 uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
551                     uint64_t addr, uint64_t ret)
552 {
553     uint16_t insn = cpu_lduw_code(env, addr);
554
555     HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
556                insn);
557     if ((insn & 0xf0ff) == 0xd000) {
558         uint32_t l, insn2, b1, b2, d1, d2;
559
560         l = v1 & 0xff;
561         insn2 = cpu_ldl_code(env, addr + 2);
562         b1 = (insn2 >> 28) & 0xf;
563         b2 = (insn2 >> 12) & 0xf;
564         d1 = (insn2 >> 16) & 0xfff;
565         d2 = insn2 & 0xfff;
566         switch (insn & 0xf00) {
567         case 0x200:
568             helper_mvc(env, l, get_address(env, 0, b1, d1),
569                        get_address(env, 0, b2, d2));
570             break;
571         case 0x500:
572             cc = helper_clc(env, l, get_address(env, 0, b1, d1),
573                             get_address(env, 0, b2, d2));
574             break;
575         case 0x700:
576             cc = helper_xc(env, l, get_address(env, 0, b1, d1),
577                            get_address(env, 0, b2, d2));
578             break;
579         case 0xc00:
580             helper_tr(env, l, get_address(env, 0, b1, d1),
581                       get_address(env, 0, b2, d2));
582             break;
583         default:
584             goto abort;
585             break;
586         }
587     } else if ((insn & 0xff00) == 0x0a00) {
588         /* supervisor call */
589         HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
590         env->psw.addr = ret - 4;
591         env->int_svc_code = (insn | v1) & 0xff;
592         env->int_svc_ilen = 4;
593         helper_exception(env, EXCP_SVC);
594     } else if ((insn & 0xff00) == 0xbf00) {
595         uint32_t insn2, r1, r3, b2, d2;
596
597         insn2 = cpu_ldl_code(env, addr + 2);
598         r1 = (insn2 >> 20) & 0xf;
599         r3 = (insn2 >> 16) & 0xf;
600         b2 = (insn2 >> 12) & 0xf;
601         d2 = insn2 & 0xfff;
602         cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
603     } else {
604     abort:
605         cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
606                   insn);
607     }
608     return cc;
609 }
610
611 /* store character under mask high operates on the upper half of r1 */
612 void HELPER(stcmh)(CPUS390XState *env, uint32_t r1, uint64_t address,
613                    uint32_t mask)
614 {
615     int pos = 56; /* top of the upper half of r1 */
616
617     while (mask) {
618         if (mask & 8) {
619             cpu_stb_data(env, address, (env->regs[r1] >> pos) & 0xff);
620             address++;
621         }
622         mask = (mask << 1) & 0xf;
623         pos -= 8;
624     }
625 }
626
627 /* load access registers r1 to r3 from memory at a2 */
628 void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
629 {
630     int i;
631
632     for (i = r1;; i = (i + 1) % 16) {
633         env->aregs[i] = cpu_ldl_data(env, a2);
634         a2 += 4;
635
636         if (i == r3) {
637             break;
638         }
639     }
640 }
641
642 /* store access registers r1 to r3 in memory at a2 */
643 void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
644 {
645     int i;
646
647     for (i = r1;; i = (i + 1) % 16) {
648         cpu_stl_data(env, a2, env->aregs[i]);
649         a2 += 4;
650
651         if (i == r3) {
652             break;
653         }
654     }
655 }
656
657 /* move long */
658 uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
659 {
660     uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
661     uint64_t dest = get_address_31fix(env, r1);
662     uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
663     uint64_t src = get_address_31fix(env, r2);
664     uint8_t pad = src >> 24;
665     uint8_t v;
666     uint32_t cc;
667
668     if (destlen == srclen) {
669         cc = 0;
670     } else if (destlen < srclen) {
671         cc = 1;
672     } else {
673         cc = 2;
674     }
675
676     if (srclen > destlen) {
677         srclen = destlen;
678     }
679
680     for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
681         v = cpu_ldub_data(env, src);
682         cpu_stb_data(env, dest, v);
683     }
684
685     for (; destlen; dest++, destlen--) {
686         cpu_stb_data(env, dest, pad);
687     }
688
689     env->regs[r1 + 1] = destlen;
690     /* can't use srclen here, we trunc'ed it */
691     env->regs[r2 + 1] -= src - env->regs[r2];
692     env->regs[r1] = dest;
693     env->regs[r2] = src;
694
695     return cc;
696 }
697
698 /* move long extended another memcopy insn with more bells and whistles */
699 uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
700                        uint32_t r3)
701 {
702     uint64_t destlen = env->regs[r1 + 1];
703     uint64_t dest = env->regs[r1];
704     uint64_t srclen = env->regs[r3 + 1];
705     uint64_t src = env->regs[r3];
706     uint8_t pad = a2 & 0xff;
707     uint8_t v;
708     uint32_t cc;
709
710     if (!(env->psw.mask & PSW_MASK_64)) {
711         destlen = (uint32_t)destlen;
712         srclen = (uint32_t)srclen;
713         dest &= 0x7fffffff;
714         src &= 0x7fffffff;
715     }
716
717     if (destlen == srclen) {
718         cc = 0;
719     } else if (destlen < srclen) {
720         cc = 1;
721     } else {
722         cc = 2;
723     }
724
725     if (srclen > destlen) {
726         srclen = destlen;
727     }
728
729     for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
730         v = cpu_ldub_data(env, src);
731         cpu_stb_data(env, dest, v);
732     }
733
734     for (; destlen; dest++, destlen--) {
735         cpu_stb_data(env, dest, pad);
736     }
737
738     env->regs[r1 + 1] = destlen;
739     /* can't use srclen here, we trunc'ed it */
740     /* FIXME: 31-bit mode! */
741     env->regs[r3 + 1] -= src - env->regs[r3];
742     env->regs[r1] = dest;
743     env->regs[r3] = src;
744
745     return cc;
746 }
747
748 /* compare logical long extended memcompare insn with padding */
749 uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
750                        uint32_t r3)
751 {
752     uint64_t destlen = env->regs[r1 + 1];
753     uint64_t dest = get_address_31fix(env, r1);
754     uint64_t srclen = env->regs[r3 + 1];
755     uint64_t src = get_address_31fix(env, r3);
756     uint8_t pad = a2 & 0xff;
757     uint8_t v1 = 0, v2 = 0;
758     uint32_t cc = 0;
759
760     if (!(destlen || srclen)) {
761         return cc;
762     }
763
764     if (srclen > destlen) {
765         srclen = destlen;
766     }
767
768     for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
769         v1 = srclen ? cpu_ldub_data(env, src) : pad;
770         v2 = destlen ? cpu_ldub_data(env, dest) : pad;
771         if (v1 != v2) {
772             cc = (v1 < v2) ? 1 : 2;
773             break;
774         }
775     }
776
777     env->regs[r1 + 1] = destlen;
778     /* can't use srclen here, we trunc'ed it */
779     env->regs[r3 + 1] -= src - env->regs[r3];
780     env->regs[r1] = dest;
781     env->regs[r3] = src;
782
783     return cc;
784 }
785
786 /* checksum */
787 void HELPER(cksm)(CPUS390XState *env, uint32_t r1, uint32_t r2)
788 {
789     uint64_t src = get_address_31fix(env, r2);
790     uint64_t src_len = env->regs[(r2 + 1) & 15];
791     uint64_t cksm = (uint32_t)env->regs[r1];
792
793     while (src_len >= 4) {
794         cksm += cpu_ldl_data(env, src);
795
796         /* move to next word */
797         src_len -= 4;
798         src += 4;
799     }
800
801     switch (src_len) {
802     case 0:
803         break;
804     case 1:
805         cksm += cpu_ldub_data(env, src) << 24;
806         break;
807     case 2:
808         cksm += cpu_lduw_data(env, src) << 16;
809         break;
810     case 3:
811         cksm += cpu_lduw_data(env, src) << 16;
812         cksm += cpu_ldub_data(env, src + 2) << 8;
813         break;
814     }
815
816     /* indicate we've processed everything */
817     env->regs[r2] = src + src_len;
818     env->regs[(r2 + 1) & 15] = 0;
819
820     /* store result */
821     env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
822         ((uint32_t)cksm + (cksm >> 32));
823 }
824
825 void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
826                   uint64_t src)
827 {
828     int len_dest = len >> 4;
829     int len_src = len & 0xf;
830     uint8_t b;
831     int second_nibble = 0;
832
833     dest += len_dest;
834     src += len_src;
835
836     /* last byte is special, it only flips the nibbles */
837     b = cpu_ldub_data(env, src);
838     cpu_stb_data(env, dest, (b << 4) | (b >> 4));
839     src--;
840     len_src--;
841
842     /* now pad every nibble with 0xf0 */
843
844     while (len_dest > 0) {
845         uint8_t cur_byte = 0;
846
847         if (len_src > 0) {
848             cur_byte = cpu_ldub_data(env, src);
849         }
850
851         len_dest--;
852         dest--;
853
854         /* only advance one nibble at a time */
855         if (second_nibble) {
856             cur_byte >>= 4;
857             len_src--;
858             src--;
859         }
860         second_nibble = !second_nibble;
861
862         /* digit */
863         cur_byte = (cur_byte & 0xf);
864         /* zone bits */
865         cur_byte |= 0xf0;
866
867         cpu_stb_data(env, dest, cur_byte);
868     }
869 }
870
871 void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
872                 uint64_t trans)
873 {
874     int i;
875
876     for (i = 0; i <= len; i++) {
877         uint8_t byte = cpu_ldub_data(env, array + i);
878         uint8_t new_byte = cpu_ldub_data(env, trans + byte);
879
880         cpu_stb_data(env, array + i, new_byte);
881     }
882 }
883
884 #if !defined(CONFIG_USER_ONLY)
885 void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
886 {
887     int i;
888     uint64_t src = a2;
889
890     for (i = r1;; i = (i + 1) % 16) {
891         env->cregs[i] = cpu_ldq_data(env, src);
892         HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
893                    i, src, env->cregs[i]);
894         src += sizeof(uint64_t);
895
896         if (i == r3) {
897             break;
898         }
899     }
900
901     tlb_flush(env, 1);
902 }
903
904 void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
905 {
906     int i;
907     uint64_t src = a2;
908
909     for (i = r1;; i = (i + 1) % 16) {
910         env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
911             cpu_ldl_data(env, src);
912         src += sizeof(uint32_t);
913
914         if (i == r3) {
915             break;
916         }
917     }
918
919     tlb_flush(env, 1);
920 }
921
922 void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
923 {
924     int i;
925     uint64_t dest = a2;
926
927     for (i = r1;; i = (i + 1) % 16) {
928         cpu_stq_data(env, dest, env->cregs[i]);
929         dest += sizeof(uint64_t);
930
931         if (i == r3) {
932             break;
933         }
934     }
935 }
936
937 void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
938 {
939     int i;
940     uint64_t dest = a2;
941
942     for (i = r1;; i = (i + 1) % 16) {
943         cpu_stl_data(env, dest, env->cregs[i]);
944         dest += sizeof(uint32_t);
945
946         if (i == r3) {
947             break;
948         }
949     }
950 }
951
952 uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
953 {
954     /* XXX implement */
955
956     return 0;
957 }
958
959 /* insert storage key extended */
960 uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
961 {
962     uint64_t addr = get_address(env, 0, 0, r2);
963
964     if (addr > ram_size) {
965         return 0;
966     }
967
968     return env->storage_keys[addr / TARGET_PAGE_SIZE];
969 }
970
971 /* set storage key extended */
972 void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2)
973 {
974     uint64_t addr = get_address(env, 0, 0, r2);
975
976     if (addr > ram_size) {
977         return;
978     }
979
980     env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
981 }
982
983 /* reset reference bit extended */
984 uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2)
985 {
986     uint8_t re;
987     uint8_t key;
988
989     if (r2 > ram_size) {
990         return 0;
991     }
992
993     key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
994     re = key & (SK_R | SK_C);
995     env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
996
997     /*
998      * cc
999      *
1000      * 0  Reference bit zero; change bit zero
1001      * 1  Reference bit zero; change bit one
1002      * 2  Reference bit one; change bit zero
1003      * 3  Reference bit one; change bit one
1004      */
1005
1006     return re >> 1;
1007 }
1008
1009 /* compare and swap and purge */
1010 uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint32_t r2)
1011 {
1012     uint32_t cc;
1013     uint32_t o1 = env->regs[r1];
1014     uint64_t a2 = get_address_31fix(env, r2) & ~3ULL;
1015     uint32_t o2 = cpu_ldl_data(env, a2);
1016
1017     if (o1 == o2) {
1018         cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
1019         if (env->regs[r2] & 0x3) {
1020             /* flush TLB / ALB */
1021             tlb_flush(env, 1);
1022         }
1023         cc = 0;
1024     } else {
1025         env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
1026         cc = 1;
1027     }
1028
1029     return cc;
1030 }
1031
1032 static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
1033                         uint64_t mode1, uint64_t a2, uint64_t mode2)
1034 {
1035     target_ulong src, dest;
1036     int flags, cc = 0, i;
1037
1038     if (!l) {
1039         return 0;
1040     } else if (l > 256) {
1041         /* max 256 */
1042         l = 256;
1043         cc = 3;
1044     }
1045
1046     if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
1047         cpu_loop_exit(env);
1048     }
1049     dest |= a1 & ~TARGET_PAGE_MASK;
1050
1051     if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
1052         cpu_loop_exit(env);
1053     }
1054     src |= a2 & ~TARGET_PAGE_MASK;
1055
1056     /* XXX replace w/ memcpy */
1057     for (i = 0; i < l; i++) {
1058         /* XXX be more clever */
1059         if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
1060             (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
1061             mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
1062             break;
1063         }
1064         stb_phys(dest + i, ldub_phys(src + i));
1065     }
1066
1067     return cc;
1068 }
1069
1070 uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1071 {
1072     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1073                __func__, l, a1, a2);
1074
1075     return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
1076 }
1077
1078 uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1079 {
1080     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1081                __func__, l, a1, a2);
1082
1083     return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
1084 }
1085
1086 /* invalidate pte */
1087 void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1088 {
1089     uint64_t page = vaddr & TARGET_PAGE_MASK;
1090     uint64_t pte = 0;
1091
1092     /* XXX broadcast to other CPUs */
1093
1094     /* XXX Linux is nice enough to give us the exact pte address.
1095        According to spec we'd have to find it out ourselves */
1096     /* XXX Linux is fine with overwriting the pte, the spec requires
1097        us to only set the invalid bit */
1098     stq_phys(pte_addr, pte | _PAGE_INVALID);
1099
1100     /* XXX we exploit the fact that Linux passes the exact virtual
1101        address here - it's not obliged to! */
1102     tlb_flush_page(env, page);
1103
1104     /* XXX 31-bit hack */
1105     if (page & 0x80000000) {
1106         tlb_flush_page(env, page & ~0x80000000);
1107     } else {
1108         tlb_flush_page(env, page | 0x80000000);
1109     }
1110 }
1111
1112 /* flush local tlb */
1113 void HELPER(ptlb)(CPUS390XState *env)
1114 {
1115     tlb_flush(env, 1);
1116 }
1117
1118 /* store using real address */
1119 void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint32_t v1)
1120 {
1121     stw_phys(get_address(env, 0, 0, addr), v1);
1122 }
1123
1124 /* load real address */
1125 uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1126 {
1127     uint32_t cc = 0;
1128     int old_exc = env->exception_index;
1129     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1130     uint64_t ret;
1131     int flags;
1132
1133     /* XXX incomplete - has more corner cases */
1134     if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1135         program_interrupt(env, PGM_SPECIAL_OP, 2);
1136     }
1137
1138     env->exception_index = old_exc;
1139     if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1140         cc = 3;
1141     }
1142     if (env->exception_index == EXCP_PGM) {
1143         ret = env->int_pgm_code | 0x80000000;
1144     } else {
1145         ret |= addr & ~TARGET_PAGE_MASK;
1146     }
1147     env->exception_index = old_exc;
1148
1149     env->cc_op = cc;
1150     return ret;
1151 }
1152 #endif