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 "qemu/osdep.h"
23 #include "exec/helper-proto.h"
24 #include "exec/cpu_ldst.h"
25 #include "hw/s390x/storage-keys.h"
27 /*****************************************************************************/
29 #if !defined(CONFIG_USER_ONLY)
31 /* try to fill the TLB and return an exception if error. If retaddr is
32 NULL, it means that the function was called in C code (i.e. not
33 from generated code or from helper.c) */
34 /* XXX: fix it to restore all registers */
35 void tlb_fill(CPUState
*cs
, target_ulong addr
, int is_write
, int mmu_idx
,
40 ret
= s390_cpu_handle_mmu_fault(cs
, addr
, is_write
, mmu_idx
);
41 if (unlikely(ret
!= 0)) {
42 if (likely(retaddr
)) {
43 /* now we have a real cpu fault */
44 cpu_restore_state(cs
, retaddr
);
52 /* #define DEBUG_HELPER */
54 #define HELPER_LOG(x...) qemu_log(x)
56 #define HELPER_LOG(x...)
59 /* Reduce the length so that addr + len doesn't cross a page boundary. */
60 static inline uint64_t adj_len_to_page(uint64_t len
, uint64_t addr
)
62 #ifndef CONFIG_USER_ONLY
63 if ((addr
& ~TARGET_PAGE_MASK
) + len
- 1 >= TARGET_PAGE_SIZE
) {
64 return -addr
& ~TARGET_PAGE_MASK
;
70 static void fast_memset(CPUS390XState
*env
, uint64_t dest
, uint8_t byte
,
73 int mmu_idx
= cpu_mmu_index(env
, false);
76 void *p
= tlb_vaddr_to_host(env
, dest
, MMU_DATA_STORE
, mmu_idx
);
78 /* Access to the whole page in write mode granted. */
79 int l_adj
= adj_len_to_page(l
, dest
);
80 memset(p
, byte
, l_adj
);
84 /* We failed to get access to the whole page. The next write
85 access will likely fill the QEMU TLB for the next iteration. */
86 cpu_stb_data(env
, dest
, byte
);
93 static void fast_memmove(CPUS390XState
*env
, uint64_t dest
, uint64_t src
,
96 int mmu_idx
= cpu_mmu_index(env
, false);
99 void *src_p
= tlb_vaddr_to_host(env
, src
, MMU_DATA_LOAD
, mmu_idx
);
100 void *dest_p
= tlb_vaddr_to_host(env
, dest
, MMU_DATA_STORE
, mmu_idx
);
101 if (src_p
&& dest_p
) {
102 /* Access to both whole pages granted. */
103 int l_adj
= adj_len_to_page(l
, src
);
104 l_adj
= adj_len_to_page(l_adj
, dest
);
105 memmove(dest_p
, src_p
, l_adj
);
110 /* We failed to get access to one or both whole pages. The next
111 read or write access will likely fill the QEMU TLB for the
113 cpu_stb_data(env
, dest
, cpu_ldub_data(env
, src
));
122 uint32_t HELPER(nc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
129 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
130 __func__
, l
, dest
, src
);
131 for (i
= 0; i
<= l
; i
++) {
132 x
= cpu_ldub_data(env
, dest
+ i
) & cpu_ldub_data(env
, src
+ i
);
136 cpu_stb_data(env
, dest
+ i
, x
);
142 uint32_t HELPER(xc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
149 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
150 __func__
, l
, dest
, src
);
152 /* xor with itself is the same as memset(0) */
154 fast_memset(env
, dest
, 0, l
+ 1);
158 for (i
= 0; i
<= l
; i
++) {
159 x
= cpu_ldub_data(env
, dest
+ i
) ^ cpu_ldub_data(env
, src
+ i
);
163 cpu_stb_data(env
, dest
+ i
, x
);
169 uint32_t HELPER(oc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
,
176 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
177 __func__
, l
, dest
, src
);
178 for (i
= 0; i
<= l
; i
++) {
179 x
= cpu_ldub_data(env
, dest
+ i
) | cpu_ldub_data(env
, src
+ i
);
183 cpu_stb_data(env
, dest
+ i
, x
);
189 void HELPER(mvc
)(CPUS390XState
*env
, uint32_t l
, uint64_t dest
, uint64_t src
)
193 HELPER_LOG("%s l %d dest %" PRIx64
" src %" PRIx64
"\n",
194 __func__
, l
, dest
, src
);
196 /* mvc with source pointing to the byte after the destination is the
197 same as memset with the first source byte */
198 if (dest
== (src
+ 1)) {
199 fast_memset(env
, dest
, cpu_ldub_data(env
, src
), l
+ 1);
203 /* mvc and memmove do not behave the same when areas overlap! */
204 if ((dest
< src
) || (src
+ l
< dest
)) {
205 fast_memmove(env
, dest
, src
, l
+ 1);
209 /* slow version with byte accesses which always work */
210 for (i
= 0; i
<= l
; i
++) {
211 cpu_stb_data(env
, dest
+ i
, cpu_ldub_data(env
, src
+ i
));
215 /* compare unsigned byte arrays */
216 uint32_t HELPER(clc
)(CPUS390XState
*env
, uint32_t l
, uint64_t s1
, uint64_t s2
)
222 HELPER_LOG("%s l %d s1 %" PRIx64
" s2 %" PRIx64
"\n",
223 __func__
, l
, s1
, s2
);
224 for (i
= 0; i
<= l
; i
++) {
225 x
= cpu_ldub_data(env
, s1
+ i
);
226 y
= cpu_ldub_data(env
, s2
+ i
);
227 HELPER_LOG("%02x (%c)/%02x (%c) ", x
, x
, y
, y
);
242 /* compare logical under mask */
243 uint32_t HELPER(clm
)(CPUS390XState
*env
, uint32_t r1
, uint32_t mask
,
249 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64
"\n", __func__
, r1
,
254 d
= cpu_ldub_data(env
, addr
);
255 r
= (r1
& 0xff000000UL
) >> 24;
256 HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64
") ", mask
, r
, d
,
267 mask
= (mask
<< 1) & 0xf;
274 static inline uint64_t fix_address(CPUS390XState
*env
, uint64_t a
)
277 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
283 static inline uint64_t get_address(CPUS390XState
*env
, int x2
, int b2
, int d2
)
292 return fix_address(env
, r
);
295 static inline uint64_t get_address_31fix(CPUS390XState
*env
, int reg
)
297 return fix_address(env
, env
->regs
[reg
]);
300 /* search string (c is byte to search, r2 is string, r1 end of string) */
301 uint64_t HELPER(srst
)(CPUS390XState
*env
, uint64_t r0
, uint64_t end
,
307 str
= fix_address(env
, str
);
308 end
= fix_address(env
, end
);
310 /* Assume for now that R2 is unmodified. */
313 /* Lest we fail to service interrupts in a timely manner, limit the
314 amount of work we're willing to do. For now, let's cap at 8k. */
315 for (len
= 0; len
< 0x2000; ++len
) {
316 if (str
+ len
== end
) {
317 /* Character not found. R1 & R2 are unmodified. */
321 v
= cpu_ldub_data(env
, str
+ len
);
323 /* Character found. Set R1 to the location; R2 is unmodified. */
329 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
330 env
->retxl
= str
+ len
;
335 /* unsigned string compare (c is string terminator) */
336 uint64_t HELPER(clst
)(CPUS390XState
*env
, uint64_t c
, uint64_t s1
, uint64_t s2
)
341 s1
= fix_address(env
, s1
);
342 s2
= fix_address(env
, s2
);
344 /* Lest we fail to service interrupts in a timely manner, limit the
345 amount of work we're willing to do. For now, let's cap at 8k. */
346 for (len
= 0; len
< 0x2000; ++len
) {
347 uint8_t v1
= cpu_ldub_data(env
, s1
+ len
);
348 uint8_t v2
= cpu_ldub_data(env
, s2
+ len
);
351 /* Equal. CC=0, and don't advance the registers. */
357 /* Unequal. CC={1,2}, and advance the registers. Note that
358 the terminator need not be zero, but the string that contains
359 the terminator is by definition "low". */
360 env
->cc_op
= (v1
== c
? 1 : v2
== c
? 2 : v1
< v2
? 1 : 2);
361 env
->retxl
= s2
+ len
;
366 /* CPU-determined bytes equal; advance the registers. */
368 env
->retxl
= s2
+ len
;
373 void HELPER(mvpg
)(CPUS390XState
*env
, uint64_t r0
, uint64_t r1
, uint64_t r2
)
375 /* XXX missing r0 handling */
377 fast_memmove(env
, r1
, r2
, TARGET_PAGE_SIZE
);
380 /* string copy (c is string terminator) */
381 uint64_t HELPER(mvst
)(CPUS390XState
*env
, uint64_t c
, uint64_t d
, uint64_t s
)
386 d
= fix_address(env
, d
);
387 s
= fix_address(env
, s
);
389 /* Lest we fail to service interrupts in a timely manner, limit the
390 amount of work we're willing to do. For now, let's cap at 8k. */
391 for (len
= 0; len
< 0x2000; ++len
) {
392 uint8_t v
= cpu_ldub_data(env
, s
+ len
);
393 cpu_stb_data(env
, d
+ len
, v
);
395 /* Complete. Set CC=1 and advance R1. */
402 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
404 env
->retxl
= s
+ len
;
408 static uint32_t helper_icm(CPUS390XState
*env
, uint32_t r1
, uint64_t address
,
411 int pos
= 24; /* top of the lower half of r1 */
412 uint64_t rmask
= 0xff000000ULL
;
419 env
->regs
[r1
] &= ~rmask
;
420 val
= cpu_ldub_data(env
, address
);
421 if ((val
& 0x80) && !ccd
) {
425 if (val
&& cc
== 0) {
428 env
->regs
[r1
] |= (uint64_t)val
<< pos
;
431 mask
= (mask
<< 1) & 0xf;
439 /* execute instruction
440 this instruction executes an insn modified with the contents of r1
441 it does not change the executed instruction in memory
442 it does not change the program counter
443 in other words: tricky...
444 currently implemented by interpreting the cases it is most commonly used in
446 uint32_t HELPER(ex
)(CPUS390XState
*env
, uint32_t cc
, uint64_t v1
,
447 uint64_t addr
, uint64_t ret
)
449 S390CPU
*cpu
= s390_env_get_cpu(env
);
450 uint16_t insn
= cpu_lduw_code(env
, addr
);
452 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__
, v1
, addr
,
454 if ((insn
& 0xf0ff) == 0xd000) {
455 uint32_t l
, insn2
, b1
, b2
, d1
, d2
;
458 insn2
= cpu_ldl_code(env
, addr
+ 2);
459 b1
= (insn2
>> 28) & 0xf;
460 b2
= (insn2
>> 12) & 0xf;
461 d1
= (insn2
>> 16) & 0xfff;
463 switch (insn
& 0xf00) {
465 helper_mvc(env
, l
, get_address(env
, 0, b1
, d1
),
466 get_address(env
, 0, b2
, d2
));
469 cc
= helper_nc(env
, l
, get_address(env
, 0, b1
, d1
),
470 get_address(env
, 0, b2
, d2
));
473 cc
= helper_clc(env
, l
, get_address(env
, 0, b1
, d1
),
474 get_address(env
, 0, b2
, d2
));
477 cc
= helper_oc(env
, l
, get_address(env
, 0, b1
, d1
),
478 get_address(env
, 0, b2
, d2
));
481 cc
= helper_xc(env
, l
, get_address(env
, 0, b1
, d1
),
482 get_address(env
, 0, b2
, d2
));
485 helper_tr(env
, l
, get_address(env
, 0, b1
, d1
),
486 get_address(env
, 0, b2
, d2
));
489 cc
= helper_trt(env
, l
, get_address(env
, 0, b1
, d1
),
490 get_address(env
, 0, b2
, d2
));
495 } else if ((insn
& 0xff00) == 0x0a00) {
496 /* supervisor call */
497 HELPER_LOG("%s: svc %ld via execute\n", __func__
, (insn
| v1
) & 0xff);
498 env
->psw
.addr
= ret
- 4;
499 env
->int_svc_code
= (insn
| v1
) & 0xff;
500 env
->int_svc_ilen
= 4;
501 helper_exception(env
, EXCP_SVC
);
502 } else if ((insn
& 0xff00) == 0xbf00) {
503 uint32_t insn2
, r1
, r3
, b2
, d2
;
505 insn2
= cpu_ldl_code(env
, addr
+ 2);
506 r1
= (insn2
>> 20) & 0xf;
507 r3
= (insn2
>> 16) & 0xf;
508 b2
= (insn2
>> 12) & 0xf;
510 cc
= helper_icm(env
, r1
, get_address(env
, 0, b2
, d2
), r3
);
513 cpu_abort(CPU(cpu
), "EXECUTE on instruction prefix 0x%x not implemented\n",
519 /* load access registers r1 to r3 from memory at a2 */
520 void HELPER(lam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
524 for (i
= r1
;; i
= (i
+ 1) % 16) {
525 env
->aregs
[i
] = cpu_ldl_data(env
, a2
);
534 /* store access registers r1 to r3 in memory at a2 */
535 void HELPER(stam
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
539 for (i
= r1
;; i
= (i
+ 1) % 16) {
540 cpu_stl_data(env
, a2
, env
->aregs
[i
]);
550 uint32_t HELPER(mvcl
)(CPUS390XState
*env
, uint32_t r1
, uint32_t r2
)
552 uint64_t destlen
= env
->regs
[r1
+ 1] & 0xffffff;
553 uint64_t dest
= get_address_31fix(env
, r1
);
554 uint64_t srclen
= env
->regs
[r2
+ 1] & 0xffffff;
555 uint64_t src
= get_address_31fix(env
, r2
);
556 uint8_t pad
= env
->regs
[r2
+ 1] >> 24;
560 if (destlen
== srclen
) {
562 } else if (destlen
< srclen
) {
568 if (srclen
> destlen
) {
572 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
573 v
= cpu_ldub_data(env
, src
);
574 cpu_stb_data(env
, dest
, v
);
577 for (; destlen
; dest
++, destlen
--) {
578 cpu_stb_data(env
, dest
, pad
);
581 env
->regs
[r1
+ 1] = destlen
;
582 /* can't use srclen here, we trunc'ed it */
583 env
->regs
[r2
+ 1] -= src
- env
->regs
[r2
];
584 env
->regs
[r1
] = dest
;
590 /* move long extended another memcopy insn with more bells and whistles */
591 uint32_t HELPER(mvcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
594 uint64_t destlen
= env
->regs
[r1
+ 1];
595 uint64_t dest
= env
->regs
[r1
];
596 uint64_t srclen
= env
->regs
[r3
+ 1];
597 uint64_t src
= env
->regs
[r3
];
598 uint8_t pad
= a2
& 0xff;
602 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
603 destlen
= (uint32_t)destlen
;
604 srclen
= (uint32_t)srclen
;
609 if (destlen
== srclen
) {
611 } else if (destlen
< srclen
) {
617 if (srclen
> destlen
) {
621 for (; destlen
&& srclen
; src
++, dest
++, destlen
--, srclen
--) {
622 v
= cpu_ldub_data(env
, src
);
623 cpu_stb_data(env
, dest
, v
);
626 for (; destlen
; dest
++, destlen
--) {
627 cpu_stb_data(env
, dest
, pad
);
630 env
->regs
[r1
+ 1] = destlen
;
631 /* can't use srclen here, we trunc'ed it */
632 /* FIXME: 31-bit mode! */
633 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
634 env
->regs
[r1
] = dest
;
640 /* compare logical long extended memcompare insn with padding */
641 uint32_t HELPER(clcle
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
,
644 uint64_t destlen
= env
->regs
[r1
+ 1];
645 uint64_t dest
= get_address_31fix(env
, r1
);
646 uint64_t srclen
= env
->regs
[r3
+ 1];
647 uint64_t src
= get_address_31fix(env
, r3
);
648 uint8_t pad
= a2
& 0xff;
649 uint8_t v1
= 0, v2
= 0;
652 if (!(destlen
|| srclen
)) {
656 if (srclen
> destlen
) {
660 for (; destlen
|| srclen
; src
++, dest
++, destlen
--, srclen
--) {
661 v1
= srclen
? cpu_ldub_data(env
, src
) : pad
;
662 v2
= destlen
? cpu_ldub_data(env
, dest
) : pad
;
664 cc
= (v1
< v2
) ? 1 : 2;
669 env
->regs
[r1
+ 1] = destlen
;
670 /* can't use srclen here, we trunc'ed it */
671 env
->regs
[r3
+ 1] -= src
- env
->regs
[r3
];
672 env
->regs
[r1
] = dest
;
679 uint64_t HELPER(cksm
)(CPUS390XState
*env
, uint64_t r1
,
680 uint64_t src
, uint64_t src_len
)
682 uint64_t max_len
, len
;
683 uint64_t cksm
= (uint32_t)r1
;
685 /* Lest we fail to service interrupts in a timely manner, limit the
686 amount of work we're willing to do. For now, let's cap at 8k. */
687 max_len
= (src_len
> 0x2000 ? 0x2000 : src_len
);
689 /* Process full words as available. */
690 for (len
= 0; len
+ 4 <= max_len
; len
+= 4, src
+= 4) {
691 cksm
+= (uint32_t)cpu_ldl_data(env
, src
);
694 switch (max_len
- len
) {
696 cksm
+= cpu_ldub_data(env
, src
) << 24;
700 cksm
+= cpu_lduw_data(env
, src
) << 16;
704 cksm
+= cpu_lduw_data(env
, src
) << 16;
705 cksm
+= cpu_ldub_data(env
, src
+ 2) << 8;
710 /* Fold the carry from the checksum. Note that we can see carry-out
711 during folding more than once (but probably not more than twice). */
712 while (cksm
> 0xffffffffull
) {
713 cksm
= (uint32_t)cksm
+ (cksm
>> 32);
716 /* Indicate whether or not we've processed everything. */
717 env
->cc_op
= (len
== src_len
? 0 : 3);
719 /* Return both cksm and processed length. */
724 void HELPER(unpk
)(CPUS390XState
*env
, uint32_t len
, uint64_t dest
,
727 int len_dest
= len
>> 4;
728 int len_src
= len
& 0xf;
730 int second_nibble
= 0;
735 /* last byte is special, it only flips the nibbles */
736 b
= cpu_ldub_data(env
, src
);
737 cpu_stb_data(env
, dest
, (b
<< 4) | (b
>> 4));
741 /* now pad every nibble with 0xf0 */
743 while (len_dest
> 0) {
744 uint8_t cur_byte
= 0;
747 cur_byte
= cpu_ldub_data(env
, src
);
753 /* only advance one nibble at a time */
759 second_nibble
= !second_nibble
;
762 cur_byte
= (cur_byte
& 0xf);
766 cpu_stb_data(env
, dest
, cur_byte
);
770 void HELPER(tr
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
775 for (i
= 0; i
<= len
; i
++) {
776 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
777 uint8_t new_byte
= cpu_ldub_data(env
, trans
+ byte
);
779 cpu_stb_data(env
, array
+ i
, new_byte
);
783 uint64_t HELPER(tre
)(CPUS390XState
*env
, uint64_t array
,
784 uint64_t len
, uint64_t trans
)
786 uint8_t end
= env
->regs
[0] & 0xff;
790 if (!(env
->psw
.mask
& PSW_MASK_64
)) {
795 /* Lest we fail to service interrupts in a timely manner, limit the
796 amount of work we're willing to do. For now, let's cap at 8k. */
804 for (i
= 0; i
< l
; i
++) {
805 uint8_t byte
, new_byte
;
807 byte
= cpu_ldub_data(env
, array
+ i
);
814 new_byte
= cpu_ldub_data(env
, trans
+ byte
);
815 cpu_stb_data(env
, array
+ i
, new_byte
);
818 env
->retxl
= len
- i
;
822 uint32_t HELPER(trt
)(CPUS390XState
*env
, uint32_t len
, uint64_t array
,
828 for (i
= 0; i
<= len
; i
++) {
829 uint8_t byte
= cpu_ldub_data(env
, array
+ i
);
830 uint8_t sbyte
= cpu_ldub_data(env
, trans
+ byte
);
833 env
->regs
[1] = array
+ i
;
834 env
->regs
[2] = (env
->regs
[2] & ~0xff) | sbyte
;
835 cc
= (i
== len
) ? 2 : 1;
843 #if !defined(CONFIG_USER_ONLY)
844 void HELPER(lctlg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
846 S390CPU
*cpu
= s390_env_get_cpu(env
);
847 bool PERchanged
= false;
852 for (i
= r1
;; i
= (i
+ 1) % 16) {
853 val
= cpu_ldq_data(env
, src
);
854 if (env
->cregs
[i
] != val
&& i
>= 9 && i
<= 11) {
858 HELPER_LOG("load ctl %d from 0x%" PRIx64
" == 0x%" PRIx64
"\n",
859 i
, src
, env
->cregs
[i
]);
860 src
+= sizeof(uint64_t);
867 if (PERchanged
&& env
->psw
.mask
& PSW_MASK_PER
) {
868 s390_cpu_recompute_watchpoints(CPU(cpu
));
871 tlb_flush(CPU(cpu
), 1);
874 void HELPER(lctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
876 S390CPU
*cpu
= s390_env_get_cpu(env
);
877 bool PERchanged
= false;
882 for (i
= r1
;; i
= (i
+ 1) % 16) {
883 val
= cpu_ldl_data(env
, src
);
884 if ((uint32_t)env
->cregs
[i
] != val
&& i
>= 9 && i
<= 11) {
887 env
->cregs
[i
] = (env
->cregs
[i
] & 0xFFFFFFFF00000000ULL
) | val
;
888 src
+= sizeof(uint32_t);
895 if (PERchanged
&& env
->psw
.mask
& PSW_MASK_PER
) {
896 s390_cpu_recompute_watchpoints(CPU(cpu
));
899 tlb_flush(CPU(cpu
), 1);
902 void HELPER(stctg
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
907 for (i
= r1
;; i
= (i
+ 1) % 16) {
908 cpu_stq_data(env
, dest
, env
->cregs
[i
]);
909 dest
+= sizeof(uint64_t);
917 void HELPER(stctl
)(CPUS390XState
*env
, uint32_t r1
, uint64_t a2
, uint32_t r3
)
922 for (i
= r1
;; i
= (i
+ 1) % 16) {
923 cpu_stl_data(env
, dest
, env
->cregs
[i
]);
924 dest
+= sizeof(uint32_t);
932 uint32_t HELPER(tprot
)(uint64_t a1
, uint64_t a2
)
939 /* insert storage key extended */
940 uint64_t HELPER(iske
)(CPUS390XState
*env
, uint64_t r2
)
942 static S390SKeysState
*ss
;
943 static S390SKeysClass
*skeyclass
;
944 uint64_t addr
= get_address(env
, 0, 0, r2
);
947 if (addr
> ram_size
) {
952 ss
= s390_get_skeys_device();
953 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
956 if (skeyclass
->get_skeys(ss
, addr
/ TARGET_PAGE_SIZE
, 1, &key
)) {
962 /* set storage key extended */
963 void HELPER(sske
)(CPUS390XState
*env
, uint64_t r1
, uint64_t r2
)
965 static S390SKeysState
*ss
;
966 static S390SKeysClass
*skeyclass
;
967 uint64_t addr
= get_address(env
, 0, 0, r2
);
970 if (addr
> ram_size
) {
975 ss
= s390_get_skeys_device();
976 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
980 skeyclass
->set_skeys(ss
, addr
/ TARGET_PAGE_SIZE
, 1, &key
);
983 /* reset reference bit extended */
984 uint32_t HELPER(rrbe
)(CPUS390XState
*env
, uint64_t r2
)
986 static S390SKeysState
*ss
;
987 static S390SKeysClass
*skeyclass
;
995 ss
= s390_get_skeys_device();
996 skeyclass
= S390_SKEYS_GET_CLASS(ss
);
999 if (skeyclass
->get_skeys(ss
, r2
/ TARGET_PAGE_SIZE
, 1, &key
)) {
1003 re
= key
& (SK_R
| SK_C
);
1006 if (skeyclass
->set_skeys(ss
, r2
/ TARGET_PAGE_SIZE
, 1, &key
)) {
1013 * 0 Reference bit zero; change bit zero
1014 * 1 Reference bit zero; change bit one
1015 * 2 Reference bit one; change bit zero
1016 * 3 Reference bit one; change bit one
1022 /* compare and swap and purge */
1023 uint32_t HELPER(csp
)(CPUS390XState
*env
, uint32_t r1
, uint64_t r2
)
1025 S390CPU
*cpu
= s390_env_get_cpu(env
);
1027 uint32_t o1
= env
->regs
[r1
];
1028 uint64_t a2
= r2
& ~3ULL;
1029 uint32_t o2
= cpu_ldl_data(env
, a2
);
1032 cpu_stl_data(env
, a2
, env
->regs
[(r1
+ 1) & 15]);
1034 /* flush TLB / ALB */
1035 tlb_flush(CPU(cpu
), 1);
1039 env
->regs
[r1
] = (env
->regs
[r1
] & 0xffffffff00000000ULL
) | o2
;
1046 uint32_t HELPER(mvcs
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1050 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1051 __func__
, l
, a1
, a2
);
1059 /* XXX replace w/ memcpy */
1060 for (i
= 0; i
< l
; i
++) {
1061 cpu_stb_secondary(env
, a1
+ i
, cpu_ldub_primary(env
, a2
+ i
));
1067 uint32_t HELPER(mvcp
)(CPUS390XState
*env
, uint64_t l
, uint64_t a1
, uint64_t a2
)
1071 HELPER_LOG("%s: %16" PRIx64
" %16" PRIx64
" %16" PRIx64
"\n",
1072 __func__
, l
, a1
, a2
);
1080 /* XXX replace w/ memcpy */
1081 for (i
= 0; i
< l
; i
++) {
1082 cpu_stb_primary(env
, a1
+ i
, cpu_ldub_secondary(env
, a2
+ i
));
1088 /* invalidate pte */
1089 void HELPER(ipte
)(CPUS390XState
*env
, uint64_t pte_addr
, uint64_t vaddr
)
1091 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1092 uint64_t page
= vaddr
& TARGET_PAGE_MASK
;
1095 /* XXX broadcast to other CPUs */
1097 /* XXX Linux is nice enough to give us the exact pte address.
1098 According to spec we'd have to find it out ourselves */
1099 /* XXX Linux is fine with overwriting the pte, the spec requires
1100 us to only set the invalid bit */
1101 stq_phys(cs
->as
, pte_addr
, pte
| _PAGE_INVALID
);
1103 /* XXX we exploit the fact that Linux passes the exact virtual
1104 address here - it's not obliged to! */
1105 tlb_flush_page(cs
, page
);
1107 /* XXX 31-bit hack */
1108 if (page
& 0x80000000) {
1109 tlb_flush_page(cs
, page
& ~0x80000000);
1111 tlb_flush_page(cs
, page
| 0x80000000);
1115 /* flush local tlb */
1116 void HELPER(ptlb
)(CPUS390XState
*env
)
1118 S390CPU
*cpu
= s390_env_get_cpu(env
);
1120 tlb_flush(CPU(cpu
), 1);
1123 /* load using real address */
1124 uint64_t HELPER(lura
)(CPUS390XState
*env
, uint64_t addr
)
1126 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1128 return (uint32_t)ldl_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1131 uint64_t HELPER(lurag
)(CPUS390XState
*env
, uint64_t addr
)
1133 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1135 return ldq_phys(cs
->as
, get_address(env
, 0, 0, addr
));
1138 /* store using real address */
1139 void HELPER(stura
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1141 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1143 stl_phys(cs
->as
, get_address(env
, 0, 0, addr
), (uint32_t)v1
);
1145 if ((env
->psw
.mask
& PSW_MASK_PER
) &&
1146 (env
->cregs
[9] & PER_CR9_EVENT_STORE
) &&
1147 (env
->cregs
[9] & PER_CR9_EVENT_STORE_REAL
)) {
1148 /* PSW is saved just before calling the helper. */
1149 env
->per_address
= env
->psw
.addr
;
1150 env
->per_perc_atmid
= PER_CODE_EVENT_STORE_REAL
| get_per_atmid(env
);
1154 void HELPER(sturg
)(CPUS390XState
*env
, uint64_t addr
, uint64_t v1
)
1156 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1158 stq_phys(cs
->as
, get_address(env
, 0, 0, addr
), v1
);
1160 if ((env
->psw
.mask
& PSW_MASK_PER
) &&
1161 (env
->cregs
[9] & PER_CR9_EVENT_STORE
) &&
1162 (env
->cregs
[9] & PER_CR9_EVENT_STORE_REAL
)) {
1163 /* PSW is saved just before calling the helper. */
1164 env
->per_address
= env
->psw
.addr
;
1165 env
->per_perc_atmid
= PER_CODE_EVENT_STORE_REAL
| get_per_atmid(env
);
1169 /* load real address */
1170 uint64_t HELPER(lra
)(CPUS390XState
*env
, uint64_t addr
)
1172 CPUState
*cs
= CPU(s390_env_get_cpu(env
));
1174 int old_exc
= cs
->exception_index
;
1175 uint64_t asc
= env
->psw
.mask
& PSW_MASK_ASC
;
1179 /* XXX incomplete - has more corner cases */
1180 if (!(env
->psw
.mask
& PSW_MASK_64
) && (addr
>> 32)) {
1181 program_interrupt(env
, PGM_SPECIAL_OP
, 2);
1184 cs
->exception_index
= old_exc
;
1185 if (mmu_translate(env
, addr
, 0, asc
, &ret
, &flags
, true)) {
1188 if (cs
->exception_index
== EXCP_PGM
) {
1189 ret
= env
->int_pgm_code
| 0x80000000;
1191 ret
|= addr
& ~TARGET_PAGE_MASK
;
1193 cs
->exception_index
= old_exc
;