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