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