ACPI: Add definitions for the SPCR table
[qemu/ar7.git] / target-s390x / mem_helper.c
blobb4e5d44011666f006450c13634d65fb0a854b6a8
1 /*
2 * S/390 memory access helper routines
4 * Copyright (c) 2009 Ulrich Hecht
5 * Copyright (c) 2009 Alexander Graf
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.
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.
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/>.
21 #include "cpu.h"
22 #include "exec/helper-proto.h"
23 #include "exec/cpu_ldst.h"
25 /*****************************************************************************/
26 /* Softmmu support */
27 #if !defined(CONFIG_USER_ONLY)
29 /* try to fill the TLB and return an exception if error. If retaddr is
30 NULL, it means that the function was called in C code (i.e. not
31 from generated code or from helper.c) */
32 /* XXX: fix it to restore all registers */
33 void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
34 uintptr_t retaddr)
36 int ret;
38 ret = s390_cpu_handle_mmu_fault(cs, addr, is_write, mmu_idx);
39 if (unlikely(ret != 0)) {
40 if (likely(retaddr)) {
41 /* now we have a real cpu fault */
42 cpu_restore_state(cs, retaddr);
44 cpu_loop_exit(cs);
48 #endif
50 /* #define DEBUG_HELPER */
51 #ifdef DEBUG_HELPER
52 #define HELPER_LOG(x...) qemu_log(x)
53 #else
54 #define HELPER_LOG(x...)
55 #endif
57 #ifndef CONFIG_USER_ONLY
58 static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest,
59 uint8_t byte)
61 S390CPU *cpu = s390_env_get_cpu(env);
62 hwaddr dest_phys;
63 hwaddr len = l;
64 void *dest_p;
65 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
66 int flags;
68 if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) {
69 cpu_stb_data(env, dest, byte);
70 cpu_abort(CPU(cpu), "should never reach here");
72 dest_phys |= dest & ~TARGET_PAGE_MASK;
74 dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
76 memset(dest_p, byte, len);
78 cpu_physical_memory_unmap(dest_p, 1, len, len);
81 static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
82 uint64_t src)
84 S390CPU *cpu = s390_env_get_cpu(env);
85 hwaddr dest_phys;
86 hwaddr src_phys;
87 hwaddr len = l;
88 void *dest_p;
89 void *src_p;
90 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
91 int flags;
93 if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags, true)) {
94 cpu_stb_data(env, dest, 0);
95 cpu_abort(CPU(cpu), "should never reach here");
97 dest_phys |= dest & ~TARGET_PAGE_MASK;
99 if (mmu_translate(env, src, 0, asc, &src_phys, &flags, true)) {
100 cpu_ldub_data(env, src);
101 cpu_abort(CPU(cpu), "should never reach here");
103 src_phys |= src & ~TARGET_PAGE_MASK;
105 dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
106 src_p = cpu_physical_memory_map(src_phys, &len, 0);
108 memmove(dest_p, src_p, len);
110 cpu_physical_memory_unmap(dest_p, 1, len, len);
111 cpu_physical_memory_unmap(src_p, 0, len, len);
113 #endif
115 /* and on array */
116 uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
117 uint64_t src)
119 int i;
120 unsigned char x;
121 uint32_t cc = 0;
123 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
124 __func__, l, dest, src);
125 for (i = 0; i <= l; i++) {
126 x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
127 if (x) {
128 cc = 1;
130 cpu_stb_data(env, dest + i, x);
132 return cc;
135 /* xor on array */
136 uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
137 uint64_t src)
139 int i;
140 unsigned char x;
141 uint32_t cc = 0;
143 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
144 __func__, l, dest, src);
146 #ifndef CONFIG_USER_ONLY
147 /* xor with itself is the same as memset(0) */
148 if ((l > 32) && (src == dest) &&
149 (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) {
150 mvc_fast_memset(env, l + 1, dest, 0);
151 return 0;
153 #else
154 if (src == dest) {
155 memset(g2h(dest), 0, l + 1);
156 return 0;
158 #endif
160 for (i = 0; i <= l; i++) {
161 x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
162 if (x) {
163 cc = 1;
165 cpu_stb_data(env, dest + i, x);
167 return cc;
170 /* or on array */
171 uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
172 uint64_t src)
174 int i;
175 unsigned char x;
176 uint32_t cc = 0;
178 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
179 __func__, l, dest, src);
180 for (i = 0; i <= l; i++) {
181 x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
182 if (x) {
183 cc = 1;
185 cpu_stb_data(env, dest + i, x);
187 return cc;
190 /* memmove */
191 void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
193 int i = 0;
194 int x = 0;
195 uint32_t l_64 = (l + 1) / 8;
197 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
198 __func__, l, dest, src);
200 #ifndef CONFIG_USER_ONLY
201 if ((l > 32) &&
202 (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) &&
203 (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) {
204 if (dest == (src + 1)) {
205 mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src));
206 return;
207 } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
208 mvc_fast_memmove(env, l + 1, dest, src);
209 return;
212 #else
213 if (dest == (src + 1)) {
214 memset(g2h(dest), cpu_ldub_data(env, src), l + 1);
215 return;
216 /* mvc and memmove do not behave the same when areas overlap! */
217 } else if ((dest < src) || (src + l < dest)) {
218 memmove(g2h(dest), g2h(src), l + 1);
219 return;
221 #endif
223 /* handle the parts that fit into 8-byte loads/stores */
224 if ((dest + 8 <= src) || (src + 8 <= dest)) {
225 for (i = 0; i < l_64; i++) {
226 cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
227 x += 8;
231 /* slow version with byte accesses which always work */
232 for (i = x; i <= l; i++) {
233 cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
237 /* compare unsigned byte arrays */
238 uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
240 int i;
241 unsigned char x, y;
242 uint32_t cc;
244 HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
245 __func__, l, s1, s2);
246 for (i = 0; i <= l; i++) {
247 x = cpu_ldub_data(env, s1 + i);
248 y = cpu_ldub_data(env, s2 + i);
249 HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
250 if (x < y) {
251 cc = 1;
252 goto done;
253 } else if (x > y) {
254 cc = 2;
255 goto done;
258 cc = 0;
259 done:
260 HELPER_LOG("\n");
261 return cc;
264 /* compare logical under mask */
265 uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
266 uint64_t addr)
268 uint8_t r, d;
269 uint32_t cc;
271 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
272 mask, addr);
273 cc = 0;
274 while (mask) {
275 if (mask & 8) {
276 d = cpu_ldub_data(env, addr);
277 r = (r1 & 0xff000000UL) >> 24;
278 HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
279 addr);
280 if (r < d) {
281 cc = 1;
282 break;
283 } else if (r > d) {
284 cc = 2;
285 break;
287 addr++;
289 mask = (mask << 1) & 0xf;
290 r1 <<= 8;
292 HELPER_LOG("\n");
293 return cc;
296 static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
298 /* 31-Bit mode */
299 if (!(env->psw.mask & PSW_MASK_64)) {
300 a &= 0x7fffffff;
302 return a;
305 static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
307 uint64_t r = d2;
308 if (x2) {
309 r += env->regs[x2];
311 if (b2) {
312 r += env->regs[b2];
314 return fix_address(env, r);
317 static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
319 return fix_address(env, env->regs[reg]);
322 /* search string (c is byte to search, r2 is string, r1 end of string) */
323 uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
324 uint64_t str)
326 uint32_t len;
327 uint8_t v, c = r0;
329 str = fix_address(env, str);
330 end = fix_address(env, end);
332 /* Assume for now that R2 is unmodified. */
333 env->retxl = str;
335 /* Lest we fail to service interrupts in a timely manner, limit the
336 amount of work we're willing to do. For now, let's cap at 8k. */
337 for (len = 0; len < 0x2000; ++len) {
338 if (str + len == end) {
339 /* Character not found. R1 & R2 are unmodified. */
340 env->cc_op = 2;
341 return end;
343 v = cpu_ldub_data(env, str + len);
344 if (v == c) {
345 /* Character found. Set R1 to the location; R2 is unmodified. */
346 env->cc_op = 1;
347 return str + len;
351 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
352 env->retxl = str + len;
353 env->cc_op = 3;
354 return end;
357 /* unsigned string compare (c is string terminator) */
358 uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
360 uint32_t len;
362 c = c & 0xff;
363 s1 = fix_address(env, s1);
364 s2 = fix_address(env, s2);
366 /* Lest we fail to service interrupts in a timely manner, limit the
367 amount of work we're willing to do. For now, let's cap at 8k. */
368 for (len = 0; len < 0x2000; ++len) {
369 uint8_t v1 = cpu_ldub_data(env, s1 + len);
370 uint8_t v2 = cpu_ldub_data(env, s2 + len);
371 if (v1 == v2) {
372 if (v1 == c) {
373 /* Equal. CC=0, and don't advance the registers. */
374 env->cc_op = 0;
375 env->retxl = s2;
376 return s1;
378 } else {
379 /* Unequal. CC={1,2}, and advance the registers. Note that
380 the terminator need not be zero, but the string that contains
381 the terminator is by definition "low". */
382 env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
383 env->retxl = s2 + len;
384 return s1 + len;
388 /* CPU-determined bytes equal; advance the registers. */
389 env->cc_op = 3;
390 env->retxl = s2 + len;
391 return s1 + len;
394 /* move page */
395 void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
397 /* XXX missing r0 handling */
398 env->cc_op = 0;
399 #ifdef CONFIG_USER_ONLY
400 memmove(g2h(r1), g2h(r2), TARGET_PAGE_SIZE);
401 #else
402 mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
403 #endif
406 /* string copy (c is string terminator) */
407 uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
409 uint32_t len;
411 c = c & 0xff;
412 d = fix_address(env, d);
413 s = fix_address(env, s);
415 /* Lest we fail to service interrupts in a timely manner, limit the
416 amount of work we're willing to do. For now, let's cap at 8k. */
417 for (len = 0; len < 0x2000; ++len) {
418 uint8_t v = cpu_ldub_data(env, s + len);
419 cpu_stb_data(env, d + len, v);
420 if (v == c) {
421 /* Complete. Set CC=1 and advance R1. */
422 env->cc_op = 1;
423 env->retxl = s;
424 return d + len;
428 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
429 env->cc_op = 3;
430 env->retxl = s + len;
431 return d + len;
434 static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
435 uint32_t mask)
437 int pos = 24; /* top of the lower half of r1 */
438 uint64_t rmask = 0xff000000ULL;
439 uint8_t val = 0;
440 int ccd = 0;
441 uint32_t cc = 0;
443 while (mask) {
444 if (mask & 8) {
445 env->regs[r1] &= ~rmask;
446 val = cpu_ldub_data(env, address);
447 if ((val & 0x80) && !ccd) {
448 cc = 1;
450 ccd = 1;
451 if (val && cc == 0) {
452 cc = 2;
454 env->regs[r1] |= (uint64_t)val << pos;
455 address++;
457 mask = (mask << 1) & 0xf;
458 pos -= 8;
459 rmask >>= 8;
462 return cc;
465 /* execute instruction
466 this instruction executes an insn modified with the contents of r1
467 it does not change the executed instruction in memory
468 it does not change the program counter
469 in other words: tricky...
470 currently implemented by interpreting the cases it is most commonly used in
472 uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
473 uint64_t addr, uint64_t ret)
475 S390CPU *cpu = s390_env_get_cpu(env);
476 uint16_t insn = cpu_lduw_code(env, addr);
478 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
479 insn);
480 if ((insn & 0xf0ff) == 0xd000) {
481 uint32_t l, insn2, b1, b2, d1, d2;
483 l = v1 & 0xff;
484 insn2 = cpu_ldl_code(env, addr + 2);
485 b1 = (insn2 >> 28) & 0xf;
486 b2 = (insn2 >> 12) & 0xf;
487 d1 = (insn2 >> 16) & 0xfff;
488 d2 = insn2 & 0xfff;
489 switch (insn & 0xf00) {
490 case 0x200:
491 helper_mvc(env, l, get_address(env, 0, b1, d1),
492 get_address(env, 0, b2, d2));
493 break;
494 case 0x400:
495 cc = helper_nc(env, l, get_address(env, 0, b1, d1),
496 get_address(env, 0, b2, d2));
497 break;
498 case 0x500:
499 cc = helper_clc(env, l, get_address(env, 0, b1, d1),
500 get_address(env, 0, b2, d2));
501 break;
502 case 0x600:
503 cc = helper_oc(env, l, get_address(env, 0, b1, d1),
504 get_address(env, 0, b2, d2));
505 break;
506 case 0x700:
507 cc = helper_xc(env, l, get_address(env, 0, b1, d1),
508 get_address(env, 0, b2, d2));
509 break;
510 case 0xc00:
511 helper_tr(env, l, get_address(env, 0, b1, d1),
512 get_address(env, 0, b2, d2));
513 case 0xd00:
514 cc = helper_trt(env, l, get_address(env, 0, b1, d1),
515 get_address(env, 0, b2, d2));
516 break;
517 default:
518 goto abort;
520 } else if ((insn & 0xff00) == 0x0a00) {
521 /* supervisor call */
522 HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
523 env->psw.addr = ret - 4;
524 env->int_svc_code = (insn | v1) & 0xff;
525 env->int_svc_ilen = 4;
526 helper_exception(env, EXCP_SVC);
527 } else if ((insn & 0xff00) == 0xbf00) {
528 uint32_t insn2, r1, r3, b2, d2;
530 insn2 = cpu_ldl_code(env, addr + 2);
531 r1 = (insn2 >> 20) & 0xf;
532 r3 = (insn2 >> 16) & 0xf;
533 b2 = (insn2 >> 12) & 0xf;
534 d2 = insn2 & 0xfff;
535 cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
536 } else {
537 abort:
538 cpu_abort(CPU(cpu), "EXECUTE on instruction prefix 0x%x not implemented\n",
539 insn);
541 return cc;
544 /* load access registers r1 to r3 from memory at a2 */
545 void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
547 int i;
549 for (i = r1;; i = (i + 1) % 16) {
550 env->aregs[i] = cpu_ldl_data(env, a2);
551 a2 += 4;
553 if (i == r3) {
554 break;
559 /* store access registers r1 to r3 in memory at a2 */
560 void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
562 int i;
564 for (i = r1;; i = (i + 1) % 16) {
565 cpu_stl_data(env, a2, env->aregs[i]);
566 a2 += 4;
568 if (i == r3) {
569 break;
574 /* move long */
575 uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
577 uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
578 uint64_t dest = get_address_31fix(env, r1);
579 uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
580 uint64_t src = get_address_31fix(env, r2);
581 uint8_t pad = src >> 24;
582 uint8_t v;
583 uint32_t cc;
585 if (destlen == srclen) {
586 cc = 0;
587 } else if (destlen < srclen) {
588 cc = 1;
589 } else {
590 cc = 2;
593 if (srclen > destlen) {
594 srclen = destlen;
597 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
598 v = cpu_ldub_data(env, src);
599 cpu_stb_data(env, dest, v);
602 for (; destlen; dest++, destlen--) {
603 cpu_stb_data(env, dest, pad);
606 env->regs[r1 + 1] = destlen;
607 /* can't use srclen here, we trunc'ed it */
608 env->regs[r2 + 1] -= src - env->regs[r2];
609 env->regs[r1] = dest;
610 env->regs[r2] = src;
612 return cc;
615 /* move long extended another memcopy insn with more bells and whistles */
616 uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
617 uint32_t r3)
619 uint64_t destlen = env->regs[r1 + 1];
620 uint64_t dest = env->regs[r1];
621 uint64_t srclen = env->regs[r3 + 1];
622 uint64_t src = env->regs[r3];
623 uint8_t pad = a2 & 0xff;
624 uint8_t v;
625 uint32_t cc;
627 if (!(env->psw.mask & PSW_MASK_64)) {
628 destlen = (uint32_t)destlen;
629 srclen = (uint32_t)srclen;
630 dest &= 0x7fffffff;
631 src &= 0x7fffffff;
634 if (destlen == srclen) {
635 cc = 0;
636 } else if (destlen < srclen) {
637 cc = 1;
638 } else {
639 cc = 2;
642 if (srclen > destlen) {
643 srclen = destlen;
646 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
647 v = cpu_ldub_data(env, src);
648 cpu_stb_data(env, dest, v);
651 for (; destlen; dest++, destlen--) {
652 cpu_stb_data(env, dest, pad);
655 env->regs[r1 + 1] = destlen;
656 /* can't use srclen here, we trunc'ed it */
657 /* FIXME: 31-bit mode! */
658 env->regs[r3 + 1] -= src - env->regs[r3];
659 env->regs[r1] = dest;
660 env->regs[r3] = src;
662 return cc;
665 /* compare logical long extended memcompare insn with padding */
666 uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
667 uint32_t r3)
669 uint64_t destlen = env->regs[r1 + 1];
670 uint64_t dest = get_address_31fix(env, r1);
671 uint64_t srclen = env->regs[r3 + 1];
672 uint64_t src = get_address_31fix(env, r3);
673 uint8_t pad = a2 & 0xff;
674 uint8_t v1 = 0, v2 = 0;
675 uint32_t cc = 0;
677 if (!(destlen || srclen)) {
678 return cc;
681 if (srclen > destlen) {
682 srclen = destlen;
685 for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
686 v1 = srclen ? cpu_ldub_data(env, src) : pad;
687 v2 = destlen ? cpu_ldub_data(env, dest) : pad;
688 if (v1 != v2) {
689 cc = (v1 < v2) ? 1 : 2;
690 break;
694 env->regs[r1 + 1] = destlen;
695 /* can't use srclen here, we trunc'ed it */
696 env->regs[r3 + 1] -= src - env->regs[r3];
697 env->regs[r1] = dest;
698 env->regs[r3] = src;
700 return cc;
703 /* checksum */
704 uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
705 uint64_t src, uint64_t src_len)
707 uint64_t max_len, len;
708 uint64_t cksm = (uint32_t)r1;
710 /* Lest we fail to service interrupts in a timely manner, limit the
711 amount of work we're willing to do. For now, let's cap at 8k. */
712 max_len = (src_len > 0x2000 ? 0x2000 : src_len);
714 /* Process full words as available. */
715 for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
716 cksm += (uint32_t)cpu_ldl_data(env, src);
719 switch (max_len - len) {
720 case 1:
721 cksm += cpu_ldub_data(env, src) << 24;
722 len += 1;
723 break;
724 case 2:
725 cksm += cpu_lduw_data(env, src) << 16;
726 len += 2;
727 break;
728 case 3:
729 cksm += cpu_lduw_data(env, src) << 16;
730 cksm += cpu_ldub_data(env, src + 2) << 8;
731 len += 3;
732 break;
735 /* Fold the carry from the checksum. Note that we can see carry-out
736 during folding more than once (but probably not more than twice). */
737 while (cksm > 0xffffffffull) {
738 cksm = (uint32_t)cksm + (cksm >> 32);
741 /* Indicate whether or not we've processed everything. */
742 env->cc_op = (len == src_len ? 0 : 3);
744 /* Return both cksm and processed length. */
745 env->retxl = cksm;
746 return len;
749 void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
750 uint64_t src)
752 int len_dest = len >> 4;
753 int len_src = len & 0xf;
754 uint8_t b;
755 int second_nibble = 0;
757 dest += len_dest;
758 src += len_src;
760 /* last byte is special, it only flips the nibbles */
761 b = cpu_ldub_data(env, src);
762 cpu_stb_data(env, dest, (b << 4) | (b >> 4));
763 src--;
764 len_src--;
766 /* now pad every nibble with 0xf0 */
768 while (len_dest > 0) {
769 uint8_t cur_byte = 0;
771 if (len_src > 0) {
772 cur_byte = cpu_ldub_data(env, src);
775 len_dest--;
776 dest--;
778 /* only advance one nibble at a time */
779 if (second_nibble) {
780 cur_byte >>= 4;
781 len_src--;
782 src--;
784 second_nibble = !second_nibble;
786 /* digit */
787 cur_byte = (cur_byte & 0xf);
788 /* zone bits */
789 cur_byte |= 0xf0;
791 cpu_stb_data(env, dest, cur_byte);
795 void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
796 uint64_t trans)
798 int i;
800 for (i = 0; i <= len; i++) {
801 uint8_t byte = cpu_ldub_data(env, array + i);
802 uint8_t new_byte = cpu_ldub_data(env, trans + byte);
804 cpu_stb_data(env, array + i, new_byte);
808 uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
809 uint64_t len, uint64_t trans)
811 uint8_t end = env->regs[0] & 0xff;
812 uint64_t l = len;
813 uint64_t i;
815 if (!(env->psw.mask & PSW_MASK_64)) {
816 array &= 0x7fffffff;
817 l = (uint32_t)l;
820 /* Lest we fail to service interrupts in a timely manner, limit the
821 amount of work we're willing to do. For now, let's cap at 8k. */
822 if (l > 0x2000) {
823 l = 0x2000;
824 env->cc_op = 3;
825 } else {
826 env->cc_op = 0;
829 for (i = 0; i < l; i++) {
830 uint8_t byte, new_byte;
832 byte = cpu_ldub_data(env, array + i);
834 if (byte == end) {
835 env->cc_op = 1;
836 break;
839 new_byte = cpu_ldub_data(env, trans + byte);
840 cpu_stb_data(env, array + i, new_byte);
843 env->retxl = len - i;
844 return array + i;
847 uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
848 uint64_t trans)
850 uint32_t cc = 0;
851 int i;
853 for (i = 0; i <= len; i++) {
854 uint8_t byte = cpu_ldub_data(env, array + i);
855 uint8_t sbyte = cpu_ldub_data(env, trans + byte);
857 if (sbyte != 0) {
858 env->regs[1] = array + i;
859 env->regs[2] = (env->regs[2] & ~0xff) | sbyte;
860 cc = (i == len) ? 2 : 1;
861 break;
865 return cc;
868 #if !defined(CONFIG_USER_ONLY)
869 void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
871 S390CPU *cpu = s390_env_get_cpu(env);
872 int i;
873 uint64_t src = a2;
875 for (i = r1;; i = (i + 1) % 16) {
876 env->cregs[i] = cpu_ldq_data(env, src);
877 HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
878 i, src, env->cregs[i]);
879 src += sizeof(uint64_t);
881 if (i == r3) {
882 break;
886 tlb_flush(CPU(cpu), 1);
889 void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
891 S390CPU *cpu = s390_env_get_cpu(env);
892 int i;
893 uint64_t src = a2;
895 for (i = r1;; i = (i + 1) % 16) {
896 env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
897 cpu_ldl_data(env, src);
898 src += sizeof(uint32_t);
900 if (i == r3) {
901 break;
905 tlb_flush(CPU(cpu), 1);
908 void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
910 int i;
911 uint64_t dest = a2;
913 for (i = r1;; i = (i + 1) % 16) {
914 cpu_stq_data(env, dest, env->cregs[i]);
915 dest += sizeof(uint64_t);
917 if (i == r3) {
918 break;
923 void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
925 int i;
926 uint64_t dest = a2;
928 for (i = r1;; i = (i + 1) % 16) {
929 cpu_stl_data(env, dest, env->cregs[i]);
930 dest += sizeof(uint32_t);
932 if (i == r3) {
933 break;
938 uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
940 /* XXX implement */
942 return 0;
945 /* insert storage key extended */
946 uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
948 uint64_t addr = get_address(env, 0, 0, r2);
950 if (addr > ram_size) {
951 return 0;
954 return env->storage_keys[addr / TARGET_PAGE_SIZE];
957 /* set storage key extended */
958 void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
960 uint64_t addr = get_address(env, 0, 0, r2);
962 if (addr > ram_size) {
963 return;
966 env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
969 /* reset reference bit extended */
970 uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
972 uint8_t re;
973 uint8_t key;
975 if (r2 > ram_size) {
976 return 0;
979 key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
980 re = key & (SK_R | SK_C);
981 env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
984 * cc
986 * 0 Reference bit zero; change bit zero
987 * 1 Reference bit zero; change bit one
988 * 2 Reference bit one; change bit zero
989 * 3 Reference bit one; change bit one
992 return re >> 1;
995 /* compare and swap and purge */
996 uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
998 S390CPU *cpu = s390_env_get_cpu(env);
999 uint32_t cc;
1000 uint32_t o1 = env->regs[r1];
1001 uint64_t a2 = r2 & ~3ULL;
1002 uint32_t o2 = cpu_ldl_data(env, a2);
1004 if (o1 == o2) {
1005 cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
1006 if (r2 & 0x3) {
1007 /* flush TLB / ALB */
1008 tlb_flush(CPU(cpu), 1);
1010 cc = 0;
1011 } else {
1012 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
1013 cc = 1;
1016 return cc;
1019 uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1021 int cc = 0, i;
1023 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1024 __func__, l, a1, a2);
1026 if (l > 256) {
1027 /* max 256 */
1028 l = 256;
1029 cc = 3;
1032 /* XXX replace w/ memcpy */
1033 for (i = 0; i < l; i++) {
1034 cpu_stb_secondary(env, a1 + i, cpu_ldub_primary(env, a2 + i));
1037 return cc;
1040 uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1042 int cc = 0, i;
1044 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1045 __func__, l, a1, a2);
1047 if (l > 256) {
1048 /* max 256 */
1049 l = 256;
1050 cc = 3;
1053 /* XXX replace w/ memcpy */
1054 for (i = 0; i < l; i++) {
1055 cpu_stb_primary(env, a1 + i, cpu_ldub_secondary(env, a2 + i));
1058 return cc;
1061 /* invalidate pte */
1062 void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1064 CPUState *cs = CPU(s390_env_get_cpu(env));
1065 uint64_t page = vaddr & TARGET_PAGE_MASK;
1066 uint64_t pte = 0;
1068 /* XXX broadcast to other CPUs */
1070 /* XXX Linux is nice enough to give us the exact pte address.
1071 According to spec we'd have to find it out ourselves */
1072 /* XXX Linux is fine with overwriting the pte, the spec requires
1073 us to only set the invalid bit */
1074 stq_phys(cs->as, pte_addr, pte | _PAGE_INVALID);
1076 /* XXX we exploit the fact that Linux passes the exact virtual
1077 address here - it's not obliged to! */
1078 tlb_flush_page(cs, page);
1080 /* XXX 31-bit hack */
1081 if (page & 0x80000000) {
1082 tlb_flush_page(cs, page & ~0x80000000);
1083 } else {
1084 tlb_flush_page(cs, page | 0x80000000);
1088 /* flush local tlb */
1089 void HELPER(ptlb)(CPUS390XState *env)
1091 S390CPU *cpu = s390_env_get_cpu(env);
1093 tlb_flush(CPU(cpu), 1);
1096 /* load using real address */
1097 uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr)
1099 CPUState *cs = CPU(s390_env_get_cpu(env));
1101 return (uint32_t)ldl_phys(cs->as, get_address(env, 0, 0, addr));
1104 uint64_t HELPER(lurag)(CPUS390XState *env, uint64_t addr)
1106 CPUState *cs = CPU(s390_env_get_cpu(env));
1108 return ldq_phys(cs->as, get_address(env, 0, 0, addr));
1111 /* store using real address */
1112 void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1114 CPUState *cs = CPU(s390_env_get_cpu(env));
1116 stl_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1);
1119 void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1121 CPUState *cs = CPU(s390_env_get_cpu(env));
1123 stq_phys(cs->as, get_address(env, 0, 0, addr), v1);
1126 /* load real address */
1127 uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1129 CPUState *cs = CPU(s390_env_get_cpu(env));
1130 uint32_t cc = 0;
1131 int old_exc = cs->exception_index;
1132 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1133 uint64_t ret;
1134 int flags;
1136 /* XXX incomplete - has more corner cases */
1137 if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1138 program_interrupt(env, PGM_SPECIAL_OP, 2);
1141 cs->exception_index = old_exc;
1142 if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) {
1143 cc = 3;
1145 if (cs->exception_index == EXCP_PGM) {
1146 ret = env->int_pgm_code | 0x80000000;
1147 } else {
1148 ret |= addr & ~TARGET_PAGE_MASK;
1150 cs->exception_index = old_exc;
1152 env->cc_op = cc;
1153 return ret;
1155 #endif