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/>.
22 #include "exec/helper-proto.h"
23 #include "exec/cpu_ldst.h"
24 #include "hw/s390x/storage-keys.h"
26 /*****************************************************************************/
28 #if !defined(CONFIG_USER_ONLY)
30 /* try to fill the TLB and return an exception if error. If retaddr is
31 NULL, it means that the function was called in C code (i.e. not
32 from generated code or from helper.c) */
33 /* XXX: fix it to restore all registers */
34 void tlb_fill(CPUState
*cs
, target_ulong addr
, int is_write
, int mmu_idx
,
39 ret
= s390_cpu_handle_mmu_fault(cs
, addr
, is_write
, mmu_idx
);
40 if (unlikely(ret
!= 0)) {
41 if (likely(retaddr
)) {
42 /* now we have a real cpu fault */
43 cpu_restore_state(cs
, retaddr
);
51 /* #define DEBUG_HELPER */
53 #define HELPER_LOG(x...) qemu_log(x)
55 #define HELPER_LOG(x...)
58 /* Reduce the length so that addr + len doesn't cross a page boundary. */
59 static inline uint64_t adj_len_to_page(uint64_t len
, uint64_t addr
)
61 #ifndef CONFIG_USER_ONLY
62 if ((addr
& ~TARGET_PAGE_MASK
) + len
- 1 >= TARGET_PAGE_SIZE
) {
63 return -addr
& ~TARGET_PAGE_MASK
;
69 static void fast_memset(CPUS390XState
*env
, uint64_t dest
, uint8_t byte
,
72 int mmu_idx
= cpu_mmu_index(env
, false);
75 void *p
= tlb_vaddr_to_host(env
, dest
, MMU_DATA_STORE
, mmu_idx
);
77 /* Access to the whole page in write mode granted. */
78 int l_adj
= adj_len_to_page(l
, dest
);
79 memset(p
, byte
, l_adj
);
83 /* We failed to get access to the whole page. The next write
84 access will likely fill the QEMU TLB for the next iteration. */
85 cpu_stb_data(env
, dest
, byte
);
92 static void fast_memmove(CPUS390XState
*env
, uint64_t dest
, uint64_t src
,
95 int mmu_idx
= cpu_mmu_index(env
, false);
98 void *src_p
= tlb_vaddr_to_host(env
, src
, MMU_DATA_LOAD
, mmu_idx
);
99 void *dest_p
= tlb_vaddr_to_host(env
, dest
, MMU_DATA_STORE
, mmu_idx
);
100 if (src_p
&& dest_p
) {
101 /* Access to both whole pages granted. */
102 int l_adj
= adj_len_to_page(l
, src
);
103 l_adj
= adj_len_to_page(l_adj
, dest
);
104 memmove(dest_p
, src_p
, l_adj
);
109 /* We failed to get access to one or both whole pages. The next
110 read or write access will likely fill the QEMU TLB for the
112 cpu_stb_data(env
, dest
, cpu_ldub_data(env
, src
));
121 uint32_t HELPER(nc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
128 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
129 __func__
, l
, dest
, src
);
130 for (i
= 0; i
<= l
; i
++) {
131 x
= cpu_ldub_data(env
, dest
+ i
) & cpu_ldub_data(env
, src
+ i
);
135 cpu_stb_data(env
, dest
+ i
, x
);
141 uint32_t HELPER(xc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
148 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
149 __func__
, l
, dest
, src
);
151 /* xor with itself is the same as memset(0) */
153 fast_memset(env
, dest
, 0, l
+ 1);
157 for (i
= 0; i
<= l
; i
++) {
158 x
= cpu_ldub_data(env
, dest
+ i
) ^ cpu_ldub_data(env
, src
+ i
);
162 cpu_stb_data(env
, dest
+ i
, x
);
168 uint32_t HELPER(oc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
175 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
176 __func__
, l
, dest
, src
);
177 for (i
= 0; i
<= l
; i
++) {
178 x
= cpu_ldub_data(env
, dest
+ i
) | cpu_ldub_data(env
, src
+ i
);
182 cpu_stb_data(env
, dest
+ i
, x
);
188 void HELPER(mvc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
, uint64_t src
)
192 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
193 __func__
, l
, dest
, src
);
195 /* mvc with source pointing to the byte after the destination is the
196 same as memset with the first source byte */
197 if (dest
== (src
+ 1)) {
198 fast_memset(env
, dest
, cpu_ldub_data(env
, src
), l
+ 1);
202 /* mvc and memmove do not behave the same when areas overlap! */
203 if ((dest
< src
) || (src
+ l
< dest
)) {
204 fast_memmove(env
, dest
, src
, l
+ 1);
208 /* slow version with byte accesses which always work */
209 for (i
= 0; i
<= l
; i
++) {
210 cpu_stb_data(env
, dest
+ i
, cpu_ldub_data(env
, src
+ i
));
214 /* compare unsigned byte arrays */
215 uint32_t HELPER(clc
)(CPUS390XState
*env
, uint32_t l
, uint64_t s1
, uint64_t s2
)
221 HELPER_LOG("%s l %d s1 %" PRIx64
" s2 %" PRIx64
"\n",
222 __func__
, l
, s1
, s2
);
223 for (i
= 0; i
<= l
; i
++) {
224 x
= cpu_ldub_data(env
, s1
+ i
);
225 y
= cpu_ldub_data(env
, s2
+ i
);
226 HELPER_LOG("%02x (%c)/%02x (%c) ", x
, x
, y
, y
);
241 /* compare logical under mask */
242 uint32_t HELPER(clm
)(CPUS390XState
*env
, uint32_t r1
, uint32_t mask
,
248 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64
"\n", __func__
, r1
,
253 d
= cpu_ldub_data(env
, addr
);
254 r
= (r1
& 0xff000000UL
) >> 24;
255 HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64
") ", mask
, r
, d
,
266 mask
= (mask
<< 1) & 0xf;
273 static inline uint64_t fix_address(CPUS390XState
*env
, uint64_t a
)
276 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
282 static inline uint64_t get_address(CPUS390XState
*env
, int x2
, int b2
, int d2
)
291 return fix_address(env
, r
);
294 static inline uint64_t get_address_31fix(CPUS390XState
*env
, int reg
)
296 return fix_address(env
, env
->regs
[reg
]);
299 /* search string (c is byte to search, r2 is string, r1 end of string) */
300 uint64_t HELPER(srst
)(CPUS390XState
*env
, uint64_t r0
, uint64_t end
,
306 str
= fix_address(env
, str
);
307 end
= fix_address(env
, end
);
309 /* Assume for now that R2 is unmodified. */
312 /* Lest we fail to service interrupts in a timely manner, limit the
313 amount of work we're willing to do. For now, let's cap at 8k. */
314 for (len
= 0; len
< 0x2000; ++len
) {
315 if (str
+ len
== end
) {
316 /* Character not found. R1 & R2 are unmodified. */
320 v
= cpu_ldub_data(env
, str
+ len
);
322 /* Character found. Set R1 to the location; R2 is unmodified. */
328 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
329 env
->retxl
= str
+ len
;
334 /* unsigned string compare (c is string terminator) */
335 uint64_t HELPER(clst
)(CPUS390XState
*env
, uint64_t c
, uint64_t s1
, uint64_t s2
)
340 s1
= fix_address(env
, s1
);
341 s2
= fix_address(env
, s2
);
343 /* Lest we fail to service interrupts in a timely manner, limit the
344 amount of work we're willing to do. For now, let's cap at 8k. */
345 for (len
= 0; len
< 0x2000; ++len
) {
346 uint8_t v1
= cpu_ldub_data(env
, s1
+ len
);
347 uint8_t v2
= cpu_ldub_data(env
, s2
+ len
);
350 /* Equal. CC=0, and don't advance the registers. */
356 /* Unequal. CC={1,2}, and advance the registers. Note that
357 the terminator need not be zero, but the string that contains
358 the terminator is by definition "low". */
359 env
->cc_op
= (v1
== c
? 1 : v2
== c
? 2 : v1
< v2
? 1 : 2);
360 env
->retxl
= s2
+ len
;
365 /* CPU-determined bytes equal; advance the registers. */
367 env
->retxl
= s2
+ len
;
372 void HELPER(mvpg
)(CPUS390XState
*env
, uint64_t r0
, uint64_t r1
, uint64_t r2
)
374 /* XXX missing r0 handling */
376 fast_memmove(env
, r1
, r2
, TARGET_PAGE_SIZE
);
379 /* string copy (c is string terminator) */
380 uint64_t HELPER(mvst
)(CPUS390XState
*env
, uint64_t c
, uint64_t d
, uint64_t s
)
385 d
= fix_address(env
, d
);
386 s
= fix_address(env
, s
);
388 /* Lest we fail to service interrupts in a timely manner, limit the
389 amount of work we're willing to do. For now, let's cap at 8k. */
390 for (len
= 0; len
< 0x2000; ++len
) {
391 uint8_t v
= cpu_ldub_data(env
, s
+ len
);
392 cpu_stb_data(env
, d
+ len
, v
);
394 /* Complete. Set CC=1 and advance R1. */
401 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
403 env
->retxl
= s
+ len
;
407 static uint32_t helper_icm(CPUS390XState
*env
, uint32_t r1
, uint64_t address
,
410 int pos
= 24; /* top of the lower half of r1 */
411 uint64_t rmask
= 0xff000000ULL
;
418 env
->regs
[r1
] &= ~rmask
;
419 val
= cpu_ldub_data(env
, address
);
420 if ((val
& 0x80) && !ccd
) {
424 if (val
&& cc
== 0) {
427 env
->regs
[r1
] |= (uint64_t)val
<< pos
;
430 mask
= (mask
<< 1) & 0xf;
438 /* execute instruction
439 this instruction executes an insn modified with the contents of r1
440 it does not change the executed instruction in memory
441 it does not change the program counter
442 in other words: tricky...
443 currently implemented by interpreting the cases it is most commonly used in
445 uint32_t HELPER(ex
)(CPUS390XState
*env
, uint32_t cc
, uint64_t v1
,
446 uint64_t addr
, uint64_t ret
)
448 S390CPU
*cpu
= s390_env_get_cpu(env
);
449 uint16_t insn
= cpu_lduw_code(env
, addr
);
451 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__
, v1
, addr
,
453 if ((insn
& 0xf0ff) == 0xd000) {
454 uint32_t l
, insn2
, b1
, b2
, d1
, d2
;
457 insn2
= cpu_ldl_code(env
, addr
+ 2);
458 b1
= (insn2
>> 28) & 0xf;
459 b2
= (insn2
>> 12) & 0xf;
460 d1
= (insn2
>> 16) & 0xfff;
462 switch (insn
& 0xf00) {
464 helper_mvc(env
, l
, get_address(env
, 0, b1
, d1
),
465 get_address(env
, 0, b2
, d2
));
468 cc
= helper_nc(env
, l
, get_address(env
, 0, b1
, d1
),
469 get_address(env
, 0, b2
, d2
));
472 cc
= helper_clc(env
, l
, get_address(env
, 0, b1
, d1
),
473 get_address(env
, 0, b2
, d2
));
476 cc
= helper_oc(env
, l
, get_address(env
, 0, b1
, d1
),
477 get_address(env
, 0, b2
, d2
));
480 cc
= helper_xc(env
, l
, get_address(env
, 0, b1
, d1
),
481 get_address(env
, 0, b2
, d2
));
484 helper_tr(env
, l
, get_address(env
, 0, b1
, d1
),
485 get_address(env
, 0, b2
, d2
));
488 cc
= helper_trt(env
, l
, get_address(env
, 0, b1
, d1
),
489 get_address(env
, 0, b2
, d2
));
494 } else if ((insn
& 0xff00) == 0x0a00) {
495 /* supervisor call */
496 HELPER_LOG("%s: svc %ld via execute\n", __func__
, (insn
| v1
) & 0xff);
497 env
->psw
.addr
= ret
- 4;
498 env
->int_svc_code
= (insn
| v1
) & 0xff;
499 env
->int_svc_ilen
= 4;
500 helper_exception(env
, EXCP_SVC
);
501 } else if ((insn
& 0xff00) == 0xbf00) {
502 uint32_t insn2
, r1
, r3
, b2
, d2
;
504 insn2
= cpu_ldl_code(env
, addr
+ 2);
505 r1
= (insn2
>> 20) & 0xf;
506 r3
= (insn2
>> 16) & 0xf;
507 b2
= (insn2
>> 12) & 0xf;
509 cc
= helper_icm(env
, r1
, get_address(env
, 0, b2
, d2
), r3
);
512 cpu_abort(CPU(cpu
), "EXECUTE on instruction prefix 0x%x not implemented\n",
518 /* load access registers r1 to r3 from memory at a2 */
519 void HELPER(lam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
523 for (i
= r1
;; i
= (i
+ 1) % 16) {
524 env
->aregs
[i
] = cpu_ldl_data(env
, a2
);
533 /* store access registers r1 to r3 in memory at a2 */
534 void HELPER(stam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
538 for (i
= r1
;; i
= (i
+ 1) % 16) {
539 cpu_stl_data(env
, a2
, env
->aregs
[i
]);
549 uint32_t HELPER(mvcl
)(CPUS390XState
*env
, uint32_t r1
, uint32_t r2
)
551 uint64_t destlen
= env
->regs
[r1
+ 1] & 0xffffff;
552 uint64_t dest
= get_address_31fix(env
, r1
);
553 uint64_t srclen
= env
->regs
[r2
+ 1] & 0xffffff;
554 uint64_t src
= get_address_31fix(env
, r2
);
555 uint8_t pad
= env
->regs
[r2
+ 1] >> 24;
559 if (destlen
== srclen
) {
561 } else if (destlen
< srclen
) {
567 if (srclen
> destlen
) {
571 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
572 v
= cpu_ldub_data(env
, src
);
573 cpu_stb_data(env
, dest
, v
);
576 for (; destlen
; dest
++, destlen
--) {
577 cpu_stb_data(env
, dest
, pad
);
580 env
->regs
[r1
+ 1] = destlen
;
581 /* can't use srclen here, we trunc'ed it */
582 env
->regs
[r2
+ 1] -= src
- env
->regs
[r2
];
583 env
->regs
[r1
] = dest
;
589 /* move long extended another memcopy insn with more bells and whistles */
590 uint32_t HELPER(mvcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
593 uint64_t destlen
= env
->regs
[r1
+ 1];
594 uint64_t dest
= env
->regs
[r1
];
595 uint64_t srclen
= env
->regs
[r3
+ 1];
596 uint64_t src
= env
->regs
[r3
];
597 uint8_t pad
= a2
& 0xff;
601 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
602 destlen
= (uint32_t)destlen
;
603 srclen
= (uint32_t)srclen
;
608 if (destlen
== srclen
) {
610 } else if (destlen
< srclen
) {
616 if (srclen
> destlen
) {
620 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
621 v
= cpu_ldub_data(env
, src
);
622 cpu_stb_data(env
, dest
, v
);
625 for (; destlen
; dest
++, destlen
--) {
626 cpu_stb_data(env
, dest
, pad
);
629 env
->regs
[r1
+ 1] = destlen
;
630 /* can't use srclen here, we trunc'ed it */
631 /* FIXME: 31-bit mode! */
632 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
633 env
->regs
[r1
] = dest
;
639 /* compare logical long extended memcompare insn with padding */
640 uint32_t HELPER(clcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
643 uint64_t destlen
= env
->regs
[r1
+ 1];
644 uint64_t dest
= get_address_31fix(env
, r1
);
645 uint64_t srclen
= env
->regs
[r3
+ 1];
646 uint64_t src
= get_address_31fix(env
, r3
);
647 uint8_t pad
= a2
& 0xff;
648 uint8_t v1
= 0, v2
= 0;
651 if (!(destlen
|| srclen
)) {
655 if (srclen
> destlen
) {
659 for (; destlen
|| srclen
; src
++, dest
++, destlen
--, srclen
--) {
660 v1
= srclen
? cpu_ldub_data(env
, src
) : pad
;
661 v2
= destlen
? cpu_ldub_data(env
, dest
) : pad
;
663 cc
= (v1
< v2
) ? 1 : 2;
668 env
->regs
[r1
+ 1] = destlen
;
669 /* can't use srclen here, we trunc'ed it */
670 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
671 env
->regs
[r1
] = dest
;
678 uint64_t HELPER(cksm
)(CPUS390XState
*env
, uint64_t r1
,
679 uint64_t src
, uint64_t src_len
)
681 uint64_t max_len
, len
;
682 uint64_t cksm
= (uint32_t)r1
;
684 /* Lest we fail to service interrupts in a timely manner, limit the
685 amount of work we're willing to do. For now, let's cap at 8k. */
686 max_len
= (src_len
> 0x2000 ? 0x2000 : src_len
);
688 /* Process full words as available. */
689 for (len
= 0; len
+ 4 <= max_len
; len
+= 4, src
+= 4) {
690 cksm
+= (uint32_t)cpu_ldl_data(env
, src
);
693 switch (max_len
- len
) {
695 cksm
+= cpu_ldub_data(env
, src
) << 24;
699 cksm
+= cpu_lduw_data(env
, src
) << 16;
703 cksm
+= cpu_lduw_data(env
, src
) << 16;
704 cksm
+= cpu_ldub_data(env
, src
+ 2) << 8;
709 /* Fold the carry from the checksum. Note that we can see carry-out
710 during folding more than once (but probably not more than twice). */
711 while (cksm
> 0xffffffffull
) {
712 cksm
= (uint32_t)cksm
+ (cksm
>> 32);
715 /* Indicate whether or not we've processed everything. */
716 env
->cc_op
= (len
== src_len
? 0 : 3);
718 /* Return both cksm and processed length. */
723 void HELPER(unpk
)(CPUS390XState
*env
, uint32_t len
, uint64_t dest
,
726 int len_dest
= len
>> 4;
727 int len_src
= len
& 0xf;
729 int second_nibble
= 0;
734 /* last byte is special, it only flips the nibbles */
735 b
= cpu_ldub_data(env
, src
);
736 cpu_stb_data(env
, dest
, (b
<< 4) | (b
>> 4));
740 /* now pad every nibble with 0xf0 */
742 while (len_dest
> 0) {
743 uint8_t cur_byte
= 0;
746 cur_byte
= cpu_ldub_data(env
, src
);
752 /* only advance one nibble at a time */
758 second_nibble
= !second_nibble
;
761 cur_byte
= (cur_byte
& 0xf);
765 cpu_stb_data(env
, dest
, cur_byte
);
769 void HELPER(tr
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
774 for (i
= 0; i
<= len
; i
++) {
775 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
776 uint8_t new_byte
= cpu_ldub_data(env
, trans
+ byte
);
778 cpu_stb_data(env
, array
+ i
, new_byte
);
782 uint64_t HELPER(tre
)(CPUS390XState
*env
, uint64_t array
,
783 uint64_t len
, uint64_t trans
)
785 uint8_t end
= env
->regs
[0] & 0xff;
789 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
794 /* Lest we fail to service interrupts in a timely manner, limit the
795 amount of work we're willing to do. For now, let's cap at 8k. */
803 for (i
= 0; i
< l
; i
++) {
804 uint8_t byte
, new_byte
;
806 byte
= cpu_ldub_data(env
, array
+ i
);
813 new_byte
= cpu_ldub_data(env
, trans
+ byte
);
814 cpu_stb_data(env
, array
+ i
, new_byte
);
817 env
->retxl
= len
- i
;
821 uint32_t HELPER(trt
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
827 for (i
= 0; i
<= len
; i
++) {
828 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
829 uint8_t sbyte
= cpu_ldub_data(env
, trans
+ byte
);
832 env
->regs
[1] = array
+ i
;
833 env
->regs
[2] = (env
->regs
[2] & ~0xff) | sbyte
;
834 cc
= (i
== len
) ? 2 : 1;
842 #if !defined(CONFIG_USER_ONLY)
843 void HELPER(lctlg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
845 S390CPU
*cpu
= s390_env_get_cpu(env
);
846 bool PERchanged
= false;
851 for (i
= r1
;; i
= (i
+ 1) % 16) {
852 val
= cpu_ldq_data(env
, src
);
853 if (env
->cregs
[i
] != val
&& i
>= 9 && i
<= 11) {
857 HELPER_LOG("load ctl %d from 0x%" PRIx64
" == 0x%" PRIx64
"\n",
858 i
, src
, env
->cregs
[i
]);
859 src
+= sizeof(uint64_t);
866 if (PERchanged
&& env
->psw
.mask
& PSW_MASK_PER
) {
867 s390_cpu_recompute_watchpoints(CPU(cpu
));
870 tlb_flush(CPU(cpu
), 1);
873 void HELPER(lctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
875 S390CPU
*cpu
= s390_env_get_cpu(env
);
876 bool PERchanged
= false;
881 for (i
= r1
;; i
= (i
+ 1) % 16) {
882 val
= cpu_ldl_data(env
, src
);
883 if ((uint32_t)env
->cregs
[i
] != val
&& i
>= 9 && i
<= 11) {
886 env
->cregs
[i
] = (env
->cregs
[i
] & 0xFFFFFFFF00000000ULL
) | val
;
887 src
+= sizeof(uint32_t);
894 if (PERchanged
&& env
->psw
.mask
& PSW_MASK_PER
) {
895 s390_cpu_recompute_watchpoints(CPU(cpu
));
898 tlb_flush(CPU(cpu
), 1);
901 void HELPER(stctg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
906 for (i
= r1
;; i
= (i
+ 1) % 16) {
907 cpu_stq_data(env
, dest
, env
->cregs
[i
]);
908 dest
+= sizeof(uint64_t);
916 void HELPER(stctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
921 for (i
= r1
;; i
= (i
+ 1) % 16) {
922 cpu_stl_data(env
, dest
, env
->cregs
[i
]);
923 dest
+= sizeof(uint32_t);
931 uint32_t HELPER(tprot
)(uint64_t a1
, uint64_t a2
)
938 /* insert storage key extended */
939 uint64_t HELPER(iske
)(CPUS390XState
*env
, uint64_t r2
)
941 static S390SKeysState
*ss
;
942 static S390SKeysClass
*skeyclass
;
943 uint64_t addr
= get_address(env
, 0, 0, r2
);
946 if (addr
> ram_size
) {
951 ss
= s390_get_skeys_device();
952 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
955 if (skeyclass
->get_skeys(ss
, addr
/ TARGET_PAGE_SIZE
, 1, &key
)) {
961 /* set storage key extended */
962 void HELPER(sske
)(CPUS390XState
*env
, uint64_t r1
, uint64_t r2
)
964 static S390SKeysState
*ss
;
965 static S390SKeysClass
*skeyclass
;
966 uint64_t addr
= get_address(env
, 0, 0, r2
);
969 if (addr
> ram_size
) {
974 ss
= s390_get_skeys_device();
975 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
979 skeyclass
->set_skeys(ss
, addr
/ TARGET_PAGE_SIZE
, 1, &key
);
982 /* reset reference bit extended */
983 uint32_t HELPER(rrbe
)(CPUS390XState
*env
, uint64_t r2
)
985 static S390SKeysState
*ss
;
986 static S390SKeysClass
*skeyclass
;
994 ss
= s390_get_skeys_device();
995 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
998 if (skeyclass
->get_skeys(ss
, r2
/ TARGET_PAGE_SIZE
, 1, &key
)) {
1002 re
= key
& (SK_R
| SK_C
);
1005 if (skeyclass
->set_skeys(ss
, r2
/ TARGET_PAGE_SIZE
, 1, &key
)) {
1012 * 0 Reference bit zero; change bit zero
1013 * 1 Reference bit zero; change bit one
1014 * 2 Reference bit one; change bit zero
1015 * 3 Reference bit one; change bit one
1021 /* compare and swap and purge */
1022 uint32_t HELPER(csp
)(CPUS390XState
*env
, uint32_t r1
, uint64_t r2
)
1024 S390CPU
*cpu
= s390_env_get_cpu(env
);
1026 uint32_t o1
= env
->regs
[r1
];
1027 uint64_t a2
= r2
& ~3ULL;
1028 uint32_t o2
= cpu_ldl_data(env
, a2
);
1031 cpu_stl_data(env
, a2
, env
->regs
[(r1
+ 1) & 15]);
1033 /* flush TLB / ALB */
1034 tlb_flush(CPU(cpu
), 1);
1038 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) | o2
;
1045 uint32_t HELPER(mvcs
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1049 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1050 __func__
, l
, a1
, a2
);
1058 /* XXX replace w/ memcpy */
1059 for (i
= 0; i
< l
; i
++) {
1060 cpu_stb_secondary(env
, a1
+ i
, cpu_ldub_primary(env
, a2
+ i
));
1066 uint32_t HELPER(mvcp
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1070 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1071 __func__
, l
, a1
, a2
);
1079 /* XXX replace w/ memcpy */
1080 for (i
= 0; i
< l
; i
++) {
1081 cpu_stb_primary(env
, a1
+ i
, cpu_ldub_secondary(env
, a2
+ i
));
1087 /* invalidate pte */
1088 void HELPER(ipte
)(CPUS390XState
*env
, uint64_t pte_addr
, uint64_t vaddr
)
1090 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1091 uint64_t page
= vaddr
& TARGET_PAGE_MASK
;
1094 /* XXX broadcast to other CPUs */
1096 /* XXX Linux is nice enough to give us the exact pte address.
1097 According to spec we'd have to find it out ourselves */
1098 /* XXX Linux is fine with overwriting the pte, the spec requires
1099 us to only set the invalid bit */
1100 stq_phys(cs
->as
, pte_addr
, pte
| _PAGE_INVALID
);
1102 /* XXX we exploit the fact that Linux passes the exact virtual
1103 address here - it's not obliged to! */
1104 tlb_flush_page(cs
, page
);
1106 /* XXX 31-bit hack */
1107 if (page
& 0x80000000) {
1108 tlb_flush_page(cs
, page
& ~0x80000000);
1110 tlb_flush_page(cs
, page
| 0x80000000);
1114 /* flush local tlb */
1115 void HELPER(ptlb
)(CPUS390XState
*env
)
1117 S390CPU
*cpu
= s390_env_get_cpu(env
);
1119 tlb_flush(CPU(cpu
), 1);
1122 /* load using real address */
1123 uint64_t HELPER(lura
)(CPUS390XState
*env
, uint64_t addr
)
1125 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1127 return (uint32_t)ldl_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1130 uint64_t HELPER(lurag
)(CPUS390XState
*env
, uint64_t addr
)
1132 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1134 return ldq_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1137 /* store using real address */
1138 void HELPER(stura
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1140 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1142 stl_phys(cs
->as
, get_address(env
, 0, 0, addr
), (uint32_t)v1
);
1144 if ((env
->psw
.mask
& PSW_MASK_PER
) &&
1145 (env
->cregs
[9] & PER_CR9_EVENT_STORE
) &&
1146 (env
->cregs
[9] & PER_CR9_EVENT_STORE_REAL
)) {
1147 /* PSW is saved just before calling the helper. */
1148 env
->per_address
= env
->psw
.addr
;
1149 env
->per_perc_atmid
= PER_CODE_EVENT_STORE_REAL
| get_per_atmid(env
);
1153 void HELPER(sturg
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1155 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1157 stq_phys(cs
->as
, get_address(env
, 0, 0, addr
), v1
);
1159 if ((env
->psw
.mask
& PSW_MASK_PER
) &&
1160 (env
->cregs
[9] & PER_CR9_EVENT_STORE
) &&
1161 (env
->cregs
[9] & PER_CR9_EVENT_STORE_REAL
)) {
1162 /* PSW is saved just before calling the helper. */
1163 env
->per_address
= env
->psw
.addr
;
1164 env
->per_perc_atmid
= PER_CODE_EVENT_STORE_REAL
| get_per_atmid(env
);
1168 /* load real address */
1169 uint64_t HELPER(lra
)(CPUS390XState
*env
, uint64_t addr
)
1171 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1173 int old_exc
= cs
->exception_index
;
1174 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
1178 /* XXX incomplete - has more corner cases */
1179 if (!(env
->psw
.mask
& PSW_MASK_64
) && (addr
>> 32)) {
1180 program_interrupt(env
, PGM_SPECIAL_OP
, 2);
1183 cs
->exception_index
= old_exc
;
1184 if (mmu_translate(env
, addr
, 0, asc
, &ret
, &flags
, true)) {
1187 if (cs
->exception_index
== EXCP_PGM
) {
1188 ret
= env
->int_pgm_code
| 0x80000000;
1190 ret
|= addr
& ~TARGET_PAGE_MASK
;
1192 cs
->exception_index
= old_exc
;