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/>.
24 /*****************************************************************************/
26 #if !defined(CONFIG_USER_ONLY)
27 #include "exec/softmmu_exec.h"
29 #define MMUSUFFIX _mmu
32 #include "exec/softmmu_template.h"
35 #include "exec/softmmu_template.h"
38 #include "exec/softmmu_template.h"
41 #include "exec/softmmu_template.h"
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
,
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
);
64 /* #define DEBUG_HELPER */
66 #define HELPER_LOG(x...) qemu_log(x)
68 #define HELPER_LOG(x...)
71 #ifndef CONFIG_USER_ONLY
72 static void mvc_fast_memset(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
78 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
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");
85 dest_phys
|= dest
& ~TARGET_PAGE_MASK
;
87 dest_p
= cpu_physical_memory_map(dest_phys
, &len
, 1);
89 memset(dest_p
, byte
, len
);
91 cpu_physical_memory_unmap(dest_p
, 1, len
, len
);
94 static void mvc_fast_memmove(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
102 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
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");
109 dest_phys
|= dest
& ~TARGET_PAGE_MASK
;
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");
115 src_phys
|= src
& ~TARGET_PAGE_MASK
;
117 dest_p
= cpu_physical_memory_map(dest_phys
, &len
, 1);
118 src_p
= cpu_physical_memory_map(src_phys
, &len
, 0);
120 memmove(dest_p
, src_p
, len
);
122 cpu_physical_memory_unmap(dest_p
, 1, len
, len
);
123 cpu_physical_memory_unmap(src_p
, 0, len
, len
);
128 uint32_t HELPER(nc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
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
);
142 cpu_stb_data(env
, dest
+ i
, x
);
148 uint32_t HELPER(xc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
155 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
156 __func__
, l
, dest
, src
);
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);
167 memset(g2h(dest
), 0, l
+ 1);
172 for (i
= 0; i
<= l
; i
++) {
173 x
= cpu_ldub_data(env
, dest
+ i
) ^ cpu_ldub_data(env
, src
+ i
);
177 cpu_stb_data(env
, dest
+ i
, x
);
183 uint32_t HELPER(oc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
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
);
197 cpu_stb_data(env
, dest
+ i
, x
);
203 void HELPER(mvc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
, uint64_t src
)
207 uint32_t l_64
= (l
+ 1) / 8;
209 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
210 __func__
, l
, dest
, src
);
212 #ifndef CONFIG_USER_ONLY
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
));
219 } else if ((src
& TARGET_PAGE_MASK
) != (dest
& TARGET_PAGE_MASK
)) {
220 mvc_fast_memmove(env
, l
+ 1, dest
, src
);
225 if (dest
== (src
+ 1)) {
226 memset(g2h(dest
), cpu_ldub_data(env
, src
), l
+ 1);
229 memmove(g2h(dest
), g2h(src
), l
+ 1);
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
));
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
));
248 /* compare unsigned byte arrays */
249 uint32_t HELPER(clc
)(CPUS390XState
*env
, uint32_t l
, uint64_t s1
, uint64_t s2
)
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
);
275 /* compare logical under mask */
276 uint32_t HELPER(clm
)(CPUS390XState
*env
, uint32_t r1
, uint32_t mask
,
282 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64
"\n", __func__
, r1
,
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
,
300 mask
= (mask
<< 1) & 0xf;
307 /* store character under mask */
308 void HELPER(stcm
)(CPUS390XState
*env
, uint32_t r1
, uint32_t mask
,
313 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%lx\n", __func__
, r1
, mask
,
317 r
= (r1
& 0xff000000UL
) >> 24;
318 cpu_stb_data(env
, addr
, r
);
319 HELPER_LOG("mask 0x%x %02x (0x%lx) ", mask
, r
, addr
);
322 mask
= (mask
<< 1) & 0xf;
328 static inline uint64_t get_address(CPUS390XState
*env
, int x2
, int b2
, int d2
)
341 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
348 static inline uint64_t get_address_31fix(CPUS390XState
*env
, int reg
)
350 uint64_t r
= env
->regs
[reg
];
353 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
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
)
365 uint64_t str
= get_address_31fix(env
, r2
);
366 uint64_t end
= get_address_31fix(env
, r1
);
368 HELPER_LOG("%s: c %d *r1 0x%" PRIx64
" *r2 0x%" PRIx64
"\n", __func__
,
369 c
, env
->regs
[r1
], env
->regs
[r2
]);
371 for (i
= str
; i
!= end
; i
++) {
372 if (cpu_ldub_data(env
, i
) == c
) {
382 /* unsigned string compare (c is string terminator) */
383 uint32_t HELPER(clst
)(CPUS390XState
*env
, uint32_t c
, uint32_t r1
, uint32_t r2
)
385 uint64_t s1
= get_address_31fix(env
, r1
);
386 uint64_t s2
= get_address_31fix(env
, r2
);
391 #ifdef CONFIG_USER_ONLY
393 HELPER_LOG("%s: comparing '%s' and '%s'\n",
394 __func__
, (char *)g2h(s1
), (char *)g2h(s2
));
398 v1
= cpu_ldub_data(env
, s1
);
399 v2
= cpu_ldub_data(env
, s2
);
400 if ((v1
== c
|| v2
== c
) || (v1
!= v2
)) {
410 cc
= (v1
< v2
) ? 1 : 2;
411 /* FIXME: 31-bit mode! */
419 void HELPER(mvpg
)(CPUS390XState
*env
, uint64_t r0
, uint64_t r1
, uint64_t r2
)
421 /* XXX missing r0 handling */
422 #ifdef CONFIG_USER_ONLY
425 for (i
= 0; i
< TARGET_PAGE_SIZE
; i
++) {
426 cpu_stb_data(env
, r1
+ i
, cpu_ldub_data(env
, r2
+ i
));
429 mvc_fast_memmove(env
, TARGET_PAGE_SIZE
, r1
, r2
);
433 /* string copy (c is string terminator) */
434 void HELPER(mvst
)(CPUS390XState
*env
, uint32_t c
, uint32_t r1
, uint32_t r2
)
436 uint64_t dest
= get_address_31fix(env
, r1
);
437 uint64_t src
= get_address_31fix(env
, r2
);
441 #ifdef CONFIG_USER_ONLY
443 HELPER_LOG("%s: copy '%s' to 0x%lx\n", __func__
, (char *)g2h(src
),
448 v
= cpu_ldub_data(env
, src
);
449 cpu_stb_data(env
, dest
, v
);
456 env
->regs
[r1
] = dest
; /* FIXME: 31-bit mode! */
459 /* compare and swap 64-bit */
460 uint32_t HELPER(csg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
462 /* FIXME: locking? */
464 uint64_t v2
= cpu_ldq_data(env
, a2
);
466 if (env
->regs
[r1
] == v2
) {
468 cpu_stq_data(env
, a2
, env
->regs
[r3
]);
476 /* compare double and swap 64-bit */
477 uint32_t HELPER(cdsg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
479 /* FIXME: locking? */
481 uint64_t v2_hi
= cpu_ldq_data(env
, a2
);
482 uint64_t v2_lo
= cpu_ldq_data(env
, a2
+ 8);
483 uint64_t v1_hi
= env
->regs
[r1
];
484 uint64_t v1_lo
= env
->regs
[r1
+ 1];
486 if ((v1_hi
== v2_hi
) && (v1_lo
== v2_lo
)) {
488 cpu_stq_data(env
, a2
, env
->regs
[r3
]);
489 cpu_stq_data(env
, a2
+ 8, env
->regs
[r3
+ 1]);
492 env
->regs
[r1
] = v2_hi
;
493 env
->regs
[r1
+ 1] = v2_lo
;
499 /* compare and swap 32-bit */
500 uint32_t HELPER(cs
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
502 /* FIXME: locking? */
504 uint32_t v2
= cpu_ldl_data(env
, a2
);
506 HELPER_LOG("%s: r1 %d a2 0x%lx r3 %d\n", __func__
, r1
, a2
, r3
);
507 if (((uint32_t)env
->regs
[r1
]) == v2
) {
509 cpu_stl_data(env
, a2
, (uint32_t)env
->regs
[r3
]);
512 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) | v2
;
517 static uint32_t helper_icm(CPUS390XState
*env
, uint32_t r1
, uint64_t address
,
520 int pos
= 24; /* top of the lower half of r1 */
521 uint64_t rmask
= 0xff000000ULL
;
528 env
->regs
[r1
] &= ~rmask
;
529 val
= cpu_ldub_data(env
, address
);
530 if ((val
& 0x80) && !ccd
) {
534 if (val
&& cc
== 0) {
537 env
->regs
[r1
] |= (uint64_t)val
<< pos
;
540 mask
= (mask
<< 1) & 0xf;
548 /* execute instruction
549 this instruction executes an insn modified with the contents of r1
550 it does not change the executed instruction in memory
551 it does not change the program counter
552 in other words: tricky...
553 currently implemented by interpreting the cases it is most commonly used in
555 uint32_t HELPER(ex
)(CPUS390XState
*env
, uint32_t cc
, uint64_t v1
,
556 uint64_t addr
, uint64_t ret
)
558 uint16_t insn
= cpu_lduw_code(env
, addr
);
560 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__
, v1
, addr
,
562 if ((insn
& 0xf0ff) == 0xd000) {
563 uint32_t l
, insn2
, b1
, b2
, d1
, d2
;
566 insn2
= cpu_ldl_code(env
, addr
+ 2);
567 b1
= (insn2
>> 28) & 0xf;
568 b2
= (insn2
>> 12) & 0xf;
569 d1
= (insn2
>> 16) & 0xfff;
571 switch (insn
& 0xf00) {
573 helper_mvc(env
, l
, get_address(env
, 0, b1
, d1
),
574 get_address(env
, 0, b2
, d2
));
577 cc
= helper_clc(env
, l
, get_address(env
, 0, b1
, d1
),
578 get_address(env
, 0, b2
, d2
));
581 cc
= helper_xc(env
, l
, get_address(env
, 0, b1
, d1
),
582 get_address(env
, 0, b2
, d2
));
585 helper_tr(env
, l
, get_address(env
, 0, b1
, d1
),
586 get_address(env
, 0, b2
, d2
));
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(env
, EXCP_SVC
);
599 } else if ((insn
& 0xff00) == 0xbf00) {
600 uint32_t insn2
, r1
, r3
, b2
, d2
;
602 insn2
= cpu_ldl_code(env
, addr
+ 2);
603 r1
= (insn2
>> 20) & 0xf;
604 r3
= (insn2
>> 16) & 0xf;
605 b2
= (insn2
>> 12) & 0xf;
607 cc
= helper_icm(env
, r1
, get_address(env
, 0, b2
, d2
), r3
);
610 cpu_abort(env
, "EXECUTE on instruction prefix 0x%x not implemented\n",
616 /* store character under mask high operates on the upper half of r1 */
617 void HELPER(stcmh
)(CPUS390XState
*env
, uint32_t r1
, uint64_t address
,
620 int pos
= 56; /* top of the upper half of r1 */
624 cpu_stb_data(env
, address
, (env
->regs
[r1
] >> pos
) & 0xff);
627 mask
= (mask
<< 1) & 0xf;
632 /* insert character under mask high; same as icm, but operates on the
634 uint32_t HELPER(icmh
)(CPUS390XState
*env
, uint32_t r1
, uint64_t address
,
637 int pos
= 56; /* top of the upper half of r1 */
638 uint64_t rmask
= 0xff00000000000000ULL
;
645 env
->regs
[r1
] &= ~rmask
;
646 val
= cpu_ldub_data(env
, address
);
647 if ((val
& 0x80) && !ccd
) {
651 if (val
&& cc
== 0) {
654 env
->regs
[r1
] |= (uint64_t)val
<< pos
;
657 mask
= (mask
<< 1) & 0xf;
665 /* load access registers r1 to r3 from memory at a2 */
666 void HELPER(lam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
670 for (i
= r1
;; i
= (i
+ 1) % 16) {
671 env
->aregs
[i
] = cpu_ldl_data(env
, a2
);
680 /* store access registers r1 to r3 in memory at a2 */
681 void HELPER(stam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
685 for (i
= r1
;; i
= (i
+ 1) % 16) {
686 cpu_stl_data(env
, a2
, env
->aregs
[i
]);
696 uint32_t HELPER(mvcl
)(CPUS390XState
*env
, uint32_t r1
, uint32_t r2
)
698 uint64_t destlen
= env
->regs
[r1
+ 1] & 0xffffff;
699 uint64_t dest
= get_address_31fix(env
, r1
);
700 uint64_t srclen
= env
->regs
[r2
+ 1] & 0xffffff;
701 uint64_t src
= get_address_31fix(env
, r2
);
702 uint8_t pad
= src
>> 24;
706 if (destlen
== srclen
) {
708 } else if (destlen
< srclen
) {
714 if (srclen
> destlen
) {
718 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
719 v
= cpu_ldub_data(env
, src
);
720 cpu_stb_data(env
, dest
, v
);
723 for (; destlen
; dest
++, destlen
--) {
724 cpu_stb_data(env
, dest
, pad
);
727 env
->regs
[r1
+ 1] = destlen
;
728 /* can't use srclen here, we trunc'ed it */
729 env
->regs
[r2
+ 1] -= src
- env
->regs
[r2
];
730 env
->regs
[r1
] = dest
;
736 /* move long extended another memcopy insn with more bells and whistles */
737 uint32_t HELPER(mvcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
740 uint64_t destlen
= env
->regs
[r1
+ 1];
741 uint64_t dest
= env
->regs
[r1
];
742 uint64_t srclen
= env
->regs
[r3
+ 1];
743 uint64_t src
= env
->regs
[r3
];
744 uint8_t pad
= a2
& 0xff;
748 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
749 destlen
= (uint32_t)destlen
;
750 srclen
= (uint32_t)srclen
;
755 if (destlen
== srclen
) {
757 } else if (destlen
< srclen
) {
763 if (srclen
> destlen
) {
767 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
768 v
= cpu_ldub_data(env
, src
);
769 cpu_stb_data(env
, dest
, v
);
772 for (; destlen
; dest
++, destlen
--) {
773 cpu_stb_data(env
, dest
, pad
);
776 env
->regs
[r1
+ 1] = destlen
;
777 /* can't use srclen here, we trunc'ed it */
778 /* FIXME: 31-bit mode! */
779 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
780 env
->regs
[r1
] = dest
;
786 /* compare logical long extended memcompare insn with padding */
787 uint32_t HELPER(clcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
790 uint64_t destlen
= env
->regs
[r1
+ 1];
791 uint64_t dest
= get_address_31fix(env
, r1
);
792 uint64_t srclen
= env
->regs
[r3
+ 1];
793 uint64_t src
= get_address_31fix(env
, r3
);
794 uint8_t pad
= a2
& 0xff;
795 uint8_t v1
= 0, v2
= 0;
798 if (!(destlen
|| srclen
)) {
802 if (srclen
> destlen
) {
806 for (; destlen
|| srclen
; src
++, dest
++, destlen
--, srclen
--) {
807 v1
= srclen
? cpu_ldub_data(env
, src
) : pad
;
808 v2
= destlen
? cpu_ldub_data(env
, dest
) : pad
;
810 cc
= (v1
< v2
) ? 1 : 2;
815 env
->regs
[r1
+ 1] = destlen
;
816 /* can't use srclen here, we trunc'ed it */
817 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
818 env
->regs
[r1
] = dest
;
825 void HELPER(cksm
)(CPUS390XState
*env
, uint32_t r1
, uint32_t r2
)
827 uint64_t src
= get_address_31fix(env
, r2
);
828 uint64_t src_len
= env
->regs
[(r2
+ 1) & 15];
829 uint64_t cksm
= (uint32_t)env
->regs
[r1
];
831 while (src_len
>= 4) {
832 cksm
+= cpu_ldl_data(env
, src
);
834 /* move to next word */
843 cksm
+= cpu_ldub_data(env
, src
) << 24;
846 cksm
+= cpu_lduw_data(env
, src
) << 16;
849 cksm
+= cpu_lduw_data(env
, src
) << 16;
850 cksm
+= cpu_ldub_data(env
, src
+ 2) << 8;
854 /* indicate we've processed everything */
855 env
->regs
[r2
] = src
+ src_len
;
856 env
->regs
[(r2
+ 1) & 15] = 0;
859 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) |
860 ((uint32_t)cksm
+ (cksm
>> 32));
863 void HELPER(unpk
)(CPUS390XState
*env
, uint32_t len
, uint64_t dest
,
866 int len_dest
= len
>> 4;
867 int len_src
= len
& 0xf;
869 int second_nibble
= 0;
874 /* last byte is special, it only flips the nibbles */
875 b
= cpu_ldub_data(env
, src
);
876 cpu_stb_data(env
, dest
, (b
<< 4) | (b
>> 4));
880 /* now pad every nibble with 0xf0 */
882 while (len_dest
> 0) {
883 uint8_t cur_byte
= 0;
886 cur_byte
= cpu_ldub_data(env
, src
);
892 /* only advance one nibble at a time */
898 second_nibble
= !second_nibble
;
901 cur_byte
= (cur_byte
& 0xf);
905 cpu_stb_data(env
, dest
, cur_byte
);
909 void HELPER(tr
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
914 for (i
= 0; i
<= len
; i
++) {
915 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
916 uint8_t new_byte
= cpu_ldub_data(env
, trans
+ byte
);
918 cpu_stb_data(env
, array
+ i
, new_byte
);
922 #if !defined(CONFIG_USER_ONLY)
923 void HELPER(lctlg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
928 for (i
= r1
;; i
= (i
+ 1) % 16) {
929 env
->cregs
[i
] = cpu_ldq_data(env
, src
);
930 HELPER_LOG("load ctl %d from 0x%" PRIx64
" == 0x%" PRIx64
"\n",
931 i
, src
, env
->cregs
[i
]);
932 src
+= sizeof(uint64_t);
942 void HELPER(lctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
947 for (i
= r1
;; i
= (i
+ 1) % 16) {
948 env
->cregs
[i
] = (env
->cregs
[i
] & 0xFFFFFFFF00000000ULL
) |
949 cpu_ldl_data(env
, src
);
950 src
+= sizeof(uint32_t);
960 void HELPER(stctg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
965 for (i
= r1
;; i
= (i
+ 1) % 16) {
966 cpu_stq_data(env
, dest
, env
->cregs
[i
]);
967 dest
+= sizeof(uint64_t);
975 void HELPER(stctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
980 for (i
= r1
;; i
= (i
+ 1) % 16) {
981 cpu_stl_data(env
, dest
, env
->cregs
[i
]);
982 dest
+= sizeof(uint32_t);
990 uint32_t HELPER(tprot
)(uint64_t a1
, uint64_t a2
)
997 /* insert storage key extended */
998 uint64_t HELPER(iske
)(CPUS390XState
*env
, uint64_t r2
)
1000 uint64_t addr
= get_address(env
, 0, 0, r2
);
1002 if (addr
> ram_size
) {
1006 return env
->storage_keys
[addr
/ TARGET_PAGE_SIZE
];
1009 /* set storage key extended */
1010 void HELPER(sske
)(CPUS390XState
*env
, uint32_t r1
, uint64_t r2
)
1012 uint64_t addr
= get_address(env
, 0, 0, r2
);
1014 if (addr
> ram_size
) {
1018 env
->storage_keys
[addr
/ TARGET_PAGE_SIZE
] = r1
;
1021 /* reset reference bit extended */
1022 uint32_t HELPER(rrbe
)(CPUS390XState
*env
, uint32_t r1
, uint64_t r2
)
1027 if (r2
> ram_size
) {
1031 key
= env
->storage_keys
[r2
/ TARGET_PAGE_SIZE
];
1032 re
= key
& (SK_R
| SK_C
);
1033 env
->storage_keys
[r2
/ TARGET_PAGE_SIZE
] = (key
& ~SK_R
);
1038 * 0 Reference bit zero; change bit zero
1039 * 1 Reference bit zero; change bit one
1040 * 2 Reference bit one; change bit zero
1041 * 3 Reference bit one; change bit one
1047 /* compare and swap and purge */
1048 uint32_t HELPER(csp
)(CPUS390XState
*env
, uint32_t r1
, uint32_t r2
)
1051 uint32_t o1
= env
->regs
[r1
];
1052 uint64_t a2
= get_address_31fix(env
, r2
) & ~3ULL;
1053 uint32_t o2
= cpu_ldl_data(env
, a2
);
1056 cpu_stl_data(env
, a2
, env
->regs
[(r1
+ 1) & 15]);
1057 if (env
->regs
[r2
] & 0x3) {
1058 /* flush TLB / ALB */
1063 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) | o2
;
1070 static uint32_t mvc_asc(CPUS390XState
*env
, int64_t l
, uint64_t a1
,
1071 uint64_t mode1
, uint64_t a2
, uint64_t mode2
)
1073 target_ulong src
, dest
;
1074 int flags
, cc
= 0, i
;
1078 } else if (l
> 256) {
1084 if (mmu_translate(env
, a1
& TARGET_PAGE_MASK
, 1, mode1
, &dest
, &flags
)) {
1087 dest
|= a1
& ~TARGET_PAGE_MASK
;
1089 if (mmu_translate(env
, a2
& TARGET_PAGE_MASK
, 0, mode2
, &src
, &flags
)) {
1092 src
|= a2
& ~TARGET_PAGE_MASK
;
1094 /* XXX replace w/ memcpy */
1095 for (i
= 0; i
< l
; i
++) {
1096 /* XXX be more clever */
1097 if ((((dest
+ i
) & TARGET_PAGE_MASK
) != (dest
& TARGET_PAGE_MASK
)) ||
1098 (((src
+ i
) & TARGET_PAGE_MASK
) != (src
& TARGET_PAGE_MASK
))) {
1099 mvc_asc(env
, l
- i
, a1
+ i
, mode1
, a2
+ i
, mode2
);
1102 stb_phys(dest
+ i
, ldub_phys(src
+ i
));
1108 uint32_t HELPER(mvcs
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1110 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1111 __func__
, l
, a1
, a2
);
1113 return mvc_asc(env
, l
, a1
, PSW_ASC_SECONDARY
, a2
, PSW_ASC_PRIMARY
);
1116 uint32_t HELPER(mvcp
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1118 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1119 __func__
, l
, a1
, a2
);
1121 return mvc_asc(env
, l
, a1
, PSW_ASC_PRIMARY
, a2
, PSW_ASC_SECONDARY
);
1124 /* invalidate pte */
1125 void HELPER(ipte
)(CPUS390XState
*env
, uint64_t pte_addr
, uint64_t vaddr
)
1127 uint64_t page
= vaddr
& TARGET_PAGE_MASK
;
1130 /* XXX broadcast to other CPUs */
1132 /* XXX Linux is nice enough to give us the exact pte address.
1133 According to spec we'd have to find it out ourselves */
1134 /* XXX Linux is fine with overwriting the pte, the spec requires
1135 us to only set the invalid bit */
1136 stq_phys(pte_addr
, pte
| _PAGE_INVALID
);
1138 /* XXX we exploit the fact that Linux passes the exact virtual
1139 address here - it's not obliged to! */
1140 tlb_flush_page(env
, page
);
1142 /* XXX 31-bit hack */
1143 if (page
& 0x80000000) {
1144 tlb_flush_page(env
, page
& ~0x80000000);
1146 tlb_flush_page(env
, page
| 0x80000000);
1150 /* flush local tlb */
1151 void HELPER(ptlb
)(CPUS390XState
*env
)
1156 /* store using real address */
1157 void HELPER(stura
)(CPUS390XState
*env
, uint64_t addr
, uint32_t v1
)
1159 stw_phys(get_address(env
, 0, 0, addr
), v1
);
1162 /* load real address */
1163 uint32_t HELPER(lra
)(CPUS390XState
*env
, uint64_t addr
, uint32_t r1
)
1166 int old_exc
= env
->exception_index
;
1167 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
1171 /* XXX incomplete - has more corner cases */
1172 if (!(env
->psw
.mask
& PSW_MASK_64
) && (addr
>> 32)) {
1173 program_interrupt(env
, PGM_SPECIAL_OP
, 2);
1176 env
->exception_index
= old_exc
;
1177 if (mmu_translate(env
, addr
, 0, asc
, &ret
, &flags
)) {
1180 if (env
->exception_index
== EXCP_PGM
) {
1181 ret
= env
->int_pgm_code
| 0x80000000;
1183 ret
|= addr
& ~TARGET_PAGE_MASK
;
1185 env
->exception_index
= old_exc
;
1187 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
1188 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) |
1189 (ret
& 0xffffffffULL
);
1191 env
->regs
[r1
] = ret
;