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