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