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