Merge remote-tracking branch 'kraxel/seabios-a810e4e' into staging
[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_ilc = 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 /* insert character under mask high; same as icm, but operates on the
633    upper half of r1 */
634 uint32_t HELPER(icmh)(CPUS390XState *env, uint32_t r1, uint64_t address,
635                       uint32_t mask)
636 {
637     int pos = 56; /* top of the upper half of r1 */
638     uint64_t rmask = 0xff00000000000000ULL;
639     uint8_t val = 0;
640     int ccd = 0;
641     uint32_t cc = 0;
642
643     while (mask) {
644         if (mask & 8) {
645             env->regs[r1] &= ~rmask;
646             val = cpu_ldub_data(env, address);
647             if ((val & 0x80) && !ccd) {
648                 cc = 1;
649             }
650             ccd = 1;
651             if (val && cc == 0) {
652                 cc = 2;
653             }
654             env->regs[r1] |= (uint64_t)val << pos;
655             address++;
656         }
657         mask = (mask << 1) & 0xf;
658         pos -= 8;
659         rmask >>= 8;
660     }
661
662     return cc;
663 }
664
665 /* load access registers r1 to r3 from memory at a2 */
666 void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
667 {
668     int i;
669
670     for (i = r1;; i = (i + 1) % 16) {
671         env->aregs[i] = cpu_ldl_data(env, a2);
672         a2 += 4;
673
674         if (i == r3) {
675             break;
676         }
677     }
678 }
679
680 /* store access registers r1 to r3 in memory at a2 */
681 void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
682 {
683     int i;
684
685     for (i = r1;; i = (i + 1) % 16) {
686         cpu_stl_data(env, a2, env->aregs[i]);
687         a2 += 4;
688
689         if (i == r3) {
690             break;
691         }
692     }
693 }
694
695 /* move long */
696 uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
697 {
698     uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
699     uint64_t dest = get_address_31fix(env, r1);
700     uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
701     uint64_t src = get_address_31fix(env, r2);
702     uint8_t pad = src >> 24;
703     uint8_t v;
704     uint32_t cc;
705
706     if (destlen == srclen) {
707         cc = 0;
708     } else if (destlen < srclen) {
709         cc = 1;
710     } else {
711         cc = 2;
712     }
713
714     if (srclen > destlen) {
715         srclen = destlen;
716     }
717
718     for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
719         v = cpu_ldub_data(env, src);
720         cpu_stb_data(env, dest, v);
721     }
722
723     for (; destlen; dest++, destlen--) {
724         cpu_stb_data(env, dest, pad);
725     }
726
727     env->regs[r1 + 1] = destlen;
728     /* can't use srclen here, we trunc'ed it */
729     env->regs[r2 + 1] -= src - env->regs[r2];
730     env->regs[r1] = dest;
731     env->regs[r2] = src;
732
733     return cc;
734 }
735
736 /* move long extended another memcopy insn with more bells and whistles */
737 uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
738                        uint32_t r3)
739 {
740     uint64_t destlen = env->regs[r1 + 1];
741     uint64_t dest = env->regs[r1];
742     uint64_t srclen = env->regs[r3 + 1];
743     uint64_t src = env->regs[r3];
744     uint8_t pad = a2 & 0xff;
745     uint8_t v;
746     uint32_t cc;
747
748     if (!(env->psw.mask & PSW_MASK_64)) {
749         destlen = (uint32_t)destlen;
750         srclen = (uint32_t)srclen;
751         dest &= 0x7fffffff;
752         src &= 0x7fffffff;
753     }
754
755     if (destlen == srclen) {
756         cc = 0;
757     } else if (destlen < srclen) {
758         cc = 1;
759     } else {
760         cc = 2;
761     }
762
763     if (srclen > destlen) {
764         srclen = destlen;
765     }
766
767     for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
768         v = cpu_ldub_data(env, src);
769         cpu_stb_data(env, dest, v);
770     }
771
772     for (; destlen; dest++, destlen--) {
773         cpu_stb_data(env, dest, pad);
774     }
775
776     env->regs[r1 + 1] = destlen;
777     /* can't use srclen here, we trunc'ed it */
778     /* FIXME: 31-bit mode! */
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 /* compare logical long extended memcompare insn with padding */
787 uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
788                        uint32_t r3)
789 {
790     uint64_t destlen = env->regs[r1 + 1];
791     uint64_t dest = get_address_31fix(env, r1);
792     uint64_t srclen = env->regs[r3 + 1];
793     uint64_t src = get_address_31fix(env, r3);
794     uint8_t pad = a2 & 0xff;
795     uint8_t v1 = 0, v2 = 0;
796     uint32_t cc = 0;
797
798     if (!(destlen || srclen)) {
799         return cc;
800     }
801
802     if (srclen > destlen) {
803         srclen = destlen;
804     }
805
806     for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
807         v1 = srclen ? cpu_ldub_data(env, src) : pad;
808         v2 = destlen ? cpu_ldub_data(env, dest) : pad;
809         if (v1 != v2) {
810             cc = (v1 < v2) ? 1 : 2;
811             break;
812         }
813     }
814
815     env->regs[r1 + 1] = destlen;
816     /* can't use srclen here, we trunc'ed it */
817     env->regs[r3 + 1] -= src - env->regs[r3];
818     env->regs[r1] = dest;
819     env->regs[r3] = src;
820
821     return cc;
822 }
823
824 /* checksum */
825 void HELPER(cksm)(CPUS390XState *env, uint32_t r1, uint32_t r2)
826 {
827     uint64_t src = get_address_31fix(env, r2);
828     uint64_t src_len = env->regs[(r2 + 1) & 15];
829     uint64_t cksm = (uint32_t)env->regs[r1];
830
831     while (src_len >= 4) {
832         cksm += cpu_ldl_data(env, src);
833
834         /* move to next word */
835         src_len -= 4;
836         src += 4;
837     }
838
839     switch (src_len) {
840     case 0:
841         break;
842     case 1:
843         cksm += cpu_ldub_data(env, src) << 24;
844         break;
845     case 2:
846         cksm += cpu_lduw_data(env, src) << 16;
847         break;
848     case 3:
849         cksm += cpu_lduw_data(env, src) << 16;
850         cksm += cpu_ldub_data(env, src + 2) << 8;
851         break;
852     }
853
854     /* indicate we've processed everything */
855     env->regs[r2] = src + src_len;
856     env->regs[(r2 + 1) & 15] = 0;
857
858     /* store result */
859     env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
860         ((uint32_t)cksm + (cksm >> 32));
861 }
862
863 void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
864                   uint64_t src)
865 {
866     int len_dest = len >> 4;
867     int len_src = len & 0xf;
868     uint8_t b;
869     int second_nibble = 0;
870
871     dest += len_dest;
872     src += len_src;
873
874     /* last byte is special, it only flips the nibbles */
875     b = cpu_ldub_data(env, src);
876     cpu_stb_data(env, dest, (b << 4) | (b >> 4));
877     src--;
878     len_src--;
879
880     /* now pad every nibble with 0xf0 */
881
882     while (len_dest > 0) {
883         uint8_t cur_byte = 0;
884
885         if (len_src > 0) {
886             cur_byte = cpu_ldub_data(env, src);
887         }
888
889         len_dest--;
890         dest--;
891
892         /* only advance one nibble at a time */
893         if (second_nibble) {
894             cur_byte >>= 4;
895             len_src--;
896             src--;
897         }
898         second_nibble = !second_nibble;
899
900         /* digit */
901         cur_byte = (cur_byte & 0xf);
902         /* zone bits */
903         cur_byte |= 0xf0;
904
905         cpu_stb_data(env, dest, cur_byte);
906     }
907 }
908
909 void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
910                 uint64_t trans)
911 {
912     int i;
913
914     for (i = 0; i <= len; i++) {
915         uint8_t byte = cpu_ldub_data(env, array + i);
916         uint8_t new_byte = cpu_ldub_data(env, trans + byte);
917
918         cpu_stb_data(env, array + i, new_byte);
919     }
920 }
921
922 #if !defined(CONFIG_USER_ONLY)
923 void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
924 {
925     int i;
926     uint64_t src = a2;
927
928     for (i = r1;; i = (i + 1) % 16) {
929         env->cregs[i] = cpu_ldq_data(env, src);
930         HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
931                    i, src, env->cregs[i]);
932         src += sizeof(uint64_t);
933
934         if (i == r3) {
935             break;
936         }
937     }
938
939     tlb_flush(env, 1);
940 }
941
942 void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
943 {
944     int i;
945     uint64_t src = a2;
946
947     for (i = r1;; i = (i + 1) % 16) {
948         env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
949             cpu_ldl_data(env, src);
950         src += sizeof(uint32_t);
951
952         if (i == r3) {
953             break;
954         }
955     }
956
957     tlb_flush(env, 1);
958 }
959
960 void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
961 {
962     int i;
963     uint64_t dest = a2;
964
965     for (i = r1;; i = (i + 1) % 16) {
966         cpu_stq_data(env, dest, env->cregs[i]);
967         dest += sizeof(uint64_t);
968
969         if (i == r3) {
970             break;
971         }
972     }
973 }
974
975 void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
976 {
977     int i;
978     uint64_t dest = a2;
979
980     for (i = r1;; i = (i + 1) % 16) {
981         cpu_stl_data(env, dest, env->cregs[i]);
982         dest += sizeof(uint32_t);
983
984         if (i == r3) {
985             break;
986         }
987     }
988 }
989
990 uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
991 {
992     /* XXX implement */
993
994     return 0;
995 }
996
997 /* insert storage key extended */
998 uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
999 {
1000     uint64_t addr = get_address(env, 0, 0, r2);
1001
1002     if (addr > ram_size) {
1003         return 0;
1004     }
1005
1006     return env->storage_keys[addr / TARGET_PAGE_SIZE];
1007 }
1008
1009 /* set storage key extended */
1010 void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2)
1011 {
1012     uint64_t addr = get_address(env, 0, 0, r2);
1013
1014     if (addr > ram_size) {
1015         return;
1016     }
1017
1018     env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
1019 }
1020
1021 /* reset reference bit extended */
1022 uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2)
1023 {
1024     uint8_t re;
1025     uint8_t key;
1026
1027     if (r2 > ram_size) {
1028         return 0;
1029     }
1030
1031     key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
1032     re = key & (SK_R | SK_C);
1033     env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
1034
1035     /*
1036      * cc
1037      *
1038      * 0  Reference bit zero; change bit zero
1039      * 1  Reference bit zero; change bit one
1040      * 2  Reference bit one; change bit zero
1041      * 3  Reference bit one; change bit one
1042      */
1043
1044     return re >> 1;
1045 }
1046
1047 /* compare and swap and purge */
1048 uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint32_t r2)
1049 {
1050     uint32_t cc;
1051     uint32_t o1 = env->regs[r1];
1052     uint64_t a2 = get_address_31fix(env, r2) & ~3ULL;
1053     uint32_t o2 = cpu_ldl_data(env, a2);
1054
1055     if (o1 == o2) {
1056         cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
1057         if (env->regs[r2] & 0x3) {
1058             /* flush TLB / ALB */
1059             tlb_flush(env, 1);
1060         }
1061         cc = 0;
1062     } else {
1063         env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
1064         cc = 1;
1065     }
1066
1067     return cc;
1068 }
1069
1070 static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
1071                         uint64_t mode1, uint64_t a2, uint64_t mode2)
1072 {
1073     target_ulong src, dest;
1074     int flags, cc = 0, i;
1075
1076     if (!l) {
1077         return 0;
1078     } else if (l > 256) {
1079         /* max 256 */
1080         l = 256;
1081         cc = 3;
1082     }
1083
1084     if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
1085         cpu_loop_exit(env);
1086     }
1087     dest |= a1 & ~TARGET_PAGE_MASK;
1088
1089     if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
1090         cpu_loop_exit(env);
1091     }
1092     src |= a2 & ~TARGET_PAGE_MASK;
1093
1094     /* XXX replace w/ memcpy */
1095     for (i = 0; i < l; i++) {
1096         /* XXX be more clever */
1097         if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
1098             (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
1099             mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
1100             break;
1101         }
1102         stb_phys(dest + i, ldub_phys(src + i));
1103     }
1104
1105     return cc;
1106 }
1107
1108 uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1109 {
1110     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1111                __func__, l, a1, a2);
1112
1113     return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
1114 }
1115
1116 uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1117 {
1118     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1119                __func__, l, a1, a2);
1120
1121     return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
1122 }
1123
1124 /* invalidate pte */
1125 void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1126 {
1127     uint64_t page = vaddr & TARGET_PAGE_MASK;
1128     uint64_t pte = 0;
1129
1130     /* XXX broadcast to other CPUs */
1131
1132     /* XXX Linux is nice enough to give us the exact pte address.
1133        According to spec we'd have to find it out ourselves */
1134     /* XXX Linux is fine with overwriting the pte, the spec requires
1135        us to only set the invalid bit */
1136     stq_phys(pte_addr, pte | _PAGE_INVALID);
1137
1138     /* XXX we exploit the fact that Linux passes the exact virtual
1139        address here - it's not obliged to! */
1140     tlb_flush_page(env, page);
1141
1142     /* XXX 31-bit hack */
1143     if (page & 0x80000000) {
1144         tlb_flush_page(env, page & ~0x80000000);
1145     } else {
1146         tlb_flush_page(env, page | 0x80000000);
1147     }
1148 }
1149
1150 /* flush local tlb */
1151 void HELPER(ptlb)(CPUS390XState *env)
1152 {
1153     tlb_flush(env, 1);
1154 }
1155
1156 /* store using real address */
1157 void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint32_t v1)
1158 {
1159     stw_phys(get_address(env, 0, 0, addr), v1);
1160 }
1161
1162 /* load real address */
1163 uint32_t HELPER(lra)(CPUS390XState *env, uint64_t addr, uint32_t r1)
1164 {
1165     uint32_t cc = 0;
1166     int old_exc = env->exception_index;
1167     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1168     uint64_t ret;
1169     int flags;
1170
1171     /* XXX incomplete - has more corner cases */
1172     if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1173         program_interrupt(env, PGM_SPECIAL_OP, 2);
1174     }
1175
1176     env->exception_index = old_exc;
1177     if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1178         cc = 3;
1179     }
1180     if (env->exception_index == EXCP_PGM) {
1181         ret = env->int_pgm_code | 0x80000000;
1182     } else {
1183         ret |= addr & ~TARGET_PAGE_MASK;
1184     }
1185     env->exception_index = old_exc;
1186
1187     if (!(env->psw.mask & PSW_MASK_64)) {
1188         env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
1189             (ret & 0xffffffffULL);
1190     } else {
1191         env->regs[r1] = ret;
1192     }
1193
1194     return cc;
1195 }
1196
1197 #endif