xref: /openbmc/qemu/target/s390x/tcg/mem_helper.c (revision 066b9de4b3010c5071775d986487127772963664)
1c9274b6bSCho, Yu-Chen /*
2c9274b6bSCho, Yu-Chen  *  S/390 memory access helper routines
3c9274b6bSCho, Yu-Chen  *
4c9274b6bSCho, Yu-Chen  *  Copyright (c) 2009 Ulrich Hecht
5c9274b6bSCho, Yu-Chen  *  Copyright (c) 2009 Alexander Graf
6c9274b6bSCho, Yu-Chen  *
7c9274b6bSCho, Yu-Chen  * This library is free software; you can redistribute it and/or
8c9274b6bSCho, Yu-Chen  * modify it under the terms of the GNU Lesser General Public
9c9274b6bSCho, Yu-Chen  * License as published by the Free Software Foundation; either
10c9274b6bSCho, Yu-Chen  * version 2.1 of the License, or (at your option) any later version.
11c9274b6bSCho, Yu-Chen  *
12c9274b6bSCho, Yu-Chen  * This library is distributed in the hope that it will be useful,
13c9274b6bSCho, Yu-Chen  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14c9274b6bSCho, Yu-Chen  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15c9274b6bSCho, Yu-Chen  * Lesser General Public License for more details.
16c9274b6bSCho, Yu-Chen  *
17c9274b6bSCho, Yu-Chen  * You should have received a copy of the GNU Lesser General Public
18c9274b6bSCho, Yu-Chen  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19c9274b6bSCho, Yu-Chen  */
20c9274b6bSCho, Yu-Chen 
21c9274b6bSCho, Yu-Chen #include "qemu/osdep.h"
22cd617484SPhilippe Mathieu-Daudé #include "qemu/log.h"
23c9274b6bSCho, Yu-Chen #include "cpu.h"
24c9274b6bSCho, Yu-Chen #include "s390x-internal.h"
25c9274b6bSCho, Yu-Chen #include "tcg_s390x.h"
26c9274b6bSCho, Yu-Chen #include "exec/helper-proto.h"
27c9274b6bSCho, Yu-Chen #include "exec/exec-all.h"
2874781c08SPhilippe Mathieu-Daudé #include "exec/page-protection.h"
29c9274b6bSCho, Yu-Chen #include "exec/cpu_ldst.h"
306eece7f5SPhilippe Mathieu-Daudé #include "hw/core/tcg-cpu-ops.h"
31c9274b6bSCho, Yu-Chen #include "qemu/int128.h"
32c9274b6bSCho, Yu-Chen #include "qemu/atomic128.h"
33c9274b6bSCho, Yu-Chen 
34c9274b6bSCho, Yu-Chen #if !defined(CONFIG_USER_ONLY)
35c9274b6bSCho, Yu-Chen #include "hw/s390x/storage-keys.h"
36c9274b6bSCho, Yu-Chen #include "hw/boards.h"
37c9274b6bSCho, Yu-Chen #endif
38c9274b6bSCho, Yu-Chen 
3961dee10fSRichard Henderson #ifdef CONFIG_USER_ONLY
4061dee10fSRichard Henderson # define user_or_likely(X)    true
4161dee10fSRichard Henderson #else
4261dee10fSRichard Henderson # define user_or_likely(X)    likely(X)
4361dee10fSRichard Henderson #endif
4461dee10fSRichard Henderson 
45c9274b6bSCho, Yu-Chen /*****************************************************************************/
46c9274b6bSCho, Yu-Chen /* Softmmu support */
47c9274b6bSCho, Yu-Chen 
48c9274b6bSCho, Yu-Chen /* #define DEBUG_HELPER */
49c9274b6bSCho, Yu-Chen #ifdef DEBUG_HELPER
50c9274b6bSCho, Yu-Chen #define HELPER_LOG(x...) qemu_log(x)
51c9274b6bSCho, Yu-Chen #else
52c9274b6bSCho, Yu-Chen #define HELPER_LOG(x...)
53c9274b6bSCho, Yu-Chen #endif
54c9274b6bSCho, Yu-Chen 
psw_key_valid(CPUS390XState * env,uint8_t psw_key)55c9274b6bSCho, Yu-Chen static inline bool psw_key_valid(CPUS390XState *env, uint8_t psw_key)
56c9274b6bSCho, Yu-Chen {
57c9274b6bSCho, Yu-Chen     uint16_t pkm = env->cregs[3] >> 16;
58c9274b6bSCho, Yu-Chen 
59c9274b6bSCho, Yu-Chen     if (env->psw.mask & PSW_MASK_PSTATE) {
60c9274b6bSCho, Yu-Chen         /* PSW key has range 0..15, it is valid if the bit is 1 in the PKM */
615e275ca6SThomas Huth         return pkm & (0x8000 >> psw_key);
62c9274b6bSCho, Yu-Chen     }
63c9274b6bSCho, Yu-Chen     return true;
64c9274b6bSCho, Yu-Chen }
65c9274b6bSCho, Yu-Chen 
is_destructive_overlap(CPUS390XState * env,uint64_t dest,uint64_t src,uint32_t len)66c9274b6bSCho, Yu-Chen static bool is_destructive_overlap(CPUS390XState *env, uint64_t dest,
67c9274b6bSCho, Yu-Chen                                    uint64_t src, uint32_t len)
68c9274b6bSCho, Yu-Chen {
69c9274b6bSCho, Yu-Chen     if (!len || src == dest) {
70c9274b6bSCho, Yu-Chen         return false;
71c9274b6bSCho, Yu-Chen     }
72c9274b6bSCho, Yu-Chen     /* Take care of wrapping at the end of address space. */
73c9274b6bSCho, Yu-Chen     if (unlikely(wrap_address(env, src + len - 1) < src)) {
74c9274b6bSCho, Yu-Chen         return dest > src || dest <= wrap_address(env, src + len - 1);
75c9274b6bSCho, Yu-Chen     }
76c9274b6bSCho, Yu-Chen     return dest > src && dest <= src + len - 1;
77c9274b6bSCho, Yu-Chen }
78c9274b6bSCho, Yu-Chen 
79c9274b6bSCho, Yu-Chen /* Trigger a SPECIFICATION exception if an address or a length is not
80c9274b6bSCho, Yu-Chen    naturally aligned.  */
check_alignment(CPUS390XState * env,uint64_t v,int wordsize,uintptr_t ra)81c9274b6bSCho, Yu-Chen static inline void check_alignment(CPUS390XState *env, uint64_t v,
82c9274b6bSCho, Yu-Chen                                    int wordsize, uintptr_t ra)
83c9274b6bSCho, Yu-Chen {
84c9274b6bSCho, Yu-Chen     if (v % wordsize) {
85c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
86c9274b6bSCho, Yu-Chen     }
87c9274b6bSCho, Yu-Chen }
88c9274b6bSCho, Yu-Chen 
89c9274b6bSCho, Yu-Chen /* Load a value from memory according to its size.  */
cpu_ldusize_data_ra(CPUS390XState * env,uint64_t addr,int wordsize,uintptr_t ra)90c9274b6bSCho, Yu-Chen static inline uint64_t cpu_ldusize_data_ra(CPUS390XState *env, uint64_t addr,
91c9274b6bSCho, Yu-Chen                                            int wordsize, uintptr_t ra)
92c9274b6bSCho, Yu-Chen {
93c9274b6bSCho, Yu-Chen     switch (wordsize) {
94c9274b6bSCho, Yu-Chen     case 1:
95c9274b6bSCho, Yu-Chen         return cpu_ldub_data_ra(env, addr, ra);
96c9274b6bSCho, Yu-Chen     case 2:
97c9274b6bSCho, Yu-Chen         return cpu_lduw_data_ra(env, addr, ra);
98c9274b6bSCho, Yu-Chen     default:
99c9274b6bSCho, Yu-Chen         abort();
100c9274b6bSCho, Yu-Chen     }
101c9274b6bSCho, Yu-Chen }
102c9274b6bSCho, Yu-Chen 
103c9274b6bSCho, Yu-Chen /* Store a to memory according to its size.  */
cpu_stsize_data_ra(CPUS390XState * env,uint64_t addr,uint64_t value,int wordsize,uintptr_t ra)104c9274b6bSCho, Yu-Chen static inline void cpu_stsize_data_ra(CPUS390XState *env, uint64_t addr,
105c9274b6bSCho, Yu-Chen                                       uint64_t value, int wordsize,
106c9274b6bSCho, Yu-Chen                                       uintptr_t ra)
107c9274b6bSCho, Yu-Chen {
108c9274b6bSCho, Yu-Chen     switch (wordsize) {
109c9274b6bSCho, Yu-Chen     case 1:
110c9274b6bSCho, Yu-Chen         cpu_stb_data_ra(env, addr, value, ra);
111c9274b6bSCho, Yu-Chen         break;
112c9274b6bSCho, Yu-Chen     case 2:
113c9274b6bSCho, Yu-Chen         cpu_stw_data_ra(env, addr, value, ra);
114c9274b6bSCho, Yu-Chen         break;
115c9274b6bSCho, Yu-Chen     default:
116c9274b6bSCho, Yu-Chen         abort();
117c9274b6bSCho, Yu-Chen     }
118c9274b6bSCho, Yu-Chen }
119c9274b6bSCho, Yu-Chen 
120c9274b6bSCho, Yu-Chen /* An access covers at most 4096 bytes and therefore at most two pages. */
121c9274b6bSCho, Yu-Chen typedef struct S390Access {
122c9274b6bSCho, Yu-Chen     target_ulong vaddr1;
123c9274b6bSCho, Yu-Chen     target_ulong vaddr2;
124bebc8adeSRichard Henderson     void *haddr1;
125bebc8adeSRichard Henderson     void *haddr2;
126c9274b6bSCho, Yu-Chen     uint16_t size1;
127c9274b6bSCho, Yu-Chen     uint16_t size2;
128c9274b6bSCho, Yu-Chen     /*
129c9274b6bSCho, Yu-Chen      * If we can't access the host page directly, we'll have to do I/O access
130c9274b6bSCho, Yu-Chen      * via ld/st helpers. These are internal details, so we store the
131c9274b6bSCho, Yu-Chen      * mmu idx to do the access here instead of passing it around in the
13296b1416fSRichard Henderson      * helpers.
133c9274b6bSCho, Yu-Chen      */
134c9274b6bSCho, Yu-Chen     int mmu_idx;
135c9274b6bSCho, Yu-Chen } S390Access;
136c9274b6bSCho, Yu-Chen 
137c9274b6bSCho, Yu-Chen /*
138c9274b6bSCho, Yu-Chen  * With nonfault=1, return the PGM_ exception that would have been injected
139c9274b6bSCho, Yu-Chen  * into the guest; return 0 if no exception was detected.
140c9274b6bSCho, Yu-Chen  *
141c9274b6bSCho, Yu-Chen  * For !CONFIG_USER_ONLY, the TEC is stored stored to env->tlb_fill_tec.
142c9274b6bSCho, Yu-Chen  * For CONFIG_USER_ONLY, the faulting address is stored to env->__excp_addr.
143c9274b6bSCho, Yu-Chen  */
s390_probe_access(CPUArchState * env,target_ulong addr,int size,MMUAccessType access_type,int mmu_idx,bool nonfault,void ** phost,uintptr_t ra)14440494314SRichard Henderson static inline int s390_probe_access(CPUArchState *env, target_ulong addr,
14540494314SRichard Henderson                                     int size, MMUAccessType access_type,
14640494314SRichard Henderson                                     int mmu_idx, bool nonfault,
14740494314SRichard Henderson                                     void **phost, uintptr_t ra)
148c9274b6bSCho, Yu-Chen {
149*066b9de4SIlya Leoshkevich     int flags = probe_access_flags(env, addr, size, access_type, mmu_idx,
150db9aab57SRichard Henderson                                    nonfault, phost, ra);
151c9274b6bSCho, Yu-Chen 
15240494314SRichard Henderson     if (unlikely(flags & TLB_INVALID_MASK)) {
15340494314SRichard Henderson #ifdef CONFIG_USER_ONLY
15440494314SRichard Henderson         /* Address is in TEC in system mode; see s390_cpu_record_sigsegv. */
15540494314SRichard Henderson         env->__excp_addr = addr & TARGET_PAGE_MASK;
15640494314SRichard Henderson         return (page_get_flags(addr) & PAGE_VALID
15740494314SRichard Henderson                 ? PGM_PROTECTION : PGM_ADDRESSING);
15840494314SRichard Henderson #else
159c9274b6bSCho, Yu-Chen         return env->tlb_fill_exc;
16040494314SRichard Henderson #endif
161c9274b6bSCho, Yu-Chen     }
162c9274b6bSCho, Yu-Chen 
16340494314SRichard Henderson #ifndef CONFIG_USER_ONLY
164c9274b6bSCho, Yu-Chen     if (unlikely(flags & TLB_WATCHPOINT)) {
165c9274b6bSCho, Yu-Chen         /* S390 does not presently use transaction attributes. */
166c9274b6bSCho, Yu-Chen         cpu_check_watchpoint(env_cpu(env), addr, size,
167c9274b6bSCho, Yu-Chen                              MEMTXATTRS_UNSPECIFIED,
168c9274b6bSCho, Yu-Chen                              (access_type == MMU_DATA_STORE
169c9274b6bSCho, Yu-Chen                               ? BP_MEM_WRITE : BP_MEM_READ), ra);
170c9274b6bSCho, Yu-Chen     }
171db9aab57SRichard Henderson #endif
17240494314SRichard Henderson 
17340494314SRichard Henderson     return 0;
174c9274b6bSCho, Yu-Chen }
175c9274b6bSCho, Yu-Chen 
access_prepare_nf(S390Access * access,CPUS390XState * env,bool nonfault,vaddr vaddr1,int size,MMUAccessType access_type,int mmu_idx,uintptr_t ra)176c9274b6bSCho, Yu-Chen static int access_prepare_nf(S390Access *access, CPUS390XState *env,
177c9274b6bSCho, Yu-Chen                              bool nonfault, vaddr vaddr1, int size,
178c9274b6bSCho, Yu-Chen                              MMUAccessType access_type,
179c9274b6bSCho, Yu-Chen                              int mmu_idx, uintptr_t ra)
180c9274b6bSCho, Yu-Chen {
181c9274b6bSCho, Yu-Chen     int size1, size2, exc;
182c9274b6bSCho, Yu-Chen 
183c9274b6bSCho, Yu-Chen     assert(size > 0 && size <= 4096);
184c9274b6bSCho, Yu-Chen 
185c9274b6bSCho, Yu-Chen     size1 = MIN(size, -(vaddr1 | TARGET_PAGE_MASK)),
186c9274b6bSCho, Yu-Chen     size2 = size - size1;
187c9274b6bSCho, Yu-Chen 
188fb391b0bSRichard Henderson     memset(access, 0, sizeof(*access));
189fb391b0bSRichard Henderson     access->vaddr1 = vaddr1;
190fb391b0bSRichard Henderson     access->size1 = size1;
191fb391b0bSRichard Henderson     access->size2 = size2;
192fb391b0bSRichard Henderson     access->mmu_idx = mmu_idx;
193fb391b0bSRichard Henderson 
194c9274b6bSCho, Yu-Chen     exc = s390_probe_access(env, vaddr1, size1, access_type, mmu_idx, nonfault,
195fb391b0bSRichard Henderson                             &access->haddr1, ra);
196fb391b0bSRichard Henderson     if (unlikely(exc)) {
197c9274b6bSCho, Yu-Chen         return exc;
198c9274b6bSCho, Yu-Chen     }
199c9274b6bSCho, Yu-Chen     if (unlikely(size2)) {
200c9274b6bSCho, Yu-Chen         /* The access crosses page boundaries. */
201fb391b0bSRichard Henderson         vaddr vaddr2 = wrap_address(env, vaddr1 + size1);
202fb391b0bSRichard Henderson 
203fb391b0bSRichard Henderson         access->vaddr2 = vaddr2;
204c9274b6bSCho, Yu-Chen         exc = s390_probe_access(env, vaddr2, size2, access_type, mmu_idx,
205fb391b0bSRichard Henderson                                 nonfault, &access->haddr2, ra);
206fb391b0bSRichard Henderson         if (unlikely(exc)) {
207c9274b6bSCho, Yu-Chen             return exc;
208c9274b6bSCho, Yu-Chen         }
209c9274b6bSCho, Yu-Chen     }
210c9274b6bSCho, Yu-Chen     return 0;
211c9274b6bSCho, Yu-Chen }
212c9274b6bSCho, Yu-Chen 
access_prepare(S390Access * ret,CPUS390XState * env,vaddr vaddr,int size,MMUAccessType access_type,int mmu_idx,uintptr_t ra)2137ba5da81SRichard Henderson static inline void access_prepare(S390Access *ret, CPUS390XState *env,
2147ba5da81SRichard Henderson                                   vaddr vaddr, int size,
215c9274b6bSCho, Yu-Chen                                   MMUAccessType access_type, int mmu_idx,
216c9274b6bSCho, Yu-Chen                                   uintptr_t ra)
217c9274b6bSCho, Yu-Chen {
2187ba5da81SRichard Henderson     int exc = access_prepare_nf(ret, env, false, vaddr, size,
219c9274b6bSCho, Yu-Chen                                 access_type, mmu_idx, ra);
220c9274b6bSCho, Yu-Chen     assert(!exc);
221c9274b6bSCho, Yu-Chen }
222c9274b6bSCho, Yu-Chen 
223c9274b6bSCho, Yu-Chen /* Helper to handle memset on a single page. */
do_access_memset(CPUS390XState * env,vaddr vaddr,char * haddr,uint8_t byte,uint16_t size,int mmu_idx,uintptr_t ra)224c9274b6bSCho, Yu-Chen static void do_access_memset(CPUS390XState *env, vaddr vaddr, char *haddr,
225c9274b6bSCho, Yu-Chen                              uint8_t byte, uint16_t size, int mmu_idx,
226c9274b6bSCho, Yu-Chen                              uintptr_t ra)
227c9274b6bSCho, Yu-Chen {
228814e4659SRichard Henderson     if (user_or_likely(haddr)) {
229c9274b6bSCho, Yu-Chen         memset(haddr, byte, size);
230c9274b6bSCho, Yu-Chen     } else {
23196b1416fSRichard Henderson         MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx);
23296b1416fSRichard Henderson         for (int i = 0; i < size; i++) {
233bfe5b847SRichard Henderson             cpu_stb_mmu(env, vaddr + i, byte, oi, ra);
234c9274b6bSCho, Yu-Chen         }
235c9274b6bSCho, Yu-Chen     }
236c9274b6bSCho, Yu-Chen }
237c9274b6bSCho, Yu-Chen 
access_memset(CPUS390XState * env,S390Access * desta,uint8_t byte,uintptr_t ra)238c9274b6bSCho, Yu-Chen static void access_memset(CPUS390XState *env, S390Access *desta,
239c9274b6bSCho, Yu-Chen                           uint8_t byte, uintptr_t ra)
240c9274b6bSCho, Yu-Chen {
2412730df91SRichard Henderson     set_helper_retaddr(ra);
242c9274b6bSCho, Yu-Chen     do_access_memset(env, desta->vaddr1, desta->haddr1, byte, desta->size1,
243c9274b6bSCho, Yu-Chen                      desta->mmu_idx, ra);
2442730df91SRichard Henderson     if (unlikely(desta->size2)) {
2452730df91SRichard Henderson         do_access_memset(env, desta->vaddr2, desta->haddr2, byte,
2462730df91SRichard Henderson                          desta->size2, desta->mmu_idx, ra);
247c9274b6bSCho, Yu-Chen     }
2482730df91SRichard Henderson     clear_helper_retaddr();
249c9274b6bSCho, Yu-Chen }
250c9274b6bSCho, Yu-Chen 
access_get_byte(CPUS390XState * env,S390Access * access,int offset,uintptr_t ra)251c9274b6bSCho, Yu-Chen static uint8_t access_get_byte(CPUS390XState *env, S390Access *access,
252c9274b6bSCho, Yu-Chen                                int offset, uintptr_t ra)
253c9274b6bSCho, Yu-Chen {
25461dee10fSRichard Henderson     target_ulong vaddr = access->vaddr1;
25561dee10fSRichard Henderson     void *haddr = access->haddr1;
25661dee10fSRichard Henderson 
25761dee10fSRichard Henderson     if (unlikely(offset >= access->size1)) {
25861dee10fSRichard Henderson         offset -= access->size1;
25961dee10fSRichard Henderson         vaddr = access->vaddr2;
26061dee10fSRichard Henderson         haddr = access->haddr2;
261c9274b6bSCho, Yu-Chen     }
262c9274b6bSCho, Yu-Chen 
26361dee10fSRichard Henderson     if (user_or_likely(haddr)) {
26461dee10fSRichard Henderson         return ldub_p(haddr + offset);
26596b1416fSRichard Henderson     } else {
26661dee10fSRichard Henderson         MemOpIdx oi = make_memop_idx(MO_UB, access->mmu_idx);
26761dee10fSRichard Henderson         return cpu_ldb_mmu(env, vaddr + offset, oi, ra);
26896b1416fSRichard Henderson     }
269c9274b6bSCho, Yu-Chen }
270c9274b6bSCho, Yu-Chen 
access_set_byte(CPUS390XState * env,S390Access * access,int offset,uint8_t byte,uintptr_t ra)271c9274b6bSCho, Yu-Chen static void access_set_byte(CPUS390XState *env, S390Access *access,
272c9274b6bSCho, Yu-Chen                             int offset, uint8_t byte, uintptr_t ra)
273c9274b6bSCho, Yu-Chen {
27461dee10fSRichard Henderson     target_ulong vaddr = access->vaddr1;
27561dee10fSRichard Henderson     void *haddr = access->haddr1;
27661dee10fSRichard Henderson 
27761dee10fSRichard Henderson     if (unlikely(offset >= access->size1)) {
27861dee10fSRichard Henderson         offset -= access->size1;
27961dee10fSRichard Henderson         vaddr = access->vaddr2;
28061dee10fSRichard Henderson         haddr = access->haddr2;
28161dee10fSRichard Henderson     }
28261dee10fSRichard Henderson 
28361dee10fSRichard Henderson     if (user_or_likely(haddr)) {
28461dee10fSRichard Henderson         stb_p(haddr + offset, byte);
285c9274b6bSCho, Yu-Chen     } else {
28661dee10fSRichard Henderson         MemOpIdx oi = make_memop_idx(MO_UB, access->mmu_idx);
28761dee10fSRichard Henderson         cpu_stb_mmu(env, vaddr + offset, byte, oi, ra);
288c9274b6bSCho, Yu-Chen     }
289c9274b6bSCho, Yu-Chen }
290c9274b6bSCho, Yu-Chen 
291c9274b6bSCho, Yu-Chen /*
292c9274b6bSCho, Yu-Chen  * Move data with the same semantics as memmove() in case ranges don't overlap
293c9274b6bSCho, Yu-Chen  * or src > dest. Undefined behavior on destructive overlaps.
294c9274b6bSCho, Yu-Chen  */
access_memmove(CPUS390XState * env,S390Access * desta,S390Access * srca,uintptr_t ra)295c9274b6bSCho, Yu-Chen static void access_memmove(CPUS390XState *env, S390Access *desta,
296c9274b6bSCho, Yu-Chen                            S390Access *srca, uintptr_t ra)
297c9274b6bSCho, Yu-Chen {
298e73a0f40SRichard Henderson     int len = desta->size1 + desta->size2;
299c9274b6bSCho, Yu-Chen 
300e73a0f40SRichard Henderson     assert(len == srca->size1 + srca->size2);
301c9274b6bSCho, Yu-Chen 
302c9274b6bSCho, Yu-Chen     /* Fallback to slow access in case we don't have access to all host pages */
303573b7783SRichard Henderson     if (user_or_likely(desta->haddr1 &&
304573b7783SRichard Henderson                        srca->haddr1 &&
305573b7783SRichard Henderson                        (!desta->size2 || desta->haddr2) &&
306573b7783SRichard Henderson                        (!srca->size2 || srca->haddr2))) {
307573b7783SRichard Henderson         int diff = desta->size1 - srca->size1;
308c9274b6bSCho, Yu-Chen 
309e73a0f40SRichard Henderson         if (likely(diff == 0)) {
310c9274b6bSCho, Yu-Chen             memmove(desta->haddr1, srca->haddr1, srca->size1);
311c9274b6bSCho, Yu-Chen             if (unlikely(srca->size2)) {
312c9274b6bSCho, Yu-Chen                 memmove(desta->haddr2, srca->haddr2, srca->size2);
313c9274b6bSCho, Yu-Chen             }
314e73a0f40SRichard Henderson         } else if (diff > 0) {
315c9274b6bSCho, Yu-Chen             memmove(desta->haddr1, srca->haddr1, srca->size1);
316c9274b6bSCho, Yu-Chen             memmove(desta->haddr1 + srca->size1, srca->haddr2, diff);
317c9274b6bSCho, Yu-Chen             if (likely(desta->size2)) {
318c9274b6bSCho, Yu-Chen                 memmove(desta->haddr2, srca->haddr2 + diff, desta->size2);
319c9274b6bSCho, Yu-Chen             }
320c9274b6bSCho, Yu-Chen         } else {
321e73a0f40SRichard Henderson             diff = -diff;
322c9274b6bSCho, Yu-Chen             memmove(desta->haddr1, srca->haddr1, desta->size1);
323c9274b6bSCho, Yu-Chen             memmove(desta->haddr2, srca->haddr1 + desta->size1, diff);
324c9274b6bSCho, Yu-Chen             if (likely(srca->size2)) {
325c9274b6bSCho, Yu-Chen                 memmove(desta->haddr2 + diff, srca->haddr2, srca->size2);
326c9274b6bSCho, Yu-Chen             }
327c9274b6bSCho, Yu-Chen         }
328573b7783SRichard Henderson     } else {
329573b7783SRichard Henderson         for (int i = 0; i < len; i++) {
330573b7783SRichard Henderson             uint8_t byte = access_get_byte(env, srca, i, ra);
331573b7783SRichard Henderson             access_set_byte(env, desta, i, byte, ra);
332573b7783SRichard Henderson         }
333573b7783SRichard Henderson     }
334c9274b6bSCho, Yu-Chen }
335c9274b6bSCho, Yu-Chen 
mmu_idx_from_as(uint8_t as)336c9274b6bSCho, Yu-Chen static int mmu_idx_from_as(uint8_t as)
337c9274b6bSCho, Yu-Chen {
338c9274b6bSCho, Yu-Chen     switch (as) {
339c9274b6bSCho, Yu-Chen     case AS_PRIMARY:
340c9274b6bSCho, Yu-Chen         return MMU_PRIMARY_IDX;
341c9274b6bSCho, Yu-Chen     case AS_SECONDARY:
342c9274b6bSCho, Yu-Chen         return MMU_SECONDARY_IDX;
343c9274b6bSCho, Yu-Chen     case AS_HOME:
344c9274b6bSCho, Yu-Chen         return MMU_HOME_IDX;
345c9274b6bSCho, Yu-Chen     default:
346c9274b6bSCho, Yu-Chen         /* FIXME AS_ACCREG */
347c9274b6bSCho, Yu-Chen         g_assert_not_reached();
348c9274b6bSCho, Yu-Chen     }
349c9274b6bSCho, Yu-Chen }
350c9274b6bSCho, Yu-Chen 
351c9274b6bSCho, Yu-Chen /* and on array */
do_helper_nc(CPUS390XState * env,uint32_t l,uint64_t dest,uint64_t src,uintptr_t ra)352c9274b6bSCho, Yu-Chen static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest,
353c9274b6bSCho, Yu-Chen                              uint64_t src, uintptr_t ra)
354c9274b6bSCho, Yu-Chen {
35590b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
356c9274b6bSCho, Yu-Chen     S390Access srca1, srca2, desta;
357c9274b6bSCho, Yu-Chen     uint32_t i;
358c9274b6bSCho, Yu-Chen     uint8_t c = 0;
359c9274b6bSCho, Yu-Chen 
360c9274b6bSCho, Yu-Chen     HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
361c9274b6bSCho, Yu-Chen                __func__, l, dest, src);
362c9274b6bSCho, Yu-Chen 
363c9274b6bSCho, Yu-Chen     /* NC always processes one more byte than specified - maximum is 256 */
364c9274b6bSCho, Yu-Chen     l++;
365c9274b6bSCho, Yu-Chen 
3667ba5da81SRichard Henderson     access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
3677ba5da81SRichard Henderson     access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
3687ba5da81SRichard Henderson     access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
3692730df91SRichard Henderson     set_helper_retaddr(ra);
3702730df91SRichard Henderson 
371c9274b6bSCho, Yu-Chen     for (i = 0; i < l; i++) {
372c9274b6bSCho, Yu-Chen         const uint8_t x = access_get_byte(env, &srca1, i, ra) &
373c9274b6bSCho, Yu-Chen                           access_get_byte(env, &srca2, i, ra);
374c9274b6bSCho, Yu-Chen 
375c9274b6bSCho, Yu-Chen         c |= x;
376c9274b6bSCho, Yu-Chen         access_set_byte(env, &desta, i, x, ra);
377c9274b6bSCho, Yu-Chen     }
3782730df91SRichard Henderson 
3792730df91SRichard Henderson     clear_helper_retaddr();
380c9274b6bSCho, Yu-Chen     return c != 0;
381c9274b6bSCho, Yu-Chen }
382c9274b6bSCho, Yu-Chen 
HELPER(nc)383c9274b6bSCho, Yu-Chen uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
384c9274b6bSCho, Yu-Chen                     uint64_t src)
385c9274b6bSCho, Yu-Chen {
386c9274b6bSCho, Yu-Chen     return do_helper_nc(env, l, dest, src, GETPC());
387c9274b6bSCho, Yu-Chen }
388c9274b6bSCho, Yu-Chen 
389c9274b6bSCho, Yu-Chen /* xor on array */
do_helper_xc(CPUS390XState * env,uint32_t l,uint64_t dest,uint64_t src,uintptr_t ra)390c9274b6bSCho, Yu-Chen static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest,
391c9274b6bSCho, Yu-Chen                              uint64_t src, uintptr_t ra)
392c9274b6bSCho, Yu-Chen {
39390b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
394c9274b6bSCho, Yu-Chen     S390Access srca1, srca2, desta;
395c9274b6bSCho, Yu-Chen     uint32_t i;
396c9274b6bSCho, Yu-Chen     uint8_t c = 0;
397c9274b6bSCho, Yu-Chen 
398c9274b6bSCho, Yu-Chen     HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
399c9274b6bSCho, Yu-Chen                __func__, l, dest, src);
400c9274b6bSCho, Yu-Chen 
401c9274b6bSCho, Yu-Chen     /* XC always processes one more byte than specified - maximum is 256 */
402c9274b6bSCho, Yu-Chen     l++;
403c9274b6bSCho, Yu-Chen 
4047ba5da81SRichard Henderson     access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
4057ba5da81SRichard Henderson     access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
4067ba5da81SRichard Henderson     access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
407c9274b6bSCho, Yu-Chen 
408c9274b6bSCho, Yu-Chen     /* xor with itself is the same as memset(0) */
409c9274b6bSCho, Yu-Chen     if (src == dest) {
410c9274b6bSCho, Yu-Chen         access_memset(env, &desta, 0, ra);
411c9274b6bSCho, Yu-Chen         return 0;
412c9274b6bSCho, Yu-Chen     }
413c9274b6bSCho, Yu-Chen 
4142730df91SRichard Henderson     set_helper_retaddr(ra);
415c9274b6bSCho, Yu-Chen     for (i = 0; i < l; i++) {
416c9274b6bSCho, Yu-Chen         const uint8_t x = access_get_byte(env, &srca1, i, ra) ^
417c9274b6bSCho, Yu-Chen                           access_get_byte(env, &srca2, i, ra);
418c9274b6bSCho, Yu-Chen 
419c9274b6bSCho, Yu-Chen         c |= x;
420c9274b6bSCho, Yu-Chen         access_set_byte(env, &desta, i, x, ra);
421c9274b6bSCho, Yu-Chen     }
4222730df91SRichard Henderson     clear_helper_retaddr();
423c9274b6bSCho, Yu-Chen     return c != 0;
424c9274b6bSCho, Yu-Chen }
425c9274b6bSCho, Yu-Chen 
HELPER(xc)426c9274b6bSCho, Yu-Chen uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
427c9274b6bSCho, Yu-Chen                     uint64_t src)
428c9274b6bSCho, Yu-Chen {
429c9274b6bSCho, Yu-Chen     return do_helper_xc(env, l, dest, src, GETPC());
430c9274b6bSCho, Yu-Chen }
431c9274b6bSCho, Yu-Chen 
432c9274b6bSCho, Yu-Chen /* or on array */
do_helper_oc(CPUS390XState * env,uint32_t l,uint64_t dest,uint64_t src,uintptr_t ra)433c9274b6bSCho, Yu-Chen static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest,
434c9274b6bSCho, Yu-Chen                              uint64_t src, uintptr_t ra)
435c9274b6bSCho, Yu-Chen {
43690b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
437c9274b6bSCho, Yu-Chen     S390Access srca1, srca2, desta;
438c9274b6bSCho, Yu-Chen     uint32_t i;
439c9274b6bSCho, Yu-Chen     uint8_t c = 0;
440c9274b6bSCho, Yu-Chen 
441c9274b6bSCho, Yu-Chen     HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
442c9274b6bSCho, Yu-Chen                __func__, l, dest, src);
443c9274b6bSCho, Yu-Chen 
444c9274b6bSCho, Yu-Chen     /* OC always processes one more byte than specified - maximum is 256 */
445c9274b6bSCho, Yu-Chen     l++;
446c9274b6bSCho, Yu-Chen 
4477ba5da81SRichard Henderson     access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
4487ba5da81SRichard Henderson     access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
4497ba5da81SRichard Henderson     access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
4502730df91SRichard Henderson     set_helper_retaddr(ra);
4512730df91SRichard Henderson 
452c9274b6bSCho, Yu-Chen     for (i = 0; i < l; i++) {
453c9274b6bSCho, Yu-Chen         const uint8_t x = access_get_byte(env, &srca1, i, ra) |
454c9274b6bSCho, Yu-Chen                           access_get_byte(env, &srca2, i, ra);
455c9274b6bSCho, Yu-Chen 
456c9274b6bSCho, Yu-Chen         c |= x;
457c9274b6bSCho, Yu-Chen         access_set_byte(env, &desta, i, x, ra);
458c9274b6bSCho, Yu-Chen     }
4592730df91SRichard Henderson 
4602730df91SRichard Henderson     clear_helper_retaddr();
461c9274b6bSCho, Yu-Chen     return c != 0;
462c9274b6bSCho, Yu-Chen }
463c9274b6bSCho, Yu-Chen 
HELPER(oc)464c9274b6bSCho, Yu-Chen uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
465c9274b6bSCho, Yu-Chen                     uint64_t src)
466c9274b6bSCho, Yu-Chen {
467c9274b6bSCho, Yu-Chen     return do_helper_oc(env, l, dest, src, GETPC());
468c9274b6bSCho, Yu-Chen }
469c9274b6bSCho, Yu-Chen 
470c9274b6bSCho, Yu-Chen /* memmove */
do_helper_mvc(CPUS390XState * env,uint32_t l,uint64_t dest,uint64_t src,uintptr_t ra)471c9274b6bSCho, Yu-Chen static uint32_t do_helper_mvc(CPUS390XState *env, uint32_t l, uint64_t dest,
472c9274b6bSCho, Yu-Chen                               uint64_t src, uintptr_t ra)
473c9274b6bSCho, Yu-Chen {
47490b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
475c9274b6bSCho, Yu-Chen     S390Access srca, desta;
476c9274b6bSCho, Yu-Chen     uint32_t i;
477c9274b6bSCho, Yu-Chen 
478c9274b6bSCho, Yu-Chen     HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
479c9274b6bSCho, Yu-Chen                __func__, l, dest, src);
480c9274b6bSCho, Yu-Chen 
481c9274b6bSCho, Yu-Chen     /* MVC always copies one more byte than specified - maximum is 256 */
482c9274b6bSCho, Yu-Chen     l++;
483c9274b6bSCho, Yu-Chen 
4847ba5da81SRichard Henderson     access_prepare(&srca, env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
4857ba5da81SRichard Henderson     access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
486c9274b6bSCho, Yu-Chen 
487c9274b6bSCho, Yu-Chen     /*
488c9274b6bSCho, Yu-Chen      * "When the operands overlap, the result is obtained as if the operands
489c9274b6bSCho, Yu-Chen      * were processed one byte at a time". Only non-destructive overlaps
490c9274b6bSCho, Yu-Chen      * behave like memmove().
491c9274b6bSCho, Yu-Chen      */
492c9274b6bSCho, Yu-Chen     if (dest == src + 1) {
493c9274b6bSCho, Yu-Chen         access_memset(env, &desta, access_get_byte(env, &srca, 0, ra), ra);
494c9274b6bSCho, Yu-Chen     } else if (!is_destructive_overlap(env, dest, src, l)) {
495c9274b6bSCho, Yu-Chen         access_memmove(env, &desta, &srca, ra);
496c9274b6bSCho, Yu-Chen     } else {
4972730df91SRichard Henderson         set_helper_retaddr(ra);
498c9274b6bSCho, Yu-Chen         for (i = 0; i < l; i++) {
499c9274b6bSCho, Yu-Chen             uint8_t byte = access_get_byte(env, &srca, i, ra);
500c9274b6bSCho, Yu-Chen 
501c9274b6bSCho, Yu-Chen             access_set_byte(env, &desta, i, byte, ra);
502c9274b6bSCho, Yu-Chen         }
5032730df91SRichard Henderson         clear_helper_retaddr();
504c9274b6bSCho, Yu-Chen     }
505c9274b6bSCho, Yu-Chen 
506c9274b6bSCho, Yu-Chen     return env->cc_op;
507c9274b6bSCho, Yu-Chen }
508c9274b6bSCho, Yu-Chen 
HELPER(mvc)509c9274b6bSCho, Yu-Chen void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
510c9274b6bSCho, Yu-Chen {
511c9274b6bSCho, Yu-Chen     do_helper_mvc(env, l, dest, src, GETPC());
512c9274b6bSCho, Yu-Chen }
513c9274b6bSCho, Yu-Chen 
514ea0a1053SDavid Miller /* move right to left */
HELPER(mvcrl)515ea0a1053SDavid Miller void HELPER(mvcrl)(CPUS390XState *env, uint64_t l, uint64_t dest, uint64_t src)
516ea0a1053SDavid Miller {
51790b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
518ea0a1053SDavid Miller     const uint64_t ra = GETPC();
519ea0a1053SDavid Miller     S390Access srca, desta;
520ea0a1053SDavid Miller     int32_t i;
521ea0a1053SDavid Miller 
522ea0a1053SDavid Miller     /* MVCRL always copies one more byte than specified - maximum is 256 */
52392a57534SIlya Leoshkevich     l &= 0xff;
524ea0a1053SDavid Miller     l++;
525ea0a1053SDavid Miller 
5267ba5da81SRichard Henderson     access_prepare(&srca, env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
5277ba5da81SRichard Henderson     access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
528ea0a1053SDavid Miller 
5292730df91SRichard Henderson     set_helper_retaddr(ra);
530ea0a1053SDavid Miller     for (i = l - 1; i >= 0; i--) {
531ea0a1053SDavid Miller         uint8_t byte = access_get_byte(env, &srca, i, ra);
532ea0a1053SDavid Miller         access_set_byte(env, &desta, i, byte, ra);
533ea0a1053SDavid Miller     }
5342730df91SRichard Henderson     clear_helper_retaddr();
535ea0a1053SDavid Miller }
536ea0a1053SDavid Miller 
537c9274b6bSCho, Yu-Chen /* move inverse  */
HELPER(mvcin)538c9274b6bSCho, Yu-Chen void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
539c9274b6bSCho, Yu-Chen {
54090b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
541c9274b6bSCho, Yu-Chen     S390Access srca, desta;
542c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
543c9274b6bSCho, Yu-Chen     int i;
544c9274b6bSCho, Yu-Chen 
545c9274b6bSCho, Yu-Chen     /* MVCIN always copies one more byte than specified - maximum is 256 */
546c9274b6bSCho, Yu-Chen     l++;
547c9274b6bSCho, Yu-Chen 
548c9274b6bSCho, Yu-Chen     src = wrap_address(env, src - l + 1);
5497ba5da81SRichard Henderson     access_prepare(&srca, env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
5507ba5da81SRichard Henderson     access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
5512730df91SRichard Henderson 
5522730df91SRichard Henderson     set_helper_retaddr(ra);
553c9274b6bSCho, Yu-Chen     for (i = 0; i < l; i++) {
554c9274b6bSCho, Yu-Chen         const uint8_t x = access_get_byte(env, &srca, l - i - 1, ra);
555c9274b6bSCho, Yu-Chen         access_set_byte(env, &desta, i, x, ra);
556c9274b6bSCho, Yu-Chen     }
5572730df91SRichard Henderson     clear_helper_retaddr();
558c9274b6bSCho, Yu-Chen }
559c9274b6bSCho, Yu-Chen 
560c9274b6bSCho, Yu-Chen /* move numerics  */
HELPER(mvn)561c9274b6bSCho, Yu-Chen void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
562c9274b6bSCho, Yu-Chen {
56390b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
564c9274b6bSCho, Yu-Chen     S390Access srca1, srca2, desta;
565c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
566c9274b6bSCho, Yu-Chen     int i;
567c9274b6bSCho, Yu-Chen 
568c9274b6bSCho, Yu-Chen     /* MVN always copies one more byte than specified - maximum is 256 */
569c9274b6bSCho, Yu-Chen     l++;
570c9274b6bSCho, Yu-Chen 
5717ba5da81SRichard Henderson     access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
5727ba5da81SRichard Henderson     access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
5737ba5da81SRichard Henderson     access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
5742730df91SRichard Henderson 
5752730df91SRichard Henderson     set_helper_retaddr(ra);
576c9274b6bSCho, Yu-Chen     for (i = 0; i < l; i++) {
577c9274b6bSCho, Yu-Chen         const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0x0f) |
578c9274b6bSCho, Yu-Chen                           (access_get_byte(env, &srca2, i, ra) & 0xf0);
579c9274b6bSCho, Yu-Chen 
580c9274b6bSCho, Yu-Chen         access_set_byte(env, &desta, i, x, ra);
581c9274b6bSCho, Yu-Chen     }
5822730df91SRichard Henderson     clear_helper_retaddr();
583c9274b6bSCho, Yu-Chen }
584c9274b6bSCho, Yu-Chen 
585c9274b6bSCho, Yu-Chen /* move with offset  */
HELPER(mvo)586c9274b6bSCho, Yu-Chen void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
587c9274b6bSCho, Yu-Chen {
58890b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
589c9274b6bSCho, Yu-Chen     /* MVO always processes one more byte than specified - maximum is 16 */
590c9274b6bSCho, Yu-Chen     const int len_dest = (l >> 4) + 1;
591c9274b6bSCho, Yu-Chen     const int len_src = (l & 0xf) + 1;
592c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
593c9274b6bSCho, Yu-Chen     uint8_t byte_dest, byte_src;
594c9274b6bSCho, Yu-Chen     S390Access srca, desta;
595c9274b6bSCho, Yu-Chen     int i, j;
596c9274b6bSCho, Yu-Chen 
5977ba5da81SRichard Henderson     access_prepare(&srca, env, src, len_src, MMU_DATA_LOAD, mmu_idx, ra);
5987ba5da81SRichard Henderson     access_prepare(&desta, env, dest, len_dest, MMU_DATA_STORE, mmu_idx, ra);
599c9274b6bSCho, Yu-Chen 
600c9274b6bSCho, Yu-Chen     /* Handle rightmost byte */
601c9274b6bSCho, Yu-Chen     byte_dest = cpu_ldub_data_ra(env, dest + len_dest - 1, ra);
6022730df91SRichard Henderson 
6032730df91SRichard Henderson     set_helper_retaddr(ra);
604c9274b6bSCho, Yu-Chen     byte_src = access_get_byte(env, &srca, len_src - 1, ra);
605c9274b6bSCho, Yu-Chen     byte_dest = (byte_dest & 0x0f) | (byte_src << 4);
606c9274b6bSCho, Yu-Chen     access_set_byte(env, &desta, len_dest - 1, byte_dest, ra);
607c9274b6bSCho, Yu-Chen 
608c9274b6bSCho, Yu-Chen     /* Process remaining bytes from right to left */
609c9274b6bSCho, Yu-Chen     for (i = len_dest - 2, j = len_src - 2; i >= 0; i--, j--) {
610c9274b6bSCho, Yu-Chen         byte_dest = byte_src >> 4;
611c9274b6bSCho, Yu-Chen         if (j >= 0) {
612c9274b6bSCho, Yu-Chen             byte_src = access_get_byte(env, &srca, j, ra);
613c9274b6bSCho, Yu-Chen         } else {
614c9274b6bSCho, Yu-Chen             byte_src = 0;
615c9274b6bSCho, Yu-Chen         }
616c9274b6bSCho, Yu-Chen         byte_dest |= byte_src << 4;
617c9274b6bSCho, Yu-Chen         access_set_byte(env, &desta, i, byte_dest, ra);
618c9274b6bSCho, Yu-Chen     }
6192730df91SRichard Henderson     clear_helper_retaddr();
620c9274b6bSCho, Yu-Chen }
621c9274b6bSCho, Yu-Chen 
622c9274b6bSCho, Yu-Chen /* move zones  */
HELPER(mvz)623c9274b6bSCho, Yu-Chen void HELPER(mvz)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
624c9274b6bSCho, Yu-Chen {
62590b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
626c9274b6bSCho, Yu-Chen     S390Access srca1, srca2, desta;
627c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
628c9274b6bSCho, Yu-Chen     int i;
629c9274b6bSCho, Yu-Chen 
630c9274b6bSCho, Yu-Chen     /* MVZ always copies one more byte than specified - maximum is 256 */
631c9274b6bSCho, Yu-Chen     l++;
632c9274b6bSCho, Yu-Chen 
6337ba5da81SRichard Henderson     access_prepare(&srca1, env, src, l, MMU_DATA_LOAD, mmu_idx, ra);
6347ba5da81SRichard Henderson     access_prepare(&srca2, env, dest, l, MMU_DATA_LOAD, mmu_idx, ra);
6357ba5da81SRichard Henderson     access_prepare(&desta, env, dest, l, MMU_DATA_STORE, mmu_idx, ra);
6362730df91SRichard Henderson 
6372730df91SRichard Henderson     set_helper_retaddr(ra);
638c9274b6bSCho, Yu-Chen     for (i = 0; i < l; i++) {
639c9274b6bSCho, Yu-Chen         const uint8_t x = (access_get_byte(env, &srca1, i, ra) & 0xf0) |
640c9274b6bSCho, Yu-Chen                           (access_get_byte(env, &srca2, i, ra) & 0x0f);
641c9274b6bSCho, Yu-Chen 
642c9274b6bSCho, Yu-Chen         access_set_byte(env, &desta, i, x, ra);
643c9274b6bSCho, Yu-Chen     }
6442730df91SRichard Henderson     clear_helper_retaddr();
645c9274b6bSCho, Yu-Chen }
646c9274b6bSCho, Yu-Chen 
647c9274b6bSCho, Yu-Chen /* compare unsigned byte arrays */
do_helper_clc(CPUS390XState * env,uint32_t l,uint64_t s1,uint64_t s2,uintptr_t ra)648c9274b6bSCho, Yu-Chen static uint32_t do_helper_clc(CPUS390XState *env, uint32_t l, uint64_t s1,
649c9274b6bSCho, Yu-Chen                               uint64_t s2, uintptr_t ra)
650c9274b6bSCho, Yu-Chen {
651c9274b6bSCho, Yu-Chen     uint32_t i;
652c9274b6bSCho, Yu-Chen     uint32_t cc = 0;
653c9274b6bSCho, Yu-Chen 
654c9274b6bSCho, Yu-Chen     HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
655c9274b6bSCho, Yu-Chen                __func__, l, s1, s2);
656c9274b6bSCho, Yu-Chen 
657c9274b6bSCho, Yu-Chen     for (i = 0; i <= l; i++) {
658c9274b6bSCho, Yu-Chen         uint8_t x = cpu_ldub_data_ra(env, s1 + i, ra);
659c9274b6bSCho, Yu-Chen         uint8_t y = cpu_ldub_data_ra(env, s2 + i, ra);
660c9274b6bSCho, Yu-Chen         HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
661c9274b6bSCho, Yu-Chen         if (x < y) {
662c9274b6bSCho, Yu-Chen             cc = 1;
663c9274b6bSCho, Yu-Chen             break;
664c9274b6bSCho, Yu-Chen         } else if (x > y) {
665c9274b6bSCho, Yu-Chen             cc = 2;
666c9274b6bSCho, Yu-Chen             break;
667c9274b6bSCho, Yu-Chen         }
668c9274b6bSCho, Yu-Chen     }
669c9274b6bSCho, Yu-Chen 
670c9274b6bSCho, Yu-Chen     HELPER_LOG("\n");
671c9274b6bSCho, Yu-Chen     return cc;
672c9274b6bSCho, Yu-Chen }
673c9274b6bSCho, Yu-Chen 
HELPER(clc)674c9274b6bSCho, Yu-Chen uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
675c9274b6bSCho, Yu-Chen {
676c9274b6bSCho, Yu-Chen     return do_helper_clc(env, l, s1, s2, GETPC());
677c9274b6bSCho, Yu-Chen }
678c9274b6bSCho, Yu-Chen 
679c9274b6bSCho, Yu-Chen /* compare logical under mask */
HELPER(clm)680c9274b6bSCho, Yu-Chen uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
681c9274b6bSCho, Yu-Chen                      uint64_t addr)
682c9274b6bSCho, Yu-Chen {
683c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
684c9274b6bSCho, Yu-Chen     uint32_t cc = 0;
685c9274b6bSCho, Yu-Chen 
686c9274b6bSCho, Yu-Chen     HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
687c9274b6bSCho, Yu-Chen                mask, addr);
688c9274b6bSCho, Yu-Chen 
6894b6e4c0bSIlya Leoshkevich     if (!mask) {
6904b6e4c0bSIlya Leoshkevich         /* Recognize access exceptions for the first byte */
69190b7022eSRichard Henderson         probe_read(env, addr, 1, s390x_env_mmu_index(env, false), ra);
6924b6e4c0bSIlya Leoshkevich     }
6934b6e4c0bSIlya Leoshkevich 
694c9274b6bSCho, Yu-Chen     while (mask) {
695c9274b6bSCho, Yu-Chen         if (mask & 8) {
696c9274b6bSCho, Yu-Chen             uint8_t d = cpu_ldub_data_ra(env, addr, ra);
697c9274b6bSCho, Yu-Chen             uint8_t r = extract32(r1, 24, 8);
698c9274b6bSCho, Yu-Chen             HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
699c9274b6bSCho, Yu-Chen                        addr);
700c9274b6bSCho, Yu-Chen             if (r < d) {
701c9274b6bSCho, Yu-Chen                 cc = 1;
702c9274b6bSCho, Yu-Chen                 break;
703c9274b6bSCho, Yu-Chen             } else if (r > d) {
704c9274b6bSCho, Yu-Chen                 cc = 2;
705c9274b6bSCho, Yu-Chen                 break;
706c9274b6bSCho, Yu-Chen             }
707c9274b6bSCho, Yu-Chen             addr++;
708c9274b6bSCho, Yu-Chen         }
709c9274b6bSCho, Yu-Chen         mask = (mask << 1) & 0xf;
710c9274b6bSCho, Yu-Chen         r1 <<= 8;
711c9274b6bSCho, Yu-Chen     }
712c9274b6bSCho, Yu-Chen 
713c9274b6bSCho, Yu-Chen     HELPER_LOG("\n");
714c9274b6bSCho, Yu-Chen     return cc;
715c9274b6bSCho, Yu-Chen }
716c9274b6bSCho, Yu-Chen 
get_address(CPUS390XState * env,int reg)717c9274b6bSCho, Yu-Chen static inline uint64_t get_address(CPUS390XState *env, int reg)
718c9274b6bSCho, Yu-Chen {
719c9274b6bSCho, Yu-Chen     return wrap_address(env, env->regs[reg]);
720c9274b6bSCho, Yu-Chen }
721c9274b6bSCho, Yu-Chen 
722c9274b6bSCho, Yu-Chen /*
723c9274b6bSCho, Yu-Chen  * Store the address to the given register, zeroing out unused leftmost
724c9274b6bSCho, Yu-Chen  * bits in bit positions 32-63 (24-bit and 31-bit mode only).
725c9274b6bSCho, Yu-Chen  */
set_address_zero(CPUS390XState * env,int reg,uint64_t address)726c9274b6bSCho, Yu-Chen static inline void set_address_zero(CPUS390XState *env, int reg,
727c9274b6bSCho, Yu-Chen                                     uint64_t address)
728c9274b6bSCho, Yu-Chen {
729c9274b6bSCho, Yu-Chen     if (env->psw.mask & PSW_MASK_64) {
730c9274b6bSCho, Yu-Chen         env->regs[reg] = address;
731c9274b6bSCho, Yu-Chen     } else {
732c9274b6bSCho, Yu-Chen         if (!(env->psw.mask & PSW_MASK_32)) {
733c9274b6bSCho, Yu-Chen             address &= 0x00ffffff;
734c9274b6bSCho, Yu-Chen         } else {
735c9274b6bSCho, Yu-Chen             address &= 0x7fffffff;
736c9274b6bSCho, Yu-Chen         }
737c9274b6bSCho, Yu-Chen         env->regs[reg] = deposit64(env->regs[reg], 0, 32, address);
738c9274b6bSCho, Yu-Chen     }
739c9274b6bSCho, Yu-Chen }
740c9274b6bSCho, Yu-Chen 
set_address(CPUS390XState * env,int reg,uint64_t address)741c9274b6bSCho, Yu-Chen static inline void set_address(CPUS390XState *env, int reg, uint64_t address)
742c9274b6bSCho, Yu-Chen {
743c9274b6bSCho, Yu-Chen     if (env->psw.mask & PSW_MASK_64) {
744c9274b6bSCho, Yu-Chen         /* 64-Bit mode */
745c9274b6bSCho, Yu-Chen         env->regs[reg] = address;
746c9274b6bSCho, Yu-Chen     } else {
747c9274b6bSCho, Yu-Chen         if (!(env->psw.mask & PSW_MASK_32)) {
748c9274b6bSCho, Yu-Chen             /* 24-Bit mode. According to the PoO it is implementation
749c9274b6bSCho, Yu-Chen             dependent if bits 32-39 remain unchanged or are set to
750c9274b6bSCho, Yu-Chen             zeros.  Choose the former so that the function can also be
751c9274b6bSCho, Yu-Chen             used for TRT.  */
752c9274b6bSCho, Yu-Chen             env->regs[reg] = deposit64(env->regs[reg], 0, 24, address);
753c9274b6bSCho, Yu-Chen         } else {
754c9274b6bSCho, Yu-Chen             /* 31-Bit mode. According to the PoO it is implementation
755c9274b6bSCho, Yu-Chen             dependent if bit 32 remains unchanged or is set to zero.
756c9274b6bSCho, Yu-Chen             Choose the latter so that the function can also be used for
757c9274b6bSCho, Yu-Chen             TRT.  */
758c9274b6bSCho, Yu-Chen             address &= 0x7fffffff;
759c9274b6bSCho, Yu-Chen             env->regs[reg] = deposit64(env->regs[reg], 0, 32, address);
760c9274b6bSCho, Yu-Chen         }
761c9274b6bSCho, Yu-Chen     }
762c9274b6bSCho, Yu-Chen }
763c9274b6bSCho, Yu-Chen 
wrap_length32(CPUS390XState * env,uint64_t length)764c9274b6bSCho, Yu-Chen static inline uint64_t wrap_length32(CPUS390XState *env, uint64_t length)
765c9274b6bSCho, Yu-Chen {
766c9274b6bSCho, Yu-Chen     if (!(env->psw.mask & PSW_MASK_64)) {
767c9274b6bSCho, Yu-Chen         return (uint32_t)length;
768c9274b6bSCho, Yu-Chen     }
769c9274b6bSCho, Yu-Chen     return length;
770c9274b6bSCho, Yu-Chen }
771c9274b6bSCho, Yu-Chen 
wrap_length31(CPUS390XState * env,uint64_t length)772c9274b6bSCho, Yu-Chen static inline uint64_t wrap_length31(CPUS390XState *env, uint64_t length)
773c9274b6bSCho, Yu-Chen {
774c9274b6bSCho, Yu-Chen     if (!(env->psw.mask & PSW_MASK_64)) {
775c9274b6bSCho, Yu-Chen         /* 24-Bit and 31-Bit mode */
776c9274b6bSCho, Yu-Chen         length &= 0x7fffffff;
777c9274b6bSCho, Yu-Chen     }
778c9274b6bSCho, Yu-Chen     return length;
779c9274b6bSCho, Yu-Chen }
780c9274b6bSCho, Yu-Chen 
get_length(CPUS390XState * env,int reg)781c9274b6bSCho, Yu-Chen static inline uint64_t get_length(CPUS390XState *env, int reg)
782c9274b6bSCho, Yu-Chen {
783c9274b6bSCho, Yu-Chen     return wrap_length31(env, env->regs[reg]);
784c9274b6bSCho, Yu-Chen }
785c9274b6bSCho, Yu-Chen 
set_length(CPUS390XState * env,int reg,uint64_t length)786c9274b6bSCho, Yu-Chen static inline void set_length(CPUS390XState *env, int reg, uint64_t length)
787c9274b6bSCho, Yu-Chen {
788c9274b6bSCho, Yu-Chen     if (env->psw.mask & PSW_MASK_64) {
789c9274b6bSCho, Yu-Chen         /* 64-Bit mode */
790c9274b6bSCho, Yu-Chen         env->regs[reg] = length;
791c9274b6bSCho, Yu-Chen     } else {
792c9274b6bSCho, Yu-Chen         /* 24-Bit and 31-Bit mode */
793c9274b6bSCho, Yu-Chen         env->regs[reg] = deposit64(env->regs[reg], 0, 32, length);
794c9274b6bSCho, Yu-Chen     }
795c9274b6bSCho, Yu-Chen }
796c9274b6bSCho, Yu-Chen 
797c9274b6bSCho, Yu-Chen /* search string (c is byte to search, r2 is string, r1 end of string) */
HELPER(srst)798c9274b6bSCho, Yu-Chen void HELPER(srst)(CPUS390XState *env, uint32_t r1, uint32_t r2)
799c9274b6bSCho, Yu-Chen {
800c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
801c9274b6bSCho, Yu-Chen     uint64_t end, str;
802c9274b6bSCho, Yu-Chen     uint32_t len;
803c9274b6bSCho, Yu-Chen     uint8_t v, c = env->regs[0];
804c9274b6bSCho, Yu-Chen 
805c9274b6bSCho, Yu-Chen     /* Bits 32-55 must contain all 0.  */
806c9274b6bSCho, Yu-Chen     if (env->regs[0] & 0xffffff00u) {
807c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
808c9274b6bSCho, Yu-Chen     }
809c9274b6bSCho, Yu-Chen 
810c9274b6bSCho, Yu-Chen     str = get_address(env, r2);
811c9274b6bSCho, Yu-Chen     end = get_address(env, r1);
812c9274b6bSCho, Yu-Chen 
813c9274b6bSCho, Yu-Chen     /* Lest we fail to service interrupts in a timely manner, limit the
814c9274b6bSCho, Yu-Chen        amount of work we're willing to do.  For now, let's cap at 8k.  */
815c9274b6bSCho, Yu-Chen     for (len = 0; len < 0x2000; ++len) {
816c9274b6bSCho, Yu-Chen         if (str + len == end) {
817c9274b6bSCho, Yu-Chen             /* Character not found.  R1 & R2 are unmodified.  */
818c9274b6bSCho, Yu-Chen             env->cc_op = 2;
819c9274b6bSCho, Yu-Chen             return;
820c9274b6bSCho, Yu-Chen         }
821c9274b6bSCho, Yu-Chen         v = cpu_ldub_data_ra(env, str + len, ra);
822c9274b6bSCho, Yu-Chen         if (v == c) {
823c9274b6bSCho, Yu-Chen             /* Character found.  Set R1 to the location; R2 is unmodified.  */
824c9274b6bSCho, Yu-Chen             env->cc_op = 1;
825c9274b6bSCho, Yu-Chen             set_address(env, r1, str + len);
826c9274b6bSCho, Yu-Chen             return;
827c9274b6bSCho, Yu-Chen         }
828c9274b6bSCho, Yu-Chen     }
829c9274b6bSCho, Yu-Chen 
830c9274b6bSCho, Yu-Chen     /* CPU-determined bytes processed.  Advance R2 to next byte to process.  */
831c9274b6bSCho, Yu-Chen     env->cc_op = 3;
832c9274b6bSCho, Yu-Chen     set_address(env, r2, str + len);
833c9274b6bSCho, Yu-Chen }
834c9274b6bSCho, Yu-Chen 
HELPER(srstu)835c9274b6bSCho, Yu-Chen void HELPER(srstu)(CPUS390XState *env, uint32_t r1, uint32_t r2)
836c9274b6bSCho, Yu-Chen {
837c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
838c9274b6bSCho, Yu-Chen     uint32_t len;
839c9274b6bSCho, Yu-Chen     uint16_t v, c = env->regs[0];
840c9274b6bSCho, Yu-Chen     uint64_t end, str, adj_end;
841c9274b6bSCho, Yu-Chen 
842c9274b6bSCho, Yu-Chen     /* Bits 32-47 of R0 must be zero.  */
843c9274b6bSCho, Yu-Chen     if (env->regs[0] & 0xffff0000u) {
844c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
845c9274b6bSCho, Yu-Chen     }
846c9274b6bSCho, Yu-Chen 
847c9274b6bSCho, Yu-Chen     str = get_address(env, r2);
848c9274b6bSCho, Yu-Chen     end = get_address(env, r1);
849c9274b6bSCho, Yu-Chen 
850c9274b6bSCho, Yu-Chen     /* If the LSB of the two addresses differ, use one extra byte.  */
851c9274b6bSCho, Yu-Chen     adj_end = end + ((str ^ end) & 1);
852c9274b6bSCho, Yu-Chen 
853c9274b6bSCho, Yu-Chen     /* Lest we fail to service interrupts in a timely manner, limit the
854c9274b6bSCho, Yu-Chen        amount of work we're willing to do.  For now, let's cap at 8k.  */
855c9274b6bSCho, Yu-Chen     for (len = 0; len < 0x2000; len += 2) {
856c9274b6bSCho, Yu-Chen         if (str + len == adj_end) {
857c9274b6bSCho, Yu-Chen             /* End of input found.  */
858c9274b6bSCho, Yu-Chen             env->cc_op = 2;
859c9274b6bSCho, Yu-Chen             return;
860c9274b6bSCho, Yu-Chen         }
861c9274b6bSCho, Yu-Chen         v = cpu_lduw_data_ra(env, str + len, ra);
862c9274b6bSCho, Yu-Chen         if (v == c) {
863c9274b6bSCho, Yu-Chen             /* Character found.  Set R1 to the location; R2 is unmodified.  */
864c9274b6bSCho, Yu-Chen             env->cc_op = 1;
865c9274b6bSCho, Yu-Chen             set_address(env, r1, str + len);
866c9274b6bSCho, Yu-Chen             return;
867c9274b6bSCho, Yu-Chen         }
868c9274b6bSCho, Yu-Chen     }
869c9274b6bSCho, Yu-Chen 
870c9274b6bSCho, Yu-Chen     /* CPU-determined bytes processed.  Advance R2 to next byte to process.  */
871c9274b6bSCho, Yu-Chen     env->cc_op = 3;
872c9274b6bSCho, Yu-Chen     set_address(env, r2, str + len);
873c9274b6bSCho, Yu-Chen }
874c9274b6bSCho, Yu-Chen 
875c9274b6bSCho, Yu-Chen /* unsigned string compare (c is string terminator) */
HELPER(clst)876b71dd2a5SRichard Henderson Int128 HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
877c9274b6bSCho, Yu-Chen {
878c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
879c9274b6bSCho, Yu-Chen     uint32_t len;
880c9274b6bSCho, Yu-Chen 
881c9274b6bSCho, Yu-Chen     c = c & 0xff;
882c9274b6bSCho, Yu-Chen     s1 = wrap_address(env, s1);
883c9274b6bSCho, Yu-Chen     s2 = wrap_address(env, s2);
884c9274b6bSCho, Yu-Chen 
885c9274b6bSCho, Yu-Chen     /* Lest we fail to service interrupts in a timely manner, limit the
886c9274b6bSCho, Yu-Chen        amount of work we're willing to do.  For now, let's cap at 8k.  */
887c9274b6bSCho, Yu-Chen     for (len = 0; len < 0x2000; ++len) {
888c9274b6bSCho, Yu-Chen         uint8_t v1 = cpu_ldub_data_ra(env, s1 + len, ra);
889c9274b6bSCho, Yu-Chen         uint8_t v2 = cpu_ldub_data_ra(env, s2 + len, ra);
890c9274b6bSCho, Yu-Chen         if (v1 == v2) {
891c9274b6bSCho, Yu-Chen             if (v1 == c) {
892c9274b6bSCho, Yu-Chen                 /* Equal.  CC=0, and don't advance the registers.  */
893c9274b6bSCho, Yu-Chen                 env->cc_op = 0;
894b71dd2a5SRichard Henderson                 return int128_make128(s2, s1);
895c9274b6bSCho, Yu-Chen             }
896c9274b6bSCho, Yu-Chen         } else {
897c9274b6bSCho, Yu-Chen             /* Unequal.  CC={1,2}, and advance the registers.  Note that
898c9274b6bSCho, Yu-Chen                the terminator need not be zero, but the string that contains
899c9274b6bSCho, Yu-Chen                the terminator is by definition "low".  */
900c9274b6bSCho, Yu-Chen             env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
901b71dd2a5SRichard Henderson             return int128_make128(s2 + len, s1 + len);
902c9274b6bSCho, Yu-Chen         }
903c9274b6bSCho, Yu-Chen     }
904c9274b6bSCho, Yu-Chen 
905c9274b6bSCho, Yu-Chen     /* CPU-determined bytes equal; advance the registers.  */
906c9274b6bSCho, Yu-Chen     env->cc_op = 3;
907b71dd2a5SRichard Henderson     return int128_make128(s2 + len, s1 + len);
908c9274b6bSCho, Yu-Chen }
909c9274b6bSCho, Yu-Chen 
910c9274b6bSCho, Yu-Chen /* move page */
HELPER(mvpg)911c9274b6bSCho, Yu-Chen uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint32_t r1, uint32_t r2)
912c9274b6bSCho, Yu-Chen {
913c9274b6bSCho, Yu-Chen     const uint64_t src = get_address(env, r2) & TARGET_PAGE_MASK;
914c9274b6bSCho, Yu-Chen     const uint64_t dst = get_address(env, r1) & TARGET_PAGE_MASK;
91590b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
916c9274b6bSCho, Yu-Chen     const bool f = extract64(r0, 11, 1);
917c9274b6bSCho, Yu-Chen     const bool s = extract64(r0, 10, 1);
918c9274b6bSCho, Yu-Chen     const bool cco = extract64(r0, 8, 1);
919c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
920c9274b6bSCho, Yu-Chen     S390Access srca, desta;
921c9274b6bSCho, Yu-Chen     int exc;
922c9274b6bSCho, Yu-Chen 
923c9274b6bSCho, Yu-Chen     if ((f && s) || extract64(r0, 12, 4)) {
924c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
925c9274b6bSCho, Yu-Chen     }
926c9274b6bSCho, Yu-Chen 
927c9274b6bSCho, Yu-Chen     /*
928c9274b6bSCho, Yu-Chen      * We always manually handle exceptions such that we can properly store
929c9274b6bSCho, Yu-Chen      * r1/r2 to the lowcore on page-translation exceptions.
930c9274b6bSCho, Yu-Chen      *
931c9274b6bSCho, Yu-Chen      * TODO: Access key handling
932c9274b6bSCho, Yu-Chen      */
933c9274b6bSCho, Yu-Chen     exc = access_prepare_nf(&srca, env, true, src, TARGET_PAGE_SIZE,
934c9274b6bSCho, Yu-Chen                             MMU_DATA_LOAD, mmu_idx, ra);
935c9274b6bSCho, Yu-Chen     if (exc) {
936c9274b6bSCho, Yu-Chen         if (cco) {
937c9274b6bSCho, Yu-Chen             return 2;
938c9274b6bSCho, Yu-Chen         }
939c9274b6bSCho, Yu-Chen         goto inject_exc;
940c9274b6bSCho, Yu-Chen     }
941c9274b6bSCho, Yu-Chen     exc = access_prepare_nf(&desta, env, true, dst, TARGET_PAGE_SIZE,
942c9274b6bSCho, Yu-Chen                             MMU_DATA_STORE, mmu_idx, ra);
943c9274b6bSCho, Yu-Chen     if (exc) {
944c9274b6bSCho, Yu-Chen         if (cco && exc != PGM_PROTECTION) {
945c9274b6bSCho, Yu-Chen             return 1;
946c9274b6bSCho, Yu-Chen         }
947c9274b6bSCho, Yu-Chen         goto inject_exc;
948c9274b6bSCho, Yu-Chen     }
949c9274b6bSCho, Yu-Chen     access_memmove(env, &desta, &srca, ra);
950c9274b6bSCho, Yu-Chen     return 0; /* data moved */
951c9274b6bSCho, Yu-Chen inject_exc:
952c9274b6bSCho, Yu-Chen #if !defined(CONFIG_USER_ONLY)
953c9274b6bSCho, Yu-Chen     if (exc != PGM_ADDRESSING) {
954c9274b6bSCho, Yu-Chen         stq_phys(env_cpu(env)->as, env->psa + offsetof(LowCore, trans_exc_code),
955c9274b6bSCho, Yu-Chen                  env->tlb_fill_tec);
956c9274b6bSCho, Yu-Chen     }
957c9274b6bSCho, Yu-Chen     if (exc == PGM_PAGE_TRANS) {
958c9274b6bSCho, Yu-Chen         stb_phys(env_cpu(env)->as, env->psa + offsetof(LowCore, op_access_id),
959c9274b6bSCho, Yu-Chen                  r1 << 4 | r2);
960c9274b6bSCho, Yu-Chen     }
961c9274b6bSCho, Yu-Chen #endif
962c9274b6bSCho, Yu-Chen     tcg_s390_program_interrupt(env, exc, ra);
963c9274b6bSCho, Yu-Chen }
964c9274b6bSCho, Yu-Chen 
965c9274b6bSCho, Yu-Chen /* string copy */
HELPER(mvst)966c9274b6bSCho, Yu-Chen uint32_t HELPER(mvst)(CPUS390XState *env, uint32_t r1, uint32_t r2)
967c9274b6bSCho, Yu-Chen {
96890b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
969c9274b6bSCho, Yu-Chen     const uint64_t d = get_address(env, r1);
970c9274b6bSCho, Yu-Chen     const uint64_t s = get_address(env, r2);
971c9274b6bSCho, Yu-Chen     const uint8_t c = env->regs[0];
972c9274b6bSCho, Yu-Chen     const int len = MIN(-(d | TARGET_PAGE_MASK), -(s | TARGET_PAGE_MASK));
973c9274b6bSCho, Yu-Chen     S390Access srca, desta;
974c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
975c9274b6bSCho, Yu-Chen     int i;
976c9274b6bSCho, Yu-Chen 
977c9274b6bSCho, Yu-Chen     if (env->regs[0] & 0xffffff00ull) {
978c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
979c9274b6bSCho, Yu-Chen     }
980c9274b6bSCho, Yu-Chen 
981c9274b6bSCho, Yu-Chen     /*
982c9274b6bSCho, Yu-Chen      * Our access should not exceed single pages, as we must not report access
983c9274b6bSCho, Yu-Chen      * exceptions exceeding the actually copied range (which we don't know at
984c9274b6bSCho, Yu-Chen      * this point). We might over-indicate watchpoints within the pages
985c9274b6bSCho, Yu-Chen      * (if we ever care, we have to limit processing to a single byte).
986c9274b6bSCho, Yu-Chen      */
9877ba5da81SRichard Henderson     access_prepare(&srca, env, s, len, MMU_DATA_LOAD, mmu_idx, ra);
9887ba5da81SRichard Henderson     access_prepare(&desta, env, d, len, MMU_DATA_STORE, mmu_idx, ra);
9892730df91SRichard Henderson 
9902730df91SRichard Henderson     set_helper_retaddr(ra);
991c9274b6bSCho, Yu-Chen     for (i = 0; i < len; i++) {
992c9274b6bSCho, Yu-Chen         const uint8_t v = access_get_byte(env, &srca, i, ra);
993c9274b6bSCho, Yu-Chen 
994c9274b6bSCho, Yu-Chen         access_set_byte(env, &desta, i, v, ra);
995c9274b6bSCho, Yu-Chen         if (v == c) {
9962730df91SRichard Henderson             clear_helper_retaddr();
997c9274b6bSCho, Yu-Chen             set_address_zero(env, r1, d + i);
998c9274b6bSCho, Yu-Chen             return 1;
999c9274b6bSCho, Yu-Chen         }
1000c9274b6bSCho, Yu-Chen     }
10012730df91SRichard Henderson     clear_helper_retaddr();
1002c9274b6bSCho, Yu-Chen     set_address_zero(env, r1, d + len);
1003c9274b6bSCho, Yu-Chen     set_address_zero(env, r2, s + len);
1004c9274b6bSCho, Yu-Chen     return 3;
1005c9274b6bSCho, Yu-Chen }
1006c9274b6bSCho, Yu-Chen 
1007c9274b6bSCho, Yu-Chen /* load access registers r1 to r3 from memory at a2 */
HELPER(lam)1008c9274b6bSCho, Yu-Chen void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
1009c9274b6bSCho, Yu-Chen {
1010c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1011c9274b6bSCho, Yu-Chen     int i;
1012c9274b6bSCho, Yu-Chen 
1013c9274b6bSCho, Yu-Chen     if (a2 & 0x3) {
1014c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
1015c9274b6bSCho, Yu-Chen     }
1016c9274b6bSCho, Yu-Chen 
1017c9274b6bSCho, Yu-Chen     for (i = r1;; i = (i + 1) % 16) {
1018c9274b6bSCho, Yu-Chen         env->aregs[i] = cpu_ldl_data_ra(env, a2, ra);
1019c9274b6bSCho, Yu-Chen         a2 += 4;
1020c9274b6bSCho, Yu-Chen 
1021c9274b6bSCho, Yu-Chen         if (i == r3) {
1022c9274b6bSCho, Yu-Chen             break;
1023c9274b6bSCho, Yu-Chen         }
1024c9274b6bSCho, Yu-Chen     }
1025c9274b6bSCho, Yu-Chen }
1026c9274b6bSCho, Yu-Chen 
1027c9274b6bSCho, Yu-Chen /* store access registers r1 to r3 in memory at a2 */
HELPER(stam)1028c9274b6bSCho, Yu-Chen void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
1029c9274b6bSCho, Yu-Chen {
1030c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1031c9274b6bSCho, Yu-Chen     int i;
1032c9274b6bSCho, Yu-Chen 
1033c9274b6bSCho, Yu-Chen     if (a2 & 0x3) {
1034c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
1035c9274b6bSCho, Yu-Chen     }
1036c9274b6bSCho, Yu-Chen 
1037c9274b6bSCho, Yu-Chen     for (i = r1;; i = (i + 1) % 16) {
1038c9274b6bSCho, Yu-Chen         cpu_stl_data_ra(env, a2, env->aregs[i], ra);
1039c9274b6bSCho, Yu-Chen         a2 += 4;
1040c9274b6bSCho, Yu-Chen 
1041c9274b6bSCho, Yu-Chen         if (i == r3) {
1042c9274b6bSCho, Yu-Chen             break;
1043c9274b6bSCho, Yu-Chen         }
1044c9274b6bSCho, Yu-Chen     }
1045c9274b6bSCho, Yu-Chen }
1046c9274b6bSCho, Yu-Chen 
1047c9274b6bSCho, Yu-Chen /* move long helper */
do_mvcl(CPUS390XState * env,uint64_t * dest,uint64_t * destlen,uint64_t * src,uint64_t * srclen,uint16_t pad,int wordsize,uintptr_t ra)1048c9274b6bSCho, Yu-Chen static inline uint32_t do_mvcl(CPUS390XState *env,
1049c9274b6bSCho, Yu-Chen                                uint64_t *dest, uint64_t *destlen,
1050c9274b6bSCho, Yu-Chen                                uint64_t *src, uint64_t *srclen,
1051c9274b6bSCho, Yu-Chen                                uint16_t pad, int wordsize, uintptr_t ra)
1052c9274b6bSCho, Yu-Chen {
105390b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
1054c9274b6bSCho, Yu-Chen     int len = MIN(*destlen, -(*dest | TARGET_PAGE_MASK));
1055c9274b6bSCho, Yu-Chen     S390Access srca, desta;
1056c9274b6bSCho, Yu-Chen     int i, cc;
1057c9274b6bSCho, Yu-Chen 
1058c9274b6bSCho, Yu-Chen     if (*destlen == *srclen) {
1059c9274b6bSCho, Yu-Chen         cc = 0;
1060c9274b6bSCho, Yu-Chen     } else if (*destlen < *srclen) {
1061c9274b6bSCho, Yu-Chen         cc = 1;
1062c9274b6bSCho, Yu-Chen     } else {
1063c9274b6bSCho, Yu-Chen         cc = 2;
1064c9274b6bSCho, Yu-Chen     }
1065c9274b6bSCho, Yu-Chen 
1066c9274b6bSCho, Yu-Chen     if (!*destlen) {
1067c9274b6bSCho, Yu-Chen         return cc;
1068c9274b6bSCho, Yu-Chen     }
1069c9274b6bSCho, Yu-Chen 
1070c9274b6bSCho, Yu-Chen     /*
1071c9274b6bSCho, Yu-Chen      * Only perform one type of type of operation (move/pad) at a time.
1072c9274b6bSCho, Yu-Chen      * Stay within single pages.
1073c9274b6bSCho, Yu-Chen      */
1074c9274b6bSCho, Yu-Chen     if (*srclen) {
1075c9274b6bSCho, Yu-Chen         /* Copy the src array */
1076c9274b6bSCho, Yu-Chen         len = MIN(MIN(*srclen, -(*src | TARGET_PAGE_MASK)), len);
1077c9274b6bSCho, Yu-Chen         *destlen -= len;
1078c9274b6bSCho, Yu-Chen         *srclen -= len;
10797ba5da81SRichard Henderson         access_prepare(&srca, env, *src, len, MMU_DATA_LOAD, mmu_idx, ra);
10807ba5da81SRichard Henderson         access_prepare(&desta, env, *dest, len, MMU_DATA_STORE, mmu_idx, ra);
1081c9274b6bSCho, Yu-Chen         access_memmove(env, &desta, &srca, ra);
1082c9274b6bSCho, Yu-Chen         *src = wrap_address(env, *src + len);
1083c9274b6bSCho, Yu-Chen         *dest = wrap_address(env, *dest + len);
1084c9274b6bSCho, Yu-Chen     } else if (wordsize == 1) {
1085c9274b6bSCho, Yu-Chen         /* Pad the remaining area */
1086c9274b6bSCho, Yu-Chen         *destlen -= len;
10877ba5da81SRichard Henderson         access_prepare(&desta, env, *dest, len, MMU_DATA_STORE, mmu_idx, ra);
1088c9274b6bSCho, Yu-Chen         access_memset(env, &desta, pad, ra);
1089c9274b6bSCho, Yu-Chen         *dest = wrap_address(env, *dest + len);
1090c9274b6bSCho, Yu-Chen     } else {
10917ba5da81SRichard Henderson         access_prepare(&desta, env, *dest, len, MMU_DATA_STORE, mmu_idx, ra);
10922730df91SRichard Henderson         set_helper_retaddr(ra);
1093c9274b6bSCho, Yu-Chen 
1094c9274b6bSCho, Yu-Chen         /* The remaining length selects the padding byte. */
1095c9274b6bSCho, Yu-Chen         for (i = 0; i < len; (*destlen)--, i++) {
1096c9274b6bSCho, Yu-Chen             if (*destlen & 1) {
1097c9274b6bSCho, Yu-Chen                 access_set_byte(env, &desta, i, pad, ra);
1098c9274b6bSCho, Yu-Chen             } else {
1099c9274b6bSCho, Yu-Chen                 access_set_byte(env, &desta, i, pad >> 8, ra);
1100c9274b6bSCho, Yu-Chen             }
1101c9274b6bSCho, Yu-Chen         }
11022730df91SRichard Henderson         clear_helper_retaddr();
1103c9274b6bSCho, Yu-Chen         *dest = wrap_address(env, *dest + len);
1104c9274b6bSCho, Yu-Chen     }
1105c9274b6bSCho, Yu-Chen 
1106c9274b6bSCho, Yu-Chen     return *destlen ? 3 : cc;
1107c9274b6bSCho, Yu-Chen }
1108c9274b6bSCho, Yu-Chen 
1109c9274b6bSCho, Yu-Chen /* move long */
HELPER(mvcl)1110c9274b6bSCho, Yu-Chen uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
1111c9274b6bSCho, Yu-Chen {
111290b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
1113c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1114c9274b6bSCho, Yu-Chen     uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
1115c9274b6bSCho, Yu-Chen     uint64_t dest = get_address(env, r1);
1116c9274b6bSCho, Yu-Chen     uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
1117c9274b6bSCho, Yu-Chen     uint64_t src = get_address(env, r2);
1118c9274b6bSCho, Yu-Chen     uint8_t pad = env->regs[r2 + 1] >> 24;
1119c9274b6bSCho, Yu-Chen     CPUState *cs = env_cpu(env);
1120c9274b6bSCho, Yu-Chen     S390Access srca, desta;
1121c9274b6bSCho, Yu-Chen     uint32_t cc, cur_len;
1122c9274b6bSCho, Yu-Chen 
1123c9274b6bSCho, Yu-Chen     if (is_destructive_overlap(env, dest, src, MIN(srclen, destlen))) {
1124c9274b6bSCho, Yu-Chen         cc = 3;
1125c9274b6bSCho, Yu-Chen     } else if (srclen == destlen) {
1126c9274b6bSCho, Yu-Chen         cc = 0;
1127c9274b6bSCho, Yu-Chen     } else if (destlen < srclen) {
1128c9274b6bSCho, Yu-Chen         cc = 1;
1129c9274b6bSCho, Yu-Chen     } else {
1130c9274b6bSCho, Yu-Chen         cc = 2;
1131c9274b6bSCho, Yu-Chen     }
1132c9274b6bSCho, Yu-Chen 
1133c9274b6bSCho, Yu-Chen     /* We might have to zero-out some bits even if there was no action. */
1134c9274b6bSCho, Yu-Chen     if (unlikely(!destlen || cc == 3)) {
1135c9274b6bSCho, Yu-Chen         set_address_zero(env, r2, src);
1136c9274b6bSCho, Yu-Chen         set_address_zero(env, r1, dest);
1137c9274b6bSCho, Yu-Chen         return cc;
1138c9274b6bSCho, Yu-Chen     } else if (!srclen) {
1139c9274b6bSCho, Yu-Chen         set_address_zero(env, r2, src);
1140c9274b6bSCho, Yu-Chen     }
1141c9274b6bSCho, Yu-Chen 
1142c9274b6bSCho, Yu-Chen     /*
1143c9274b6bSCho, Yu-Chen      * Only perform one type of type of operation (move/pad) in one step.
1144c9274b6bSCho, Yu-Chen      * Stay within single pages.
1145c9274b6bSCho, Yu-Chen      */
1146c9274b6bSCho, Yu-Chen     while (destlen) {
1147c9274b6bSCho, Yu-Chen         cur_len = MIN(destlen, -(dest | TARGET_PAGE_MASK));
1148c9274b6bSCho, Yu-Chen         if (!srclen) {
11497ba5da81SRichard Henderson             access_prepare(&desta, env, dest, cur_len,
11507ba5da81SRichard Henderson                            MMU_DATA_STORE, mmu_idx, ra);
1151c9274b6bSCho, Yu-Chen             access_memset(env, &desta, pad, ra);
1152c9274b6bSCho, Yu-Chen         } else {
1153c9274b6bSCho, Yu-Chen             cur_len = MIN(MIN(srclen, -(src | TARGET_PAGE_MASK)), cur_len);
1154c9274b6bSCho, Yu-Chen 
11557ba5da81SRichard Henderson             access_prepare(&srca, env, src, cur_len,
11567ba5da81SRichard Henderson                            MMU_DATA_LOAD, mmu_idx, ra);
11577ba5da81SRichard Henderson             access_prepare(&desta, env, dest, cur_len,
11587ba5da81SRichard Henderson                            MMU_DATA_STORE, mmu_idx, ra);
1159c9274b6bSCho, Yu-Chen             access_memmove(env, &desta, &srca, ra);
1160c9274b6bSCho, Yu-Chen             src = wrap_address(env, src + cur_len);
1161c9274b6bSCho, Yu-Chen             srclen -= cur_len;
1162c9274b6bSCho, Yu-Chen             env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, srclen);
1163c9274b6bSCho, Yu-Chen             set_address_zero(env, r2, src);
1164c9274b6bSCho, Yu-Chen         }
1165c9274b6bSCho, Yu-Chen         dest = wrap_address(env, dest + cur_len);
1166c9274b6bSCho, Yu-Chen         destlen -= cur_len;
1167c9274b6bSCho, Yu-Chen         env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen);
1168c9274b6bSCho, Yu-Chen         set_address_zero(env, r1, dest);
1169c9274b6bSCho, Yu-Chen 
1170c9274b6bSCho, Yu-Chen         /*
1171c9274b6bSCho, Yu-Chen          * MVCL is interruptible. Return to the main loop if requested after
1172c9274b6bSCho, Yu-Chen          * writing back all state to registers. If no interrupt will get
1173c9274b6bSCho, Yu-Chen          * injected, we'll end up back in this handler and continue processing
1174c9274b6bSCho, Yu-Chen          * the remaining parts.
1175c9274b6bSCho, Yu-Chen          */
1176c9274b6bSCho, Yu-Chen         if (destlen && unlikely(cpu_loop_exit_requested(cs))) {
1177c9274b6bSCho, Yu-Chen             cpu_loop_exit_restore(cs, ra);
1178c9274b6bSCho, Yu-Chen         }
1179c9274b6bSCho, Yu-Chen     }
1180c9274b6bSCho, Yu-Chen     return cc;
1181c9274b6bSCho, Yu-Chen }
1182c9274b6bSCho, Yu-Chen 
1183c9274b6bSCho, Yu-Chen /* move long extended */
HELPER(mvcle)1184c9274b6bSCho, Yu-Chen uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
1185c9274b6bSCho, Yu-Chen                        uint32_t r3)
1186c9274b6bSCho, Yu-Chen {
1187c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1188c9274b6bSCho, Yu-Chen     uint64_t destlen = get_length(env, r1 + 1);
1189c9274b6bSCho, Yu-Chen     uint64_t dest = get_address(env, r1);
1190c9274b6bSCho, Yu-Chen     uint64_t srclen = get_length(env, r3 + 1);
1191c9274b6bSCho, Yu-Chen     uint64_t src = get_address(env, r3);
1192c9274b6bSCho, Yu-Chen     uint8_t pad = a2;
1193c9274b6bSCho, Yu-Chen     uint32_t cc;
1194c9274b6bSCho, Yu-Chen 
1195c9274b6bSCho, Yu-Chen     cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 1, ra);
1196c9274b6bSCho, Yu-Chen 
1197c9274b6bSCho, Yu-Chen     set_length(env, r1 + 1, destlen);
1198c9274b6bSCho, Yu-Chen     set_length(env, r3 + 1, srclen);
1199c9274b6bSCho, Yu-Chen     set_address(env, r1, dest);
1200c9274b6bSCho, Yu-Chen     set_address(env, r3, src);
1201c9274b6bSCho, Yu-Chen 
1202c9274b6bSCho, Yu-Chen     return cc;
1203c9274b6bSCho, Yu-Chen }
1204c9274b6bSCho, Yu-Chen 
1205c9274b6bSCho, Yu-Chen /* move long unicode */
HELPER(mvclu)1206c9274b6bSCho, Yu-Chen uint32_t HELPER(mvclu)(CPUS390XState *env, uint32_t r1, uint64_t a2,
1207c9274b6bSCho, Yu-Chen                        uint32_t r3)
1208c9274b6bSCho, Yu-Chen {
1209c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1210c9274b6bSCho, Yu-Chen     uint64_t destlen = get_length(env, r1 + 1);
1211c9274b6bSCho, Yu-Chen     uint64_t dest = get_address(env, r1);
1212c9274b6bSCho, Yu-Chen     uint64_t srclen = get_length(env, r3 + 1);
1213c9274b6bSCho, Yu-Chen     uint64_t src = get_address(env, r3);
1214c9274b6bSCho, Yu-Chen     uint16_t pad = a2;
1215c9274b6bSCho, Yu-Chen     uint32_t cc;
1216c9274b6bSCho, Yu-Chen 
1217c9274b6bSCho, Yu-Chen     cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 2, ra);
1218c9274b6bSCho, Yu-Chen 
1219c9274b6bSCho, Yu-Chen     set_length(env, r1 + 1, destlen);
1220c9274b6bSCho, Yu-Chen     set_length(env, r3 + 1, srclen);
1221c9274b6bSCho, Yu-Chen     set_address(env, r1, dest);
1222c9274b6bSCho, Yu-Chen     set_address(env, r3, src);
1223c9274b6bSCho, Yu-Chen 
1224c9274b6bSCho, Yu-Chen     return cc;
1225c9274b6bSCho, Yu-Chen }
1226c9274b6bSCho, Yu-Chen 
1227c9274b6bSCho, Yu-Chen /* compare logical long helper */
do_clcl(CPUS390XState * env,uint64_t * src1,uint64_t * src1len,uint64_t * src3,uint64_t * src3len,uint16_t pad,uint64_t limit,int wordsize,uintptr_t ra)1228c9274b6bSCho, Yu-Chen static inline uint32_t do_clcl(CPUS390XState *env,
1229c9274b6bSCho, Yu-Chen                                uint64_t *src1, uint64_t *src1len,
1230c9274b6bSCho, Yu-Chen                                uint64_t *src3, uint64_t *src3len,
1231c9274b6bSCho, Yu-Chen                                uint16_t pad, uint64_t limit,
1232c9274b6bSCho, Yu-Chen                                int wordsize, uintptr_t ra)
1233c9274b6bSCho, Yu-Chen {
1234c9274b6bSCho, Yu-Chen     uint64_t len = MAX(*src1len, *src3len);
1235c9274b6bSCho, Yu-Chen     uint32_t cc = 0;
1236c9274b6bSCho, Yu-Chen 
1237c9274b6bSCho, Yu-Chen     check_alignment(env, *src1len | *src3len, wordsize, ra);
1238c9274b6bSCho, Yu-Chen 
1239c9274b6bSCho, Yu-Chen     if (!len) {
1240c9274b6bSCho, Yu-Chen         return cc;
1241c9274b6bSCho, Yu-Chen     }
1242c9274b6bSCho, Yu-Chen 
1243c9274b6bSCho, Yu-Chen     /* Lest we fail to service interrupts in a timely manner, limit the
1244c9274b6bSCho, Yu-Chen        amount of work we're willing to do.  */
1245c9274b6bSCho, Yu-Chen     if (len > limit) {
1246c9274b6bSCho, Yu-Chen         len = limit;
1247c9274b6bSCho, Yu-Chen         cc = 3;
1248c9274b6bSCho, Yu-Chen     }
1249c9274b6bSCho, Yu-Chen 
1250c9274b6bSCho, Yu-Chen     for (; len; len -= wordsize) {
1251c9274b6bSCho, Yu-Chen         uint16_t v1 = pad;
1252c9274b6bSCho, Yu-Chen         uint16_t v3 = pad;
1253c9274b6bSCho, Yu-Chen 
1254c9274b6bSCho, Yu-Chen         if (*src1len) {
1255c9274b6bSCho, Yu-Chen             v1 = cpu_ldusize_data_ra(env, *src1, wordsize, ra);
1256c9274b6bSCho, Yu-Chen         }
1257c9274b6bSCho, Yu-Chen         if (*src3len) {
1258c9274b6bSCho, Yu-Chen             v3 = cpu_ldusize_data_ra(env, *src3, wordsize, ra);
1259c9274b6bSCho, Yu-Chen         }
1260c9274b6bSCho, Yu-Chen 
1261c9274b6bSCho, Yu-Chen         if (v1 != v3) {
1262c9274b6bSCho, Yu-Chen             cc = (v1 < v3) ? 1 : 2;
1263c9274b6bSCho, Yu-Chen             break;
1264c9274b6bSCho, Yu-Chen         }
1265c9274b6bSCho, Yu-Chen 
1266c9274b6bSCho, Yu-Chen         if (*src1len) {
1267c9274b6bSCho, Yu-Chen             *src1 += wordsize;
1268c9274b6bSCho, Yu-Chen             *src1len -= wordsize;
1269c9274b6bSCho, Yu-Chen         }
1270c9274b6bSCho, Yu-Chen         if (*src3len) {
1271c9274b6bSCho, Yu-Chen             *src3 += wordsize;
1272c9274b6bSCho, Yu-Chen             *src3len -= wordsize;
1273c9274b6bSCho, Yu-Chen         }
1274c9274b6bSCho, Yu-Chen     }
1275c9274b6bSCho, Yu-Chen 
1276c9274b6bSCho, Yu-Chen     return cc;
1277c9274b6bSCho, Yu-Chen }
1278c9274b6bSCho, Yu-Chen 
1279c9274b6bSCho, Yu-Chen 
1280c9274b6bSCho, Yu-Chen /* compare logical long */
HELPER(clcl)1281c9274b6bSCho, Yu-Chen uint32_t HELPER(clcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
1282c9274b6bSCho, Yu-Chen {
1283c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1284c9274b6bSCho, Yu-Chen     uint64_t src1len = extract64(env->regs[r1 + 1], 0, 24);
1285c9274b6bSCho, Yu-Chen     uint64_t src1 = get_address(env, r1);
1286c9274b6bSCho, Yu-Chen     uint64_t src3len = extract64(env->regs[r2 + 1], 0, 24);
1287c9274b6bSCho, Yu-Chen     uint64_t src3 = get_address(env, r2);
1288c9274b6bSCho, Yu-Chen     uint8_t pad = env->regs[r2 + 1] >> 24;
1289c9274b6bSCho, Yu-Chen     uint32_t cc;
1290c9274b6bSCho, Yu-Chen 
1291c9274b6bSCho, Yu-Chen     cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, -1, 1, ra);
1292c9274b6bSCho, Yu-Chen 
1293c9274b6bSCho, Yu-Chen     env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, src1len);
1294c9274b6bSCho, Yu-Chen     env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, src3len);
1295c9274b6bSCho, Yu-Chen     set_address(env, r1, src1);
1296c9274b6bSCho, Yu-Chen     set_address(env, r2, src3);
1297c9274b6bSCho, Yu-Chen 
1298c9274b6bSCho, Yu-Chen     return cc;
1299c9274b6bSCho, Yu-Chen }
1300c9274b6bSCho, Yu-Chen 
1301c9274b6bSCho, Yu-Chen /* compare logical long extended memcompare insn with padding */
HELPER(clcle)1302c9274b6bSCho, Yu-Chen uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
1303c9274b6bSCho, Yu-Chen                        uint32_t r3)
1304c9274b6bSCho, Yu-Chen {
1305c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1306c9274b6bSCho, Yu-Chen     uint64_t src1len = get_length(env, r1 + 1);
1307c9274b6bSCho, Yu-Chen     uint64_t src1 = get_address(env, r1);
1308c9274b6bSCho, Yu-Chen     uint64_t src3len = get_length(env, r3 + 1);
1309c9274b6bSCho, Yu-Chen     uint64_t src3 = get_address(env, r3);
1310c9274b6bSCho, Yu-Chen     uint8_t pad = a2;
1311c9274b6bSCho, Yu-Chen     uint32_t cc;
1312c9274b6bSCho, Yu-Chen 
1313c9274b6bSCho, Yu-Chen     cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, 0x2000, 1, ra);
1314c9274b6bSCho, Yu-Chen 
1315c9274b6bSCho, Yu-Chen     set_length(env, r1 + 1, src1len);
1316c9274b6bSCho, Yu-Chen     set_length(env, r3 + 1, src3len);
1317c9274b6bSCho, Yu-Chen     set_address(env, r1, src1);
1318c9274b6bSCho, Yu-Chen     set_address(env, r3, src3);
1319c9274b6bSCho, Yu-Chen 
1320c9274b6bSCho, Yu-Chen     return cc;
1321c9274b6bSCho, Yu-Chen }
1322c9274b6bSCho, Yu-Chen 
1323c9274b6bSCho, Yu-Chen /* compare logical long unicode memcompare insn with padding */
HELPER(clclu)1324c9274b6bSCho, Yu-Chen uint32_t HELPER(clclu)(CPUS390XState *env, uint32_t r1, uint64_t a2,
1325c9274b6bSCho, Yu-Chen                        uint32_t r3)
1326c9274b6bSCho, Yu-Chen {
1327c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1328c9274b6bSCho, Yu-Chen     uint64_t src1len = get_length(env, r1 + 1);
1329c9274b6bSCho, Yu-Chen     uint64_t src1 = get_address(env, r1);
1330c9274b6bSCho, Yu-Chen     uint64_t src3len = get_length(env, r3 + 1);
1331c9274b6bSCho, Yu-Chen     uint64_t src3 = get_address(env, r3);
1332c9274b6bSCho, Yu-Chen     uint16_t pad = a2;
1333c9274b6bSCho, Yu-Chen     uint32_t cc = 0;
1334c9274b6bSCho, Yu-Chen 
1335c9274b6bSCho, Yu-Chen     cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, 0x1000, 2, ra);
1336c9274b6bSCho, Yu-Chen 
1337c9274b6bSCho, Yu-Chen     set_length(env, r1 + 1, src1len);
1338c9274b6bSCho, Yu-Chen     set_length(env, r3 + 1, src3len);
1339c9274b6bSCho, Yu-Chen     set_address(env, r1, src1);
1340c9274b6bSCho, Yu-Chen     set_address(env, r3, src3);
1341c9274b6bSCho, Yu-Chen 
1342c9274b6bSCho, Yu-Chen     return cc;
1343c9274b6bSCho, Yu-Chen }
1344c9274b6bSCho, Yu-Chen 
1345c9274b6bSCho, Yu-Chen /* checksum */
HELPER(cksm)1346c9119224SRichard Henderson Int128 HELPER(cksm)(CPUS390XState *env, uint64_t r1,
1347c9274b6bSCho, Yu-Chen                     uint64_t src, uint64_t src_len)
1348c9274b6bSCho, Yu-Chen {
1349c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1350c9274b6bSCho, Yu-Chen     uint64_t max_len, len;
1351c9274b6bSCho, Yu-Chen     uint64_t cksm = (uint32_t)r1;
1352c9274b6bSCho, Yu-Chen 
1353c9274b6bSCho, Yu-Chen     /* Lest we fail to service interrupts in a timely manner, limit the
1354c9274b6bSCho, Yu-Chen        amount of work we're willing to do.  For now, let's cap at 8k.  */
1355c9274b6bSCho, Yu-Chen     max_len = (src_len > 0x2000 ? 0x2000 : src_len);
1356c9274b6bSCho, Yu-Chen 
1357c9274b6bSCho, Yu-Chen     /* Process full words as available.  */
1358c9274b6bSCho, Yu-Chen     for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
1359c9274b6bSCho, Yu-Chen         cksm += (uint32_t)cpu_ldl_data_ra(env, src, ra);
1360c9274b6bSCho, Yu-Chen     }
1361c9274b6bSCho, Yu-Chen 
1362c9274b6bSCho, Yu-Chen     switch (max_len - len) {
1363c9274b6bSCho, Yu-Chen     case 1:
1364c9274b6bSCho, Yu-Chen         cksm += cpu_ldub_data_ra(env, src, ra) << 24;
1365c9274b6bSCho, Yu-Chen         len += 1;
1366c9274b6bSCho, Yu-Chen         break;
1367c9274b6bSCho, Yu-Chen     case 2:
1368c9274b6bSCho, Yu-Chen         cksm += cpu_lduw_data_ra(env, src, ra) << 16;
1369c9274b6bSCho, Yu-Chen         len += 2;
1370c9274b6bSCho, Yu-Chen         break;
1371c9274b6bSCho, Yu-Chen     case 3:
1372c9274b6bSCho, Yu-Chen         cksm += cpu_lduw_data_ra(env, src, ra) << 16;
1373c9274b6bSCho, Yu-Chen         cksm += cpu_ldub_data_ra(env, src + 2, ra) << 8;
1374c9274b6bSCho, Yu-Chen         len += 3;
1375c9274b6bSCho, Yu-Chen         break;
1376c9274b6bSCho, Yu-Chen     }
1377c9274b6bSCho, Yu-Chen 
1378c9274b6bSCho, Yu-Chen     /* Fold the carry from the checksum.  Note that we can see carry-out
1379c9274b6bSCho, Yu-Chen        during folding more than once (but probably not more than twice).  */
1380c9274b6bSCho, Yu-Chen     while (cksm > 0xffffffffull) {
1381c9274b6bSCho, Yu-Chen         cksm = (uint32_t)cksm + (cksm >> 32);
1382c9274b6bSCho, Yu-Chen     }
1383c9274b6bSCho, Yu-Chen 
1384c9274b6bSCho, Yu-Chen     /* Indicate whether or not we've processed everything.  */
1385c9274b6bSCho, Yu-Chen     env->cc_op = (len == src_len ? 0 : 3);
1386c9274b6bSCho, Yu-Chen 
1387c9274b6bSCho, Yu-Chen     /* Return both cksm and processed length.  */
1388c9119224SRichard Henderson     return int128_make128(cksm, len);
1389c9274b6bSCho, Yu-Chen }
1390c9274b6bSCho, Yu-Chen 
HELPER(pack)1391c9274b6bSCho, Yu-Chen void HELPER(pack)(CPUS390XState *env, uint32_t len, uint64_t dest, uint64_t src)
1392c9274b6bSCho, Yu-Chen {
1393c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1394c9274b6bSCho, Yu-Chen     int len_dest = len >> 4;
1395c9274b6bSCho, Yu-Chen     int len_src = len & 0xf;
1396c9274b6bSCho, Yu-Chen     uint8_t b;
1397c9274b6bSCho, Yu-Chen 
1398c9274b6bSCho, Yu-Chen     dest += len_dest;
1399c9274b6bSCho, Yu-Chen     src += len_src;
1400c9274b6bSCho, Yu-Chen 
1401c9274b6bSCho, Yu-Chen     /* last byte is special, it only flips the nibbles */
1402c9274b6bSCho, Yu-Chen     b = cpu_ldub_data_ra(env, src, ra);
1403c9274b6bSCho, Yu-Chen     cpu_stb_data_ra(env, dest, (b << 4) | (b >> 4), ra);
1404c9274b6bSCho, Yu-Chen     src--;
1405c9274b6bSCho, Yu-Chen     len_src--;
1406c9274b6bSCho, Yu-Chen 
1407c9274b6bSCho, Yu-Chen     /* now pack every value */
1408c9274b6bSCho, Yu-Chen     while (len_dest > 0) {
1409c9274b6bSCho, Yu-Chen         b = 0;
1410c9274b6bSCho, Yu-Chen 
1411c9274b6bSCho, Yu-Chen         if (len_src >= 0) {
1412c9274b6bSCho, Yu-Chen             b = cpu_ldub_data_ra(env, src, ra) & 0x0f;
1413c9274b6bSCho, Yu-Chen             src--;
1414c9274b6bSCho, Yu-Chen             len_src--;
1415c9274b6bSCho, Yu-Chen         }
1416c9274b6bSCho, Yu-Chen         if (len_src >= 0) {
1417c9274b6bSCho, Yu-Chen             b |= cpu_ldub_data_ra(env, src, ra) << 4;
1418c9274b6bSCho, Yu-Chen             src--;
1419c9274b6bSCho, Yu-Chen             len_src--;
1420c9274b6bSCho, Yu-Chen         }
1421c9274b6bSCho, Yu-Chen 
1422c9274b6bSCho, Yu-Chen         len_dest--;
1423c9274b6bSCho, Yu-Chen         dest--;
1424c9274b6bSCho, Yu-Chen         cpu_stb_data_ra(env, dest, b, ra);
1425c9274b6bSCho, Yu-Chen     }
1426c9274b6bSCho, Yu-Chen }
1427c9274b6bSCho, Yu-Chen 
do_pkau(CPUS390XState * env,uint64_t dest,uint64_t src,uint32_t srclen,int ssize,uintptr_t ra)1428c9274b6bSCho, Yu-Chen static inline void do_pkau(CPUS390XState *env, uint64_t dest, uint64_t src,
1429c9274b6bSCho, Yu-Chen                            uint32_t srclen, int ssize, uintptr_t ra)
1430c9274b6bSCho, Yu-Chen {
1431c9274b6bSCho, Yu-Chen     int i;
1432c9274b6bSCho, Yu-Chen     /* The destination operand is always 16 bytes long.  */
1433c9274b6bSCho, Yu-Chen     const int destlen = 16;
1434c9274b6bSCho, Yu-Chen 
1435c9274b6bSCho, Yu-Chen     /* The operands are processed from right to left.  */
1436c9274b6bSCho, Yu-Chen     src += srclen - 1;
1437c9274b6bSCho, Yu-Chen     dest += destlen - 1;
1438c9274b6bSCho, Yu-Chen 
1439c9274b6bSCho, Yu-Chen     for (i = 0; i < destlen; i++) {
1440c9274b6bSCho, Yu-Chen         uint8_t b = 0;
1441c9274b6bSCho, Yu-Chen 
1442c9274b6bSCho, Yu-Chen         /* Start with a positive sign */
1443c9274b6bSCho, Yu-Chen         if (i == 0) {
1444c9274b6bSCho, Yu-Chen             b = 0xc;
1445c9274b6bSCho, Yu-Chen         } else if (srclen > ssize) {
1446c9274b6bSCho, Yu-Chen             b = cpu_ldub_data_ra(env, src, ra) & 0x0f;
1447c9274b6bSCho, Yu-Chen             src -= ssize;
1448c9274b6bSCho, Yu-Chen             srclen -= ssize;
1449c9274b6bSCho, Yu-Chen         }
1450c9274b6bSCho, Yu-Chen 
1451c9274b6bSCho, Yu-Chen         if (srclen > ssize) {
1452c9274b6bSCho, Yu-Chen             b |= cpu_ldub_data_ra(env, src, ra) << 4;
1453c9274b6bSCho, Yu-Chen             src -= ssize;
1454c9274b6bSCho, Yu-Chen             srclen -= ssize;
1455c9274b6bSCho, Yu-Chen         }
1456c9274b6bSCho, Yu-Chen 
1457c9274b6bSCho, Yu-Chen         cpu_stb_data_ra(env, dest, b, ra);
1458c9274b6bSCho, Yu-Chen         dest--;
1459c9274b6bSCho, Yu-Chen     }
1460c9274b6bSCho, Yu-Chen }
1461c9274b6bSCho, Yu-Chen 
1462c9274b6bSCho, Yu-Chen 
HELPER(pka)1463c9274b6bSCho, Yu-Chen void HELPER(pka)(CPUS390XState *env, uint64_t dest, uint64_t src,
1464c9274b6bSCho, Yu-Chen                  uint32_t srclen)
1465c9274b6bSCho, Yu-Chen {
1466c9274b6bSCho, Yu-Chen     do_pkau(env, dest, src, srclen, 1, GETPC());
1467c9274b6bSCho, Yu-Chen }
1468c9274b6bSCho, Yu-Chen 
HELPER(pku)1469c9274b6bSCho, Yu-Chen void HELPER(pku)(CPUS390XState *env, uint64_t dest, uint64_t src,
1470c9274b6bSCho, Yu-Chen                  uint32_t srclen)
1471c9274b6bSCho, Yu-Chen {
1472c9274b6bSCho, Yu-Chen     do_pkau(env, dest, src, srclen, 2, GETPC());
1473c9274b6bSCho, Yu-Chen }
1474c9274b6bSCho, Yu-Chen 
HELPER(unpk)1475c9274b6bSCho, Yu-Chen void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
1476c9274b6bSCho, Yu-Chen                   uint64_t src)
1477c9274b6bSCho, Yu-Chen {
1478c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1479c9274b6bSCho, Yu-Chen     int len_dest = len >> 4;
1480c9274b6bSCho, Yu-Chen     int len_src = len & 0xf;
1481c9274b6bSCho, Yu-Chen     uint8_t b;
1482c9274b6bSCho, Yu-Chen     int second_nibble = 0;
1483c9274b6bSCho, Yu-Chen 
1484c9274b6bSCho, Yu-Chen     dest += len_dest;
1485c9274b6bSCho, Yu-Chen     src += len_src;
1486c9274b6bSCho, Yu-Chen 
1487c9274b6bSCho, Yu-Chen     /* last byte is special, it only flips the nibbles */
1488c9274b6bSCho, Yu-Chen     b = cpu_ldub_data_ra(env, src, ra);
1489c9274b6bSCho, Yu-Chen     cpu_stb_data_ra(env, dest, (b << 4) | (b >> 4), ra);
1490c9274b6bSCho, Yu-Chen     src--;
1491c9274b6bSCho, Yu-Chen     len_src--;
1492c9274b6bSCho, Yu-Chen 
1493c9274b6bSCho, Yu-Chen     /* now pad every nibble with 0xf0 */
1494c9274b6bSCho, Yu-Chen 
1495c9274b6bSCho, Yu-Chen     while (len_dest > 0) {
1496c9274b6bSCho, Yu-Chen         uint8_t cur_byte = 0;
1497c9274b6bSCho, Yu-Chen 
1498c9274b6bSCho, Yu-Chen         if (len_src > 0) {
1499c9274b6bSCho, Yu-Chen             cur_byte = cpu_ldub_data_ra(env, src, ra);
1500c9274b6bSCho, Yu-Chen         }
1501c9274b6bSCho, Yu-Chen 
1502c9274b6bSCho, Yu-Chen         len_dest--;
1503c9274b6bSCho, Yu-Chen         dest--;
1504c9274b6bSCho, Yu-Chen 
1505c9274b6bSCho, Yu-Chen         /* only advance one nibble at a time */
1506c9274b6bSCho, Yu-Chen         if (second_nibble) {
1507c9274b6bSCho, Yu-Chen             cur_byte >>= 4;
1508c9274b6bSCho, Yu-Chen             len_src--;
1509c9274b6bSCho, Yu-Chen             src--;
1510c9274b6bSCho, Yu-Chen         }
1511c9274b6bSCho, Yu-Chen         second_nibble = !second_nibble;
1512c9274b6bSCho, Yu-Chen 
1513c9274b6bSCho, Yu-Chen         /* digit */
1514c9274b6bSCho, Yu-Chen         cur_byte = (cur_byte & 0xf);
1515c9274b6bSCho, Yu-Chen         /* zone bits */
1516c9274b6bSCho, Yu-Chen         cur_byte |= 0xf0;
1517c9274b6bSCho, Yu-Chen 
1518c9274b6bSCho, Yu-Chen         cpu_stb_data_ra(env, dest, cur_byte, ra);
1519c9274b6bSCho, Yu-Chen     }
1520c9274b6bSCho, Yu-Chen }
1521c9274b6bSCho, Yu-Chen 
do_unpkau(CPUS390XState * env,uint64_t dest,uint32_t destlen,int dsize,uint64_t src,uintptr_t ra)1522c9274b6bSCho, Yu-Chen static inline uint32_t do_unpkau(CPUS390XState *env, uint64_t dest,
1523c9274b6bSCho, Yu-Chen                                  uint32_t destlen, int dsize, uint64_t src,
1524c9274b6bSCho, Yu-Chen                                  uintptr_t ra)
1525c9274b6bSCho, Yu-Chen {
1526c9274b6bSCho, Yu-Chen     int i;
1527c9274b6bSCho, Yu-Chen     uint32_t cc;
1528c9274b6bSCho, Yu-Chen     uint8_t b;
1529c9274b6bSCho, Yu-Chen     /* The source operand is always 16 bytes long.  */
1530c9274b6bSCho, Yu-Chen     const int srclen = 16;
1531c9274b6bSCho, Yu-Chen 
1532c9274b6bSCho, Yu-Chen     /* The operands are processed from right to left.  */
1533c9274b6bSCho, Yu-Chen     src += srclen - 1;
1534c9274b6bSCho, Yu-Chen     dest += destlen - dsize;
1535c9274b6bSCho, Yu-Chen 
1536c9274b6bSCho, Yu-Chen     /* Check for the sign.  */
1537c9274b6bSCho, Yu-Chen     b = cpu_ldub_data_ra(env, src, ra);
1538c9274b6bSCho, Yu-Chen     src--;
1539c9274b6bSCho, Yu-Chen     switch (b & 0xf) {
1540c9274b6bSCho, Yu-Chen     case 0xa:
1541c9274b6bSCho, Yu-Chen     case 0xc:
1542c9274b6bSCho, Yu-Chen     case 0xe ... 0xf:
1543c9274b6bSCho, Yu-Chen         cc = 0;  /* plus */
1544c9274b6bSCho, Yu-Chen         break;
1545c9274b6bSCho, Yu-Chen     case 0xb:
1546c9274b6bSCho, Yu-Chen     case 0xd:
1547c9274b6bSCho, Yu-Chen         cc = 1;  /* minus */
1548c9274b6bSCho, Yu-Chen         break;
1549c9274b6bSCho, Yu-Chen     default:
1550c9274b6bSCho, Yu-Chen     case 0x0 ... 0x9:
1551c9274b6bSCho, Yu-Chen         cc = 3;  /* invalid */
1552c9274b6bSCho, Yu-Chen         break;
1553c9274b6bSCho, Yu-Chen     }
1554c9274b6bSCho, Yu-Chen 
1555c9274b6bSCho, Yu-Chen     /* Now pad every nibble with 0x30, advancing one nibble at a time. */
1556c9274b6bSCho, Yu-Chen     for (i = 0; i < destlen; i += dsize) {
1557c9274b6bSCho, Yu-Chen         if (i == (31 * dsize)) {
1558c9274b6bSCho, Yu-Chen             /* If length is 32/64 bytes, the leftmost byte is 0. */
1559c9274b6bSCho, Yu-Chen             b = 0;
1560c9274b6bSCho, Yu-Chen         } else if (i % (2 * dsize)) {
1561c9274b6bSCho, Yu-Chen             b = cpu_ldub_data_ra(env, src, ra);
1562c9274b6bSCho, Yu-Chen             src--;
1563c9274b6bSCho, Yu-Chen         } else {
1564c9274b6bSCho, Yu-Chen             b >>= 4;
1565c9274b6bSCho, Yu-Chen         }
1566c9274b6bSCho, Yu-Chen         cpu_stsize_data_ra(env, dest, 0x30 + (b & 0xf), dsize, ra);
1567c9274b6bSCho, Yu-Chen         dest -= dsize;
1568c9274b6bSCho, Yu-Chen     }
1569c9274b6bSCho, Yu-Chen 
1570c9274b6bSCho, Yu-Chen     return cc;
1571c9274b6bSCho, Yu-Chen }
1572c9274b6bSCho, Yu-Chen 
HELPER(unpka)1573c9274b6bSCho, Yu-Chen uint32_t HELPER(unpka)(CPUS390XState *env, uint64_t dest, uint32_t destlen,
1574c9274b6bSCho, Yu-Chen                        uint64_t src)
1575c9274b6bSCho, Yu-Chen {
1576c9274b6bSCho, Yu-Chen     return do_unpkau(env, dest, destlen, 1, src, GETPC());
1577c9274b6bSCho, Yu-Chen }
1578c9274b6bSCho, Yu-Chen 
HELPER(unpku)1579c9274b6bSCho, Yu-Chen uint32_t HELPER(unpku)(CPUS390XState *env, uint64_t dest, uint32_t destlen,
1580c9274b6bSCho, Yu-Chen                        uint64_t src)
1581c9274b6bSCho, Yu-Chen {
1582c9274b6bSCho, Yu-Chen     return do_unpkau(env, dest, destlen, 2, src, GETPC());
1583c9274b6bSCho, Yu-Chen }
1584c9274b6bSCho, Yu-Chen 
HELPER(tp)1585c9274b6bSCho, Yu-Chen uint32_t HELPER(tp)(CPUS390XState *env, uint64_t dest, uint32_t destlen)
1586c9274b6bSCho, Yu-Chen {
1587c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1588c9274b6bSCho, Yu-Chen     uint32_t cc = 0;
1589c9274b6bSCho, Yu-Chen     int i;
1590c9274b6bSCho, Yu-Chen 
1591c9274b6bSCho, Yu-Chen     for (i = 0; i < destlen; i++) {
1592c9274b6bSCho, Yu-Chen         uint8_t b = cpu_ldub_data_ra(env, dest + i, ra);
1593c9274b6bSCho, Yu-Chen         /* digit */
1594c9274b6bSCho, Yu-Chen         cc |= (b & 0xf0) > 0x90 ? 2 : 0;
1595c9274b6bSCho, Yu-Chen 
1596c9274b6bSCho, Yu-Chen         if (i == (destlen - 1)) {
1597c9274b6bSCho, Yu-Chen             /* sign */
1598c9274b6bSCho, Yu-Chen             cc |= (b & 0xf) < 0xa ? 1 : 0;
1599c9274b6bSCho, Yu-Chen         } else {
1600c9274b6bSCho, Yu-Chen             /* digit */
1601c9274b6bSCho, Yu-Chen             cc |= (b & 0xf) > 0x9 ? 2 : 0;
1602c9274b6bSCho, Yu-Chen         }
1603c9274b6bSCho, Yu-Chen     }
1604c9274b6bSCho, Yu-Chen 
1605c9274b6bSCho, Yu-Chen     return cc;
1606c9274b6bSCho, Yu-Chen }
1607c9274b6bSCho, Yu-Chen 
do_helper_tr(CPUS390XState * env,uint32_t len,uint64_t array,uint64_t trans,uintptr_t ra)1608c9274b6bSCho, Yu-Chen static uint32_t do_helper_tr(CPUS390XState *env, uint32_t len, uint64_t array,
1609c9274b6bSCho, Yu-Chen                              uint64_t trans, uintptr_t ra)
1610c9274b6bSCho, Yu-Chen {
1611c9274b6bSCho, Yu-Chen     uint32_t i;
1612c9274b6bSCho, Yu-Chen 
1613c9274b6bSCho, Yu-Chen     for (i = 0; i <= len; i++) {
1614c9274b6bSCho, Yu-Chen         uint8_t byte = cpu_ldub_data_ra(env, array + i, ra);
1615c9274b6bSCho, Yu-Chen         uint8_t new_byte = cpu_ldub_data_ra(env, trans + byte, ra);
1616c9274b6bSCho, Yu-Chen         cpu_stb_data_ra(env, array + i, new_byte, ra);
1617c9274b6bSCho, Yu-Chen     }
1618c9274b6bSCho, Yu-Chen 
1619c9274b6bSCho, Yu-Chen     return env->cc_op;
1620c9274b6bSCho, Yu-Chen }
1621c9274b6bSCho, Yu-Chen 
HELPER(tr)1622c9274b6bSCho, Yu-Chen void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
1623c9274b6bSCho, Yu-Chen                 uint64_t trans)
1624c9274b6bSCho, Yu-Chen {
1625c9274b6bSCho, Yu-Chen     do_helper_tr(env, len, array, trans, GETPC());
1626c9274b6bSCho, Yu-Chen }
1627c9274b6bSCho, Yu-Chen 
HELPER(tre)1628ef45f5b9SRichard Henderson Int128 HELPER(tre)(CPUS390XState *env, uint64_t array,
1629c9274b6bSCho, Yu-Chen                    uint64_t len, uint64_t trans)
1630c9274b6bSCho, Yu-Chen {
1631c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1632c9274b6bSCho, Yu-Chen     uint8_t end = env->regs[0] & 0xff;
1633c9274b6bSCho, Yu-Chen     uint64_t l = len;
1634c9274b6bSCho, Yu-Chen     uint64_t i;
1635c9274b6bSCho, Yu-Chen     uint32_t cc = 0;
1636c9274b6bSCho, Yu-Chen 
1637c9274b6bSCho, Yu-Chen     if (!(env->psw.mask & PSW_MASK_64)) {
1638c9274b6bSCho, Yu-Chen         array &= 0x7fffffff;
1639c9274b6bSCho, Yu-Chen         l = (uint32_t)l;
1640c9274b6bSCho, Yu-Chen     }
1641c9274b6bSCho, Yu-Chen 
1642c9274b6bSCho, Yu-Chen     /* Lest we fail to service interrupts in a timely manner, limit the
1643c9274b6bSCho, Yu-Chen        amount of work we're willing to do.  For now, let's cap at 8k.  */
1644c9274b6bSCho, Yu-Chen     if (l > 0x2000) {
1645c9274b6bSCho, Yu-Chen         l = 0x2000;
1646c9274b6bSCho, Yu-Chen         cc = 3;
1647c9274b6bSCho, Yu-Chen     }
1648c9274b6bSCho, Yu-Chen 
1649c9274b6bSCho, Yu-Chen     for (i = 0; i < l; i++) {
1650c9274b6bSCho, Yu-Chen         uint8_t byte, new_byte;
1651c9274b6bSCho, Yu-Chen 
1652c9274b6bSCho, Yu-Chen         byte = cpu_ldub_data_ra(env, array + i, ra);
1653c9274b6bSCho, Yu-Chen 
1654c9274b6bSCho, Yu-Chen         if (byte == end) {
1655c9274b6bSCho, Yu-Chen             cc = 1;
1656c9274b6bSCho, Yu-Chen             break;
1657c9274b6bSCho, Yu-Chen         }
1658c9274b6bSCho, Yu-Chen 
1659c9274b6bSCho, Yu-Chen         new_byte = cpu_ldub_data_ra(env, trans + byte, ra);
1660c9274b6bSCho, Yu-Chen         cpu_stb_data_ra(env, array + i, new_byte, ra);
1661c9274b6bSCho, Yu-Chen     }
1662c9274b6bSCho, Yu-Chen 
1663c9274b6bSCho, Yu-Chen     env->cc_op = cc;
1664ef45f5b9SRichard Henderson     return int128_make128(len - i, array + i);
1665c9274b6bSCho, Yu-Chen }
1666c9274b6bSCho, Yu-Chen 
do_helper_trt(CPUS390XState * env,int len,uint64_t array,uint64_t trans,int inc,uintptr_t ra)1667c9274b6bSCho, Yu-Chen static inline uint32_t do_helper_trt(CPUS390XState *env, int len,
1668c9274b6bSCho, Yu-Chen                                      uint64_t array, uint64_t trans,
1669c9274b6bSCho, Yu-Chen                                      int inc, uintptr_t ra)
1670c9274b6bSCho, Yu-Chen {
1671c9274b6bSCho, Yu-Chen     int i;
1672c9274b6bSCho, Yu-Chen 
1673c9274b6bSCho, Yu-Chen     for (i = 0; i <= len; i++) {
1674c9274b6bSCho, Yu-Chen         uint8_t byte = cpu_ldub_data_ra(env, array + i * inc, ra);
1675c9274b6bSCho, Yu-Chen         uint8_t sbyte = cpu_ldub_data_ra(env, trans + byte, ra);
1676c9274b6bSCho, Yu-Chen 
1677c9274b6bSCho, Yu-Chen         if (sbyte != 0) {
1678c9274b6bSCho, Yu-Chen             set_address(env, 1, array + i * inc);
1679c9274b6bSCho, Yu-Chen             env->regs[2] = deposit64(env->regs[2], 0, 8, sbyte);
1680c9274b6bSCho, Yu-Chen             return (i == len) ? 2 : 1;
1681c9274b6bSCho, Yu-Chen         }
1682c9274b6bSCho, Yu-Chen     }
1683c9274b6bSCho, Yu-Chen 
1684c9274b6bSCho, Yu-Chen     return 0;
1685c9274b6bSCho, Yu-Chen }
1686c9274b6bSCho, Yu-Chen 
do_helper_trt_fwd(CPUS390XState * env,uint32_t len,uint64_t array,uint64_t trans,uintptr_t ra)1687c9274b6bSCho, Yu-Chen static uint32_t do_helper_trt_fwd(CPUS390XState *env, uint32_t len,
1688c9274b6bSCho, Yu-Chen                                   uint64_t array, uint64_t trans,
1689c9274b6bSCho, Yu-Chen                                   uintptr_t ra)
1690c9274b6bSCho, Yu-Chen {
1691c9274b6bSCho, Yu-Chen     return do_helper_trt(env, len, array, trans, 1, ra);
1692c9274b6bSCho, Yu-Chen }
1693c9274b6bSCho, Yu-Chen 
HELPER(trt)1694c9274b6bSCho, Yu-Chen uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
1695c9274b6bSCho, Yu-Chen                      uint64_t trans)
1696c9274b6bSCho, Yu-Chen {
1697c9274b6bSCho, Yu-Chen     return do_helper_trt(env, len, array, trans, 1, GETPC());
1698c9274b6bSCho, Yu-Chen }
1699c9274b6bSCho, Yu-Chen 
do_helper_trt_bkwd(CPUS390XState * env,uint32_t len,uint64_t array,uint64_t trans,uintptr_t ra)1700c9274b6bSCho, Yu-Chen static uint32_t do_helper_trt_bkwd(CPUS390XState *env, uint32_t len,
1701c9274b6bSCho, Yu-Chen                                    uint64_t array, uint64_t trans,
1702c9274b6bSCho, Yu-Chen                                    uintptr_t ra)
1703c9274b6bSCho, Yu-Chen {
1704c9274b6bSCho, Yu-Chen     return do_helper_trt(env, len, array, trans, -1, ra);
1705c9274b6bSCho, Yu-Chen }
1706c9274b6bSCho, Yu-Chen 
HELPER(trtr)1707c9274b6bSCho, Yu-Chen uint32_t HELPER(trtr)(CPUS390XState *env, uint32_t len, uint64_t array,
1708c9274b6bSCho, Yu-Chen                       uint64_t trans)
1709c9274b6bSCho, Yu-Chen {
1710c9274b6bSCho, Yu-Chen     return do_helper_trt(env, len, array, trans, -1, GETPC());
1711c9274b6bSCho, Yu-Chen }
1712c9274b6bSCho, Yu-Chen 
1713c9274b6bSCho, Yu-Chen /* Translate one/two to one/two */
HELPER(trXX)1714c9274b6bSCho, Yu-Chen uint32_t HELPER(trXX)(CPUS390XState *env, uint32_t r1, uint32_t r2,
1715c9274b6bSCho, Yu-Chen                       uint32_t tst, uint32_t sizes)
1716c9274b6bSCho, Yu-Chen {
1717c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1718c9274b6bSCho, Yu-Chen     int dsize = (sizes & 1) ? 1 : 2;
1719c9274b6bSCho, Yu-Chen     int ssize = (sizes & 2) ? 1 : 2;
1720c9274b6bSCho, Yu-Chen     uint64_t tbl = get_address(env, 1);
1721c9274b6bSCho, Yu-Chen     uint64_t dst = get_address(env, r1);
1722c9274b6bSCho, Yu-Chen     uint64_t len = get_length(env, r1 + 1);
1723c9274b6bSCho, Yu-Chen     uint64_t src = get_address(env, r2);
1724c9274b6bSCho, Yu-Chen     uint32_t cc = 3;
1725c9274b6bSCho, Yu-Chen     int i;
1726c9274b6bSCho, Yu-Chen 
1727c9274b6bSCho, Yu-Chen     /* The lower address bits of TBL are ignored.  For TROO, TROT, it's
1728c9274b6bSCho, Yu-Chen        the low 3 bits (double-word aligned).  For TRTO, TRTT, it's either
1729c9274b6bSCho, Yu-Chen        the low 12 bits (4K, without ETF2-ENH) or 3 bits (with ETF2-ENH).  */
1730c9274b6bSCho, Yu-Chen     if (ssize == 2 && !s390_has_feat(S390_FEAT_ETF2_ENH)) {
1731c9274b6bSCho, Yu-Chen         tbl &= -4096;
1732c9274b6bSCho, Yu-Chen     } else {
1733c9274b6bSCho, Yu-Chen         tbl &= -8;
1734c9274b6bSCho, Yu-Chen     }
1735c9274b6bSCho, Yu-Chen 
1736c9274b6bSCho, Yu-Chen     check_alignment(env, len, ssize, ra);
1737c9274b6bSCho, Yu-Chen 
1738c9274b6bSCho, Yu-Chen     /* Lest we fail to service interrupts in a timely manner, */
1739c9274b6bSCho, Yu-Chen     /* limit the amount of work we're willing to do.   */
1740c9274b6bSCho, Yu-Chen     for (i = 0; i < 0x2000; i++) {
1741c9274b6bSCho, Yu-Chen         uint16_t sval = cpu_ldusize_data_ra(env, src, ssize, ra);
1742c9274b6bSCho, Yu-Chen         uint64_t tble = tbl + (sval * dsize);
1743c9274b6bSCho, Yu-Chen         uint16_t dval = cpu_ldusize_data_ra(env, tble, dsize, ra);
1744c9274b6bSCho, Yu-Chen         if (dval == tst) {
1745c9274b6bSCho, Yu-Chen             cc = 1;
1746c9274b6bSCho, Yu-Chen             break;
1747c9274b6bSCho, Yu-Chen         }
1748c9274b6bSCho, Yu-Chen         cpu_stsize_data_ra(env, dst, dval, dsize, ra);
1749c9274b6bSCho, Yu-Chen 
1750c9274b6bSCho, Yu-Chen         len -= ssize;
1751c9274b6bSCho, Yu-Chen         src += ssize;
1752c9274b6bSCho, Yu-Chen         dst += dsize;
1753c9274b6bSCho, Yu-Chen 
1754c9274b6bSCho, Yu-Chen         if (len == 0) {
1755c9274b6bSCho, Yu-Chen             cc = 0;
1756c9274b6bSCho, Yu-Chen             break;
1757c9274b6bSCho, Yu-Chen         }
1758c9274b6bSCho, Yu-Chen     }
1759c9274b6bSCho, Yu-Chen 
1760c9274b6bSCho, Yu-Chen     set_address(env, r1, dst);
1761c9274b6bSCho, Yu-Chen     set_length(env, r1 + 1, len);
1762c9274b6bSCho, Yu-Chen     set_address(env, r2, src);
1763c9274b6bSCho, Yu-Chen 
1764c9274b6bSCho, Yu-Chen     return cc;
1765c9274b6bSCho, Yu-Chen }
1766c9274b6bSCho, Yu-Chen 
do_csst(CPUS390XState * env,uint32_t r3,uint64_t a1,uint64_t a2,bool parallel)1767c9274b6bSCho, Yu-Chen static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1,
1768c9274b6bSCho, Yu-Chen                         uint64_t a2, bool parallel)
1769c9274b6bSCho, Yu-Chen {
177090b7022eSRichard Henderson     uint32_t mem_idx = s390x_env_mmu_index(env, false);
1771ddc0ab5aSRichard Henderson     MemOpIdx oi16 = make_memop_idx(MO_TE | MO_128, mem_idx);
1772ddc0ab5aSRichard Henderson     MemOpIdx oi8 = make_memop_idx(MO_TE | MO_64, mem_idx);
1773ddc0ab5aSRichard Henderson     MemOpIdx oi4 = make_memop_idx(MO_TE | MO_32, mem_idx);
1774ddc0ab5aSRichard Henderson     MemOpIdx oi2 = make_memop_idx(MO_TE | MO_16, mem_idx);
1775ddc0ab5aSRichard Henderson     MemOpIdx oi1 = make_memop_idx(MO_8, mem_idx);
1776c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1777c9274b6bSCho, Yu-Chen     uint32_t fc = extract32(env->regs[0], 0, 8);
1778c9274b6bSCho, Yu-Chen     uint32_t sc = extract32(env->regs[0], 8, 8);
1779c9274b6bSCho, Yu-Chen     uint64_t pl = get_address(env, 1) & -16;
1780c9274b6bSCho, Yu-Chen     uint64_t svh, svl;
1781c9274b6bSCho, Yu-Chen     uint32_t cc;
1782c9274b6bSCho, Yu-Chen 
1783c9274b6bSCho, Yu-Chen     /* Sanity check the function code and storage characteristic.  */
1784c9274b6bSCho, Yu-Chen     if (fc > 1 || sc > 3) {
1785c9274b6bSCho, Yu-Chen         if (!s390_has_feat(S390_FEAT_COMPARE_AND_SWAP_AND_STORE_2)) {
1786c9274b6bSCho, Yu-Chen             goto spec_exception;
1787c9274b6bSCho, Yu-Chen         }
1788c9274b6bSCho, Yu-Chen         if (fc > 2 || sc > 4 || (fc == 2 && (r3 & 1))) {
1789c9274b6bSCho, Yu-Chen             goto spec_exception;
1790c9274b6bSCho, Yu-Chen         }
1791c9274b6bSCho, Yu-Chen     }
1792c9274b6bSCho, Yu-Chen 
1793c9274b6bSCho, Yu-Chen     /* Sanity check the alignments.  */
1794c9274b6bSCho, Yu-Chen     if (extract32(a1, 0, fc + 2) || extract32(a2, 0, sc)) {
1795c9274b6bSCho, Yu-Chen         goto spec_exception;
1796c9274b6bSCho, Yu-Chen     }
1797c9274b6bSCho, Yu-Chen 
1798c9274b6bSCho, Yu-Chen     /* Sanity check writability of the store address.  */
1799c9274b6bSCho, Yu-Chen     probe_write(env, a2, 1 << sc, mem_idx, ra);
1800c9274b6bSCho, Yu-Chen 
1801c9274b6bSCho, Yu-Chen     /*
1802c9274b6bSCho, Yu-Chen      * Note that the compare-and-swap is atomic, and the store is atomic,
1803c9274b6bSCho, Yu-Chen      * but the complete operation is not.  Therefore we do not need to
1804c9274b6bSCho, Yu-Chen      * assert serial context in order to implement this.  That said,
1805c9274b6bSCho, Yu-Chen      * restart early if we can't support either operation that is supposed
1806c9274b6bSCho, Yu-Chen      * to be atomic.
1807c9274b6bSCho, Yu-Chen      */
1808c9274b6bSCho, Yu-Chen     if (parallel) {
1809c9274b6bSCho, Yu-Chen         uint32_t max = 2;
1810c9274b6bSCho, Yu-Chen #ifdef CONFIG_ATOMIC64
1811c9274b6bSCho, Yu-Chen         max = 3;
1812c9274b6bSCho, Yu-Chen #endif
1813c9274b6bSCho, Yu-Chen         if ((HAVE_CMPXCHG128 ? 0 : fc + 2 > max) ||
181421c38f31SRichard Henderson             (HAVE_ATOMIC128_RW ? 0 : sc > max)) {
1815c9274b6bSCho, Yu-Chen             cpu_loop_exit_atomic(env_cpu(env), ra);
1816c9274b6bSCho, Yu-Chen         }
1817c9274b6bSCho, Yu-Chen     }
1818c9274b6bSCho, Yu-Chen 
1819ddc0ab5aSRichard Henderson     /*
1820ddc0ab5aSRichard Henderson      * All loads happen before all stores.  For simplicity, load the entire
1821ddc0ab5aSRichard Henderson      * store value area from the parameter list.
1822ddc0ab5aSRichard Henderson      */
1823ddc0ab5aSRichard Henderson     svh = cpu_ldq_mmu(env, pl + 16, oi8, ra);
1824ddc0ab5aSRichard Henderson     svl = cpu_ldq_mmu(env, pl + 24, oi8, ra);
1825c9274b6bSCho, Yu-Chen 
1826c9274b6bSCho, Yu-Chen     switch (fc) {
1827c9274b6bSCho, Yu-Chen     case 0:
1828c9274b6bSCho, Yu-Chen         {
1829ddc0ab5aSRichard Henderson             uint32_t nv = cpu_ldl_mmu(env, pl, oi4, ra);
1830c9274b6bSCho, Yu-Chen             uint32_t cv = env->regs[r3];
1831c9274b6bSCho, Yu-Chen             uint32_t ov;
1832c9274b6bSCho, Yu-Chen 
1833c9274b6bSCho, Yu-Chen             if (parallel) {
183447ae3e40SRichard Henderson                 ov = cpu_atomic_cmpxchgl_be_mmu(env, a1, cv, nv, oi4, ra);
1835c9274b6bSCho, Yu-Chen             } else {
1836ddc0ab5aSRichard Henderson                 ov = cpu_ldl_mmu(env, a1, oi4, ra);
1837ddc0ab5aSRichard Henderson                 cpu_stl_mmu(env, a1, (ov == cv ? nv : ov), oi4, ra);
1838c9274b6bSCho, Yu-Chen             }
1839c9274b6bSCho, Yu-Chen             cc = (ov != cv);
1840c9274b6bSCho, Yu-Chen             env->regs[r3] = deposit64(env->regs[r3], 32, 32, ov);
1841c9274b6bSCho, Yu-Chen         }
1842c9274b6bSCho, Yu-Chen         break;
1843c9274b6bSCho, Yu-Chen 
1844c9274b6bSCho, Yu-Chen     case 1:
1845c9274b6bSCho, Yu-Chen         {
1846ddc0ab5aSRichard Henderson             uint64_t nv = cpu_ldq_mmu(env, pl, oi8, ra);
1847c9274b6bSCho, Yu-Chen             uint64_t cv = env->regs[r3];
1848c9274b6bSCho, Yu-Chen             uint64_t ov;
1849c9274b6bSCho, Yu-Chen 
1850c9274b6bSCho, Yu-Chen             if (parallel) {
1851c9274b6bSCho, Yu-Chen #ifdef CONFIG_ATOMIC64
1852ddc0ab5aSRichard Henderson                 ov = cpu_atomic_cmpxchgq_be_mmu(env, a1, cv, nv, oi8, ra);
1853c9274b6bSCho, Yu-Chen #else
1854c9274b6bSCho, Yu-Chen                 /* Note that we asserted !parallel above.  */
1855c9274b6bSCho, Yu-Chen                 g_assert_not_reached();
1856c9274b6bSCho, Yu-Chen #endif
1857c9274b6bSCho, Yu-Chen             } else {
1858ddc0ab5aSRichard Henderson                 ov = cpu_ldq_mmu(env, a1, oi8, ra);
1859ddc0ab5aSRichard Henderson                 cpu_stq_mmu(env, a1, (ov == cv ? nv : ov), oi8, ra);
1860c9274b6bSCho, Yu-Chen             }
1861c9274b6bSCho, Yu-Chen             cc = (ov != cv);
1862c9274b6bSCho, Yu-Chen             env->regs[r3] = ov;
1863c9274b6bSCho, Yu-Chen         }
1864c9274b6bSCho, Yu-Chen         break;
1865c9274b6bSCho, Yu-Chen 
1866c9274b6bSCho, Yu-Chen     case 2:
1867c9274b6bSCho, Yu-Chen         {
1868ddc0ab5aSRichard Henderson             Int128 nv = cpu_ld16_mmu(env, pl, oi16, ra);
1869c9274b6bSCho, Yu-Chen             Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]);
1870c9274b6bSCho, Yu-Chen             Int128 ov;
1871c9274b6bSCho, Yu-Chen 
1872c9274b6bSCho, Yu-Chen             if (!parallel) {
1873ddc0ab5aSRichard Henderson                 ov = cpu_ld16_mmu(env, a1, oi16, ra);
1874c9274b6bSCho, Yu-Chen                 cc = !int128_eq(ov, cv);
1875c9274b6bSCho, Yu-Chen                 if (cc) {
1876c9274b6bSCho, Yu-Chen                     nv = ov;
1877c9274b6bSCho, Yu-Chen                 }
1878ddc0ab5aSRichard Henderson                 cpu_st16_mmu(env, a1, nv, oi16, ra);
1879c9274b6bSCho, Yu-Chen             } else if (HAVE_CMPXCHG128) {
1880ddc0ab5aSRichard Henderson                 ov = cpu_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi16, ra);
1881c9274b6bSCho, Yu-Chen                 cc = !int128_eq(ov, cv);
1882c9274b6bSCho, Yu-Chen             } else {
1883c9274b6bSCho, Yu-Chen                 /* Note that we asserted !parallel above.  */
1884c9274b6bSCho, Yu-Chen                 g_assert_not_reached();
1885c9274b6bSCho, Yu-Chen             }
1886c9274b6bSCho, Yu-Chen 
1887c9274b6bSCho, Yu-Chen             env->regs[r3 + 0] = int128_gethi(ov);
1888c9274b6bSCho, Yu-Chen             env->regs[r3 + 1] = int128_getlo(ov);
1889c9274b6bSCho, Yu-Chen         }
1890c9274b6bSCho, Yu-Chen         break;
1891c9274b6bSCho, Yu-Chen 
1892c9274b6bSCho, Yu-Chen     default:
1893c9274b6bSCho, Yu-Chen         g_assert_not_reached();
1894c9274b6bSCho, Yu-Chen     }
1895c9274b6bSCho, Yu-Chen 
1896c9274b6bSCho, Yu-Chen     /* Store only if the comparison succeeded.  Note that above we use a pair
1897c9274b6bSCho, Yu-Chen        of 64-bit big-endian loads, so for sc < 3 we must extract the value
1898c9274b6bSCho, Yu-Chen        from the most-significant bits of svh.  */
1899c9274b6bSCho, Yu-Chen     if (cc == 0) {
1900c9274b6bSCho, Yu-Chen         switch (sc) {
1901c9274b6bSCho, Yu-Chen         case 0:
1902ddc0ab5aSRichard Henderson             cpu_stb_mmu(env, a2, svh >> 56, oi1, ra);
1903c9274b6bSCho, Yu-Chen             break;
1904c9274b6bSCho, Yu-Chen         case 1:
1905ddc0ab5aSRichard Henderson             cpu_stw_mmu(env, a2, svh >> 48, oi2, ra);
1906c9274b6bSCho, Yu-Chen             break;
1907c9274b6bSCho, Yu-Chen         case 2:
1908ddc0ab5aSRichard Henderson             cpu_stl_mmu(env, a2, svh >> 32, oi4, ra);
1909c9274b6bSCho, Yu-Chen             break;
1910c9274b6bSCho, Yu-Chen         case 3:
1911ddc0ab5aSRichard Henderson             cpu_stq_mmu(env, a2, svh, oi8, ra);
1912c9274b6bSCho, Yu-Chen             break;
1913c9274b6bSCho, Yu-Chen         case 4:
1914ddc0ab5aSRichard Henderson             cpu_st16_mmu(env, a2, int128_make128(svl, svh), oi16, ra);
1915c9274b6bSCho, Yu-Chen             break;
1916c9274b6bSCho, Yu-Chen         default:
1917c9274b6bSCho, Yu-Chen             g_assert_not_reached();
1918c9274b6bSCho, Yu-Chen         }
1919c9274b6bSCho, Yu-Chen     }
1920c9274b6bSCho, Yu-Chen 
1921c9274b6bSCho, Yu-Chen     return cc;
1922c9274b6bSCho, Yu-Chen 
1923c9274b6bSCho, Yu-Chen  spec_exception:
1924c9274b6bSCho, Yu-Chen     tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
1925c9274b6bSCho, Yu-Chen }
1926c9274b6bSCho, Yu-Chen 
HELPER(csst)1927c9274b6bSCho, Yu-Chen uint32_t HELPER(csst)(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2)
1928c9274b6bSCho, Yu-Chen {
1929c9274b6bSCho, Yu-Chen     return do_csst(env, r3, a1, a2, false);
1930c9274b6bSCho, Yu-Chen }
1931c9274b6bSCho, Yu-Chen 
HELPER(csst_parallel)1932c9274b6bSCho, Yu-Chen uint32_t HELPER(csst_parallel)(CPUS390XState *env, uint32_t r3, uint64_t a1,
1933c9274b6bSCho, Yu-Chen                                uint64_t a2)
1934c9274b6bSCho, Yu-Chen {
1935c9274b6bSCho, Yu-Chen     return do_csst(env, r3, a1, a2, true);
1936c9274b6bSCho, Yu-Chen }
1937c9274b6bSCho, Yu-Chen 
1938c9274b6bSCho, Yu-Chen #if !defined(CONFIG_USER_ONLY)
HELPER(lctlg)1939c9274b6bSCho, Yu-Chen void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
1940c9274b6bSCho, Yu-Chen {
1941c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1942c9274b6bSCho, Yu-Chen     bool PERchanged = false;
1943c9274b6bSCho, Yu-Chen     uint64_t src = a2;
1944c9274b6bSCho, Yu-Chen     uint32_t i;
1945c9274b6bSCho, Yu-Chen 
1946c9274b6bSCho, Yu-Chen     if (src & 0x7) {
1947c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
1948c9274b6bSCho, Yu-Chen     }
1949c9274b6bSCho, Yu-Chen 
1950c9274b6bSCho, Yu-Chen     for (i = r1;; i = (i + 1) % 16) {
1951c9274b6bSCho, Yu-Chen         uint64_t val = cpu_ldq_data_ra(env, src, ra);
1952c9274b6bSCho, Yu-Chen         if (env->cregs[i] != val && i >= 9 && i <= 11) {
1953c9274b6bSCho, Yu-Chen             PERchanged = true;
1954c9274b6bSCho, Yu-Chen         }
1955c9274b6bSCho, Yu-Chen         env->cregs[i] = val;
1956c9274b6bSCho, Yu-Chen         HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
1957c9274b6bSCho, Yu-Chen                    i, src, val);
1958c9274b6bSCho, Yu-Chen         src += sizeof(uint64_t);
1959c9274b6bSCho, Yu-Chen 
1960c9274b6bSCho, Yu-Chen         if (i == r3) {
1961c9274b6bSCho, Yu-Chen             break;
1962c9274b6bSCho, Yu-Chen         }
1963c9274b6bSCho, Yu-Chen     }
1964c9274b6bSCho, Yu-Chen 
1965c9274b6bSCho, Yu-Chen     if (PERchanged && env->psw.mask & PSW_MASK_PER) {
1966c9274b6bSCho, Yu-Chen         s390_cpu_recompute_watchpoints(env_cpu(env));
1967c9274b6bSCho, Yu-Chen     }
1968c9274b6bSCho, Yu-Chen 
1969c9274b6bSCho, Yu-Chen     tlb_flush(env_cpu(env));
1970c9274b6bSCho, Yu-Chen }
1971c9274b6bSCho, Yu-Chen 
HELPER(lctl)1972c9274b6bSCho, Yu-Chen void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
1973c9274b6bSCho, Yu-Chen {
1974c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
1975c9274b6bSCho, Yu-Chen     bool PERchanged = false;
1976c9274b6bSCho, Yu-Chen     uint64_t src = a2;
1977c9274b6bSCho, Yu-Chen     uint32_t i;
1978c9274b6bSCho, Yu-Chen 
1979c9274b6bSCho, Yu-Chen     if (src & 0x3) {
1980c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
1981c9274b6bSCho, Yu-Chen     }
1982c9274b6bSCho, Yu-Chen 
1983c9274b6bSCho, Yu-Chen     for (i = r1;; i = (i + 1) % 16) {
1984c9274b6bSCho, Yu-Chen         uint32_t val = cpu_ldl_data_ra(env, src, ra);
1985c9274b6bSCho, Yu-Chen         if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) {
1986c9274b6bSCho, Yu-Chen             PERchanged = true;
1987c9274b6bSCho, Yu-Chen         }
1988c9274b6bSCho, Yu-Chen         env->cregs[i] = deposit64(env->cregs[i], 0, 32, val);
1989c9274b6bSCho, Yu-Chen         HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%x\n", i, src, val);
1990c9274b6bSCho, Yu-Chen         src += sizeof(uint32_t);
1991c9274b6bSCho, Yu-Chen 
1992c9274b6bSCho, Yu-Chen         if (i == r3) {
1993c9274b6bSCho, Yu-Chen             break;
1994c9274b6bSCho, Yu-Chen         }
1995c9274b6bSCho, Yu-Chen     }
1996c9274b6bSCho, Yu-Chen 
1997c9274b6bSCho, Yu-Chen     if (PERchanged && env->psw.mask & PSW_MASK_PER) {
1998c9274b6bSCho, Yu-Chen         s390_cpu_recompute_watchpoints(env_cpu(env));
1999c9274b6bSCho, Yu-Chen     }
2000c9274b6bSCho, Yu-Chen 
2001c9274b6bSCho, Yu-Chen     tlb_flush(env_cpu(env));
2002c9274b6bSCho, Yu-Chen }
2003c9274b6bSCho, Yu-Chen 
HELPER(stctg)2004c9274b6bSCho, Yu-Chen void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
2005c9274b6bSCho, Yu-Chen {
2006c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
2007c9274b6bSCho, Yu-Chen     uint64_t dest = a2;
2008c9274b6bSCho, Yu-Chen     uint32_t i;
2009c9274b6bSCho, Yu-Chen 
2010c9274b6bSCho, Yu-Chen     if (dest & 0x7) {
2011c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
2012c9274b6bSCho, Yu-Chen     }
2013c9274b6bSCho, Yu-Chen 
2014c9274b6bSCho, Yu-Chen     for (i = r1;; i = (i + 1) % 16) {
2015c9274b6bSCho, Yu-Chen         cpu_stq_data_ra(env, dest, env->cregs[i], ra);
2016c9274b6bSCho, Yu-Chen         dest += sizeof(uint64_t);
2017c9274b6bSCho, Yu-Chen 
2018c9274b6bSCho, Yu-Chen         if (i == r3) {
2019c9274b6bSCho, Yu-Chen             break;
2020c9274b6bSCho, Yu-Chen         }
2021c9274b6bSCho, Yu-Chen     }
2022c9274b6bSCho, Yu-Chen }
2023c9274b6bSCho, Yu-Chen 
HELPER(stctl)2024c9274b6bSCho, Yu-Chen void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
2025c9274b6bSCho, Yu-Chen {
2026c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
2027c9274b6bSCho, Yu-Chen     uint64_t dest = a2;
2028c9274b6bSCho, Yu-Chen     uint32_t i;
2029c9274b6bSCho, Yu-Chen 
2030c9274b6bSCho, Yu-Chen     if (dest & 0x3) {
2031c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
2032c9274b6bSCho, Yu-Chen     }
2033c9274b6bSCho, Yu-Chen 
2034c9274b6bSCho, Yu-Chen     for (i = r1;; i = (i + 1) % 16) {
2035c9274b6bSCho, Yu-Chen         cpu_stl_data_ra(env, dest, env->cregs[i], ra);
2036c9274b6bSCho, Yu-Chen         dest += sizeof(uint32_t);
2037c9274b6bSCho, Yu-Chen 
2038c9274b6bSCho, Yu-Chen         if (i == r3) {
2039c9274b6bSCho, Yu-Chen             break;
2040c9274b6bSCho, Yu-Chen         }
2041c9274b6bSCho, Yu-Chen     }
2042c9274b6bSCho, Yu-Chen }
2043c9274b6bSCho, Yu-Chen 
HELPER(testblock)2044c9274b6bSCho, Yu-Chen uint32_t HELPER(testblock)(CPUS390XState *env, uint64_t real_addr)
2045c9274b6bSCho, Yu-Chen {
2046c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
2047c9274b6bSCho, Yu-Chen     int i;
2048c9274b6bSCho, Yu-Chen 
2049c9274b6bSCho, Yu-Chen     real_addr = wrap_address(env, real_addr) & TARGET_PAGE_MASK;
2050c9274b6bSCho, Yu-Chen 
2051c9274b6bSCho, Yu-Chen     for (i = 0; i < TARGET_PAGE_SIZE; i += 8) {
2052c9274b6bSCho, Yu-Chen         cpu_stq_mmuidx_ra(env, real_addr + i, 0, MMU_REAL_IDX, ra);
2053c9274b6bSCho, Yu-Chen     }
2054c9274b6bSCho, Yu-Chen 
2055c9274b6bSCho, Yu-Chen     return 0;
2056c9274b6bSCho, Yu-Chen }
2057c9274b6bSCho, Yu-Chen 
HELPER(tprot)2058c9274b6bSCho, Yu-Chen uint32_t HELPER(tprot)(CPUS390XState *env, uint64_t a1, uint64_t a2)
2059c9274b6bSCho, Yu-Chen {
2060c9274b6bSCho, Yu-Chen     S390CPU *cpu = env_archcpu(env);
2061c9274b6bSCho, Yu-Chen     CPUState *cs = env_cpu(env);
2062c9274b6bSCho, Yu-Chen 
2063c9274b6bSCho, Yu-Chen     /*
2064c9274b6bSCho, Yu-Chen      * TODO: we currently don't handle all access protection types
2065c9274b6bSCho, Yu-Chen      * (including access-list and key-controlled) as well as AR mode.
2066c9274b6bSCho, Yu-Chen      */
2067c9274b6bSCho, Yu-Chen     if (!s390_cpu_virt_mem_check_write(cpu, a1, 0, 1)) {
2068c9274b6bSCho, Yu-Chen         /* Fetching permitted; storing permitted */
2069c9274b6bSCho, Yu-Chen         return 0;
2070c9274b6bSCho, Yu-Chen     }
2071c9274b6bSCho, Yu-Chen 
2072c9274b6bSCho, Yu-Chen     if (env->int_pgm_code == PGM_PROTECTION) {
2073c9274b6bSCho, Yu-Chen         /* retry if reading is possible */
2074c9274b6bSCho, Yu-Chen         cs->exception_index = -1;
2075c9274b6bSCho, Yu-Chen         if (!s390_cpu_virt_mem_check_read(cpu, a1, 0, 1)) {
2076c9274b6bSCho, Yu-Chen             /* Fetching permitted; storing not permitted */
2077c9274b6bSCho, Yu-Chen             return 1;
2078c9274b6bSCho, Yu-Chen         }
2079c9274b6bSCho, Yu-Chen     }
2080c9274b6bSCho, Yu-Chen 
2081c9274b6bSCho, Yu-Chen     switch (env->int_pgm_code) {
2082c9274b6bSCho, Yu-Chen     case PGM_PROTECTION:
2083c9274b6bSCho, Yu-Chen         /* Fetching not permitted; storing not permitted */
2084c9274b6bSCho, Yu-Chen         cs->exception_index = -1;
2085c9274b6bSCho, Yu-Chen         return 2;
2086c9274b6bSCho, Yu-Chen     case PGM_ADDRESSING:
2087c9274b6bSCho, Yu-Chen     case PGM_TRANS_SPEC:
2088c9274b6bSCho, Yu-Chen         /* exceptions forwarded to the guest */
2089c9274b6bSCho, Yu-Chen         s390_cpu_virt_mem_handle_exc(cpu, GETPC());
2090c9274b6bSCho, Yu-Chen         return 0;
2091c9274b6bSCho, Yu-Chen     }
2092c9274b6bSCho, Yu-Chen 
2093c9274b6bSCho, Yu-Chen     /* Translation not available */
2094c9274b6bSCho, Yu-Chen     cs->exception_index = -1;
2095c9274b6bSCho, Yu-Chen     return 3;
2096c9274b6bSCho, Yu-Chen }
2097c9274b6bSCho, Yu-Chen 
2098c9274b6bSCho, Yu-Chen /* insert storage key extended */
HELPER(iske)2099c9274b6bSCho, Yu-Chen uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
2100c9274b6bSCho, Yu-Chen {
2101c9274b6bSCho, Yu-Chen     static S390SKeysState *ss;
2102c9274b6bSCho, Yu-Chen     static S390SKeysClass *skeyclass;
2103c9274b6bSCho, Yu-Chen     uint64_t addr = wrap_address(env, r2);
2104c9274b6bSCho, Yu-Chen     uint8_t key;
2105eaa0feeaSDavid Hildenbrand     int rc;
2106c9274b6bSCho, Yu-Chen 
210706d8a10aSDavid Hildenbrand     addr = mmu_real2abs(env, addr);
2108eaa0feeaSDavid Hildenbrand     if (!mmu_absolute_addr_valid(addr, false)) {
2109eaa0feeaSDavid Hildenbrand         tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC());
2110c9274b6bSCho, Yu-Chen     }
2111c9274b6bSCho, Yu-Chen 
2112c9274b6bSCho, Yu-Chen     if (unlikely(!ss)) {
2113c9274b6bSCho, Yu-Chen         ss = s390_get_skeys_device();
2114c9274b6bSCho, Yu-Chen         skeyclass = S390_SKEYS_GET_CLASS(ss);
2115c3562238SDavid Hildenbrand         if (skeyclass->enable_skeys && !skeyclass->enable_skeys(ss)) {
2116c3562238SDavid Hildenbrand             tlb_flush_all_cpus_synced(env_cpu(env));
2117c3562238SDavid Hildenbrand         }
2118c9274b6bSCho, Yu-Chen     }
2119c9274b6bSCho, Yu-Chen 
21204860af2cSPhilippe Mathieu-Daudé     rc = s390_skeys_get(ss, addr / TARGET_PAGE_SIZE, 1, &key);
2121eaa0feeaSDavid Hildenbrand     if (rc) {
2122c9274b6bSCho, Yu-Chen         return 0;
2123c9274b6bSCho, Yu-Chen     }
2124c9274b6bSCho, Yu-Chen     return key;
2125c9274b6bSCho, Yu-Chen }
2126c9274b6bSCho, Yu-Chen 
2127c9274b6bSCho, Yu-Chen /* set storage key extended */
HELPER(sske)2128c9274b6bSCho, Yu-Chen void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
2129c9274b6bSCho, Yu-Chen {
2130c9274b6bSCho, Yu-Chen     static S390SKeysState *ss;
2131c9274b6bSCho, Yu-Chen     static S390SKeysClass *skeyclass;
2132c9274b6bSCho, Yu-Chen     uint64_t addr = wrap_address(env, r2);
2133c9274b6bSCho, Yu-Chen     uint8_t key;
2134c9274b6bSCho, Yu-Chen 
213506d8a10aSDavid Hildenbrand     addr = mmu_real2abs(env, addr);
2136eaa0feeaSDavid Hildenbrand     if (!mmu_absolute_addr_valid(addr, false)) {
2137eaa0feeaSDavid Hildenbrand         tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC());
2138c9274b6bSCho, Yu-Chen     }
2139c9274b6bSCho, Yu-Chen 
2140c9274b6bSCho, Yu-Chen     if (unlikely(!ss)) {
2141c9274b6bSCho, Yu-Chen         ss = s390_get_skeys_device();
2142c9274b6bSCho, Yu-Chen         skeyclass = S390_SKEYS_GET_CLASS(ss);
2143c3562238SDavid Hildenbrand         if (skeyclass->enable_skeys && !skeyclass->enable_skeys(ss)) {
2144c3562238SDavid Hildenbrand             tlb_flush_all_cpus_synced(env_cpu(env));
2145c3562238SDavid Hildenbrand         }
2146c9274b6bSCho, Yu-Chen     }
2147c9274b6bSCho, Yu-Chen 
2148fe00c705SDavid Hildenbrand     key = r1 & 0xfe;
21494860af2cSPhilippe Mathieu-Daudé     s390_skeys_set(ss, addr / TARGET_PAGE_SIZE, 1, &key);
2150c9274b6bSCho, Yu-Chen    /*
2151c9274b6bSCho, Yu-Chen     * As we can only flush by virtual address and not all the entries
2152c9274b6bSCho, Yu-Chen     * that point to a physical address we have to flush the whole TLB.
2153c9274b6bSCho, Yu-Chen     */
2154c9274b6bSCho, Yu-Chen     tlb_flush_all_cpus_synced(env_cpu(env));
2155c9274b6bSCho, Yu-Chen }
2156c9274b6bSCho, Yu-Chen 
2157c9274b6bSCho, Yu-Chen /* reset reference bit extended */
HELPER(rrbe)2158c9274b6bSCho, Yu-Chen uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
2159c9274b6bSCho, Yu-Chen {
2160634a0b51SDavid Hildenbrand     uint64_t addr = wrap_address(env, r2);
2161c9274b6bSCho, Yu-Chen     static S390SKeysState *ss;
2162c9274b6bSCho, Yu-Chen     static S390SKeysClass *skeyclass;
2163c9274b6bSCho, Yu-Chen     uint8_t re, key;
2164eaa0feeaSDavid Hildenbrand     int rc;
2165c9274b6bSCho, Yu-Chen 
216606d8a10aSDavid Hildenbrand     addr = mmu_real2abs(env, addr);
2167eaa0feeaSDavid Hildenbrand     if (!mmu_absolute_addr_valid(addr, false)) {
2168eaa0feeaSDavid Hildenbrand         tcg_s390_program_interrupt(env, PGM_ADDRESSING, GETPC());
2169c9274b6bSCho, Yu-Chen     }
2170c9274b6bSCho, Yu-Chen 
2171c9274b6bSCho, Yu-Chen     if (unlikely(!ss)) {
2172c9274b6bSCho, Yu-Chen         ss = s390_get_skeys_device();
2173c9274b6bSCho, Yu-Chen         skeyclass = S390_SKEYS_GET_CLASS(ss);
2174c3562238SDavid Hildenbrand         if (skeyclass->enable_skeys && !skeyclass->enable_skeys(ss)) {
2175c3562238SDavid Hildenbrand             tlb_flush_all_cpus_synced(env_cpu(env));
2176c3562238SDavid Hildenbrand         }
2177c9274b6bSCho, Yu-Chen     }
2178c9274b6bSCho, Yu-Chen 
21794860af2cSPhilippe Mathieu-Daudé     rc = s390_skeys_get(ss, addr / TARGET_PAGE_SIZE, 1, &key);
2180eaa0feeaSDavid Hildenbrand     if (rc) {
2181c9274b6bSCho, Yu-Chen         return 0;
2182c9274b6bSCho, Yu-Chen     }
2183c9274b6bSCho, Yu-Chen 
2184c9274b6bSCho, Yu-Chen     re = key & (SK_R | SK_C);
2185c9274b6bSCho, Yu-Chen     key &= ~SK_R;
2186c9274b6bSCho, Yu-Chen 
21874860af2cSPhilippe Mathieu-Daudé     rc = s390_skeys_set(ss, addr / TARGET_PAGE_SIZE, 1, &key);
2188eaa0feeaSDavid Hildenbrand     if (rc) {
2189c9274b6bSCho, Yu-Chen         return 0;
2190c9274b6bSCho, Yu-Chen     }
2191c9274b6bSCho, Yu-Chen    /*
2192c9274b6bSCho, Yu-Chen     * As we can only flush by virtual address and not all the entries
2193c9274b6bSCho, Yu-Chen     * that point to a physical address we have to flush the whole TLB.
2194c9274b6bSCho, Yu-Chen     */
2195c9274b6bSCho, Yu-Chen     tlb_flush_all_cpus_synced(env_cpu(env));
2196c9274b6bSCho, Yu-Chen 
2197c9274b6bSCho, Yu-Chen     /*
2198c9274b6bSCho, Yu-Chen      * cc
2199c9274b6bSCho, Yu-Chen      *
2200c9274b6bSCho, Yu-Chen      * 0  Reference bit zero; change bit zero
2201c9274b6bSCho, Yu-Chen      * 1  Reference bit zero; change bit one
2202c9274b6bSCho, Yu-Chen      * 2  Reference bit one; change bit zero
2203c9274b6bSCho, Yu-Chen      * 3  Reference bit one; change bit one
2204c9274b6bSCho, Yu-Chen      */
2205c9274b6bSCho, Yu-Chen 
2206c9274b6bSCho, Yu-Chen     return re >> 1;
2207c9274b6bSCho, Yu-Chen }
2208c9274b6bSCho, Yu-Chen 
HELPER(mvcs)22093ef473e5SThomas Huth uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2,
22103ef473e5SThomas Huth                       uint64_t key)
2211c9274b6bSCho, Yu-Chen {
2212c9274b6bSCho, Yu-Chen     const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC;
2213c9274b6bSCho, Yu-Chen     S390Access srca, desta;
2214c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
2215c9274b6bSCho, Yu-Chen     int cc = 0;
2216c9274b6bSCho, Yu-Chen 
2217c9274b6bSCho, Yu-Chen     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
2218c9274b6bSCho, Yu-Chen                __func__, l, a1, a2);
2219c9274b6bSCho, Yu-Chen 
2220c9274b6bSCho, Yu-Chen     if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) ||
2221c9274b6bSCho, Yu-Chen         psw_as == AS_HOME || psw_as == AS_ACCREG) {
2222c9274b6bSCho, Yu-Chen         s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
2223c9274b6bSCho, Yu-Chen     }
2224c9274b6bSCho, Yu-Chen 
22253ef473e5SThomas Huth     if (!psw_key_valid(env, (key >> 4) & 0xf)) {
22263ef473e5SThomas Huth         s390_program_interrupt(env, PGM_PRIVILEGED, ra);
22273ef473e5SThomas Huth     }
22283ef473e5SThomas Huth 
2229c9274b6bSCho, Yu-Chen     l = wrap_length32(env, l);
2230c9274b6bSCho, Yu-Chen     if (l > 256) {
2231c9274b6bSCho, Yu-Chen         /* max 256 */
2232c9274b6bSCho, Yu-Chen         l = 256;
2233c9274b6bSCho, Yu-Chen         cc = 3;
2234c9274b6bSCho, Yu-Chen     } else if (!l) {
2235c9274b6bSCho, Yu-Chen         return cc;
2236c9274b6bSCho, Yu-Chen     }
2237c9274b6bSCho, Yu-Chen 
22387ba5da81SRichard Henderson     access_prepare(&srca, env, a2, l, MMU_DATA_LOAD, MMU_PRIMARY_IDX, ra);
22397ba5da81SRichard Henderson     access_prepare(&desta, env, a1, l, MMU_DATA_STORE, MMU_SECONDARY_IDX, ra);
2240c9274b6bSCho, Yu-Chen     access_memmove(env, &desta, &srca, ra);
2241c9274b6bSCho, Yu-Chen     return cc;
2242c9274b6bSCho, Yu-Chen }
2243c9274b6bSCho, Yu-Chen 
HELPER(mvcp)22443ef473e5SThomas Huth uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2,
22453ef473e5SThomas Huth                       uint64_t key)
2246c9274b6bSCho, Yu-Chen {
2247c9274b6bSCho, Yu-Chen     const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC;
2248c9274b6bSCho, Yu-Chen     S390Access srca, desta;
2249c9274b6bSCho, Yu-Chen     uintptr_t ra = GETPC();
2250c9274b6bSCho, Yu-Chen     int cc = 0;
2251c9274b6bSCho, Yu-Chen 
2252c9274b6bSCho, Yu-Chen     HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
2253c9274b6bSCho, Yu-Chen                __func__, l, a1, a2);
2254c9274b6bSCho, Yu-Chen 
2255c9274b6bSCho, Yu-Chen     if (!(env->psw.mask & PSW_MASK_DAT) || !(env->cregs[0] & CR0_SECONDARY) ||
2256c9274b6bSCho, Yu-Chen         psw_as == AS_HOME || psw_as == AS_ACCREG) {
2257c9274b6bSCho, Yu-Chen         s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
2258c9274b6bSCho, Yu-Chen     }
2259c9274b6bSCho, Yu-Chen 
22603ef473e5SThomas Huth     if (!psw_key_valid(env, (key >> 4) & 0xf)) {
22613ef473e5SThomas Huth         s390_program_interrupt(env, PGM_PRIVILEGED, ra);
22623ef473e5SThomas Huth     }
22633ef473e5SThomas Huth 
2264c9274b6bSCho, Yu-Chen     l = wrap_length32(env, l);
2265c9274b6bSCho, Yu-Chen     if (l > 256) {
2266c9274b6bSCho, Yu-Chen         /* max 256 */
2267c9274b6bSCho, Yu-Chen         l = 256;
2268c9274b6bSCho, Yu-Chen         cc = 3;
2269c9274b6bSCho, Yu-Chen     } else if (!l) {
2270c9274b6bSCho, Yu-Chen         return cc;
2271c9274b6bSCho, Yu-Chen     }
22727ba5da81SRichard Henderson     access_prepare(&srca, env, a2, l, MMU_DATA_LOAD, MMU_SECONDARY_IDX, ra);
22737ba5da81SRichard Henderson     access_prepare(&desta, env, a1, l, MMU_DATA_STORE, MMU_PRIMARY_IDX, ra);
2274c9274b6bSCho, Yu-Chen     access_memmove(env, &desta, &srca, ra);
2275c9274b6bSCho, Yu-Chen     return cc;
2276c9274b6bSCho, Yu-Chen }
2277c9274b6bSCho, Yu-Chen 
HELPER(idte)2278c9274b6bSCho, Yu-Chen void HELPER(idte)(CPUS390XState *env, uint64_t r1, uint64_t r2, uint32_t m4)
2279c9274b6bSCho, Yu-Chen {
2280c9274b6bSCho, Yu-Chen     CPUState *cs = env_cpu(env);
2281c9274b6bSCho, Yu-Chen     const uintptr_t ra = GETPC();
2282c9274b6bSCho, Yu-Chen     uint64_t table, entry, raddr;
2283c9274b6bSCho, Yu-Chen     uint16_t entries, i, index = 0;
2284c9274b6bSCho, Yu-Chen 
2285c9274b6bSCho, Yu-Chen     if (r2 & 0xff000) {
2286c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, ra);
2287c9274b6bSCho, Yu-Chen     }
2288c9274b6bSCho, Yu-Chen 
2289c9274b6bSCho, Yu-Chen     if (!(r2 & 0x800)) {
2290c9274b6bSCho, Yu-Chen         /* invalidation-and-clearing operation */
2291c9274b6bSCho, Yu-Chen         table = r1 & ASCE_ORIGIN;
2292c9274b6bSCho, Yu-Chen         entries = (r2 & 0x7ff) + 1;
2293c9274b6bSCho, Yu-Chen 
2294c9274b6bSCho, Yu-Chen         switch (r1 & ASCE_TYPE_MASK) {
2295c9274b6bSCho, Yu-Chen         case ASCE_TYPE_REGION1:
2296c9274b6bSCho, Yu-Chen             index = (r2 >> 53) & 0x7ff;
2297c9274b6bSCho, Yu-Chen             break;
2298c9274b6bSCho, Yu-Chen         case ASCE_TYPE_REGION2:
2299c9274b6bSCho, Yu-Chen             index = (r2 >> 42) & 0x7ff;
2300c9274b6bSCho, Yu-Chen             break;
2301c9274b6bSCho, Yu-Chen         case ASCE_TYPE_REGION3:
2302c9274b6bSCho, Yu-Chen             index = (r2 >> 31) & 0x7ff;
2303c9274b6bSCho, Yu-Chen             break;
2304c9274b6bSCho, Yu-Chen         case ASCE_TYPE_SEGMENT:
2305c9274b6bSCho, Yu-Chen             index = (r2 >> 20) & 0x7ff;
2306c9274b6bSCho, Yu-Chen             break;
2307c9274b6bSCho, Yu-Chen         }
2308c9274b6bSCho, Yu-Chen         for (i = 0; i < entries; i++) {
2309c9274b6bSCho, Yu-Chen             /* addresses are not wrapped in 24/31bit mode but table index is */
2310c9274b6bSCho, Yu-Chen             raddr = table + ((index + i) & 0x7ff) * sizeof(entry);
2311c9274b6bSCho, Yu-Chen             entry = cpu_ldq_mmuidx_ra(env, raddr, MMU_REAL_IDX, ra);
2312c9274b6bSCho, Yu-Chen             if (!(entry & REGION_ENTRY_I)) {
2313c9274b6bSCho, Yu-Chen                 /* we are allowed to not store if already invalid */
2314c9274b6bSCho, Yu-Chen                 entry |= REGION_ENTRY_I;
2315c9274b6bSCho, Yu-Chen                 cpu_stq_mmuidx_ra(env, raddr, entry, MMU_REAL_IDX, ra);
2316c9274b6bSCho, Yu-Chen             }
2317c9274b6bSCho, Yu-Chen         }
2318c9274b6bSCho, Yu-Chen     }
2319c9274b6bSCho, Yu-Chen 
2320c9274b6bSCho, Yu-Chen     /* We simply flush the complete tlb, therefore we can ignore r3. */
2321c9274b6bSCho, Yu-Chen     if (m4 & 1) {
2322c9274b6bSCho, Yu-Chen         tlb_flush(cs);
2323c9274b6bSCho, Yu-Chen     } else {
2324c9274b6bSCho, Yu-Chen         tlb_flush_all_cpus_synced(cs);
2325c9274b6bSCho, Yu-Chen     }
2326c9274b6bSCho, Yu-Chen }
2327c9274b6bSCho, Yu-Chen 
2328c9274b6bSCho, Yu-Chen /* invalidate pte */
HELPER(ipte)2329c9274b6bSCho, Yu-Chen void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr,
2330c9274b6bSCho, Yu-Chen                   uint32_t m4)
2331c9274b6bSCho, Yu-Chen {
2332c9274b6bSCho, Yu-Chen     CPUState *cs = env_cpu(env);
2333c9274b6bSCho, Yu-Chen     const uintptr_t ra = GETPC();
2334c9274b6bSCho, Yu-Chen     uint64_t page = vaddr & TARGET_PAGE_MASK;
2335c9274b6bSCho, Yu-Chen     uint64_t pte_addr, pte;
2336c9274b6bSCho, Yu-Chen 
2337c9274b6bSCho, Yu-Chen     /* Compute the page table entry address */
2338c9274b6bSCho, Yu-Chen     pte_addr = (pto & SEGMENT_ENTRY_ORIGIN);
2339c9274b6bSCho, Yu-Chen     pte_addr += VADDR_PAGE_TX(vaddr) * 8;
2340c9274b6bSCho, Yu-Chen 
2341c9274b6bSCho, Yu-Chen     /* Mark the page table entry as invalid */
2342c9274b6bSCho, Yu-Chen     pte = cpu_ldq_mmuidx_ra(env, pte_addr, MMU_REAL_IDX, ra);
2343c9274b6bSCho, Yu-Chen     pte |= PAGE_ENTRY_I;
2344c9274b6bSCho, Yu-Chen     cpu_stq_mmuidx_ra(env, pte_addr, pte, MMU_REAL_IDX, ra);
2345c9274b6bSCho, Yu-Chen 
2346c9274b6bSCho, Yu-Chen     /* XXX we exploit the fact that Linux passes the exact virtual
2347c9274b6bSCho, Yu-Chen        address here - it's not obliged to! */
2348c9274b6bSCho, Yu-Chen     if (m4 & 1) {
2349c9274b6bSCho, Yu-Chen         if (vaddr & ~VADDR_PAGE_TX_MASK) {
2350c9274b6bSCho, Yu-Chen             tlb_flush_page(cs, page);
2351c9274b6bSCho, Yu-Chen             /* XXX 31-bit hack */
2352c9274b6bSCho, Yu-Chen             tlb_flush_page(cs, page ^ 0x80000000);
2353c9274b6bSCho, Yu-Chen         } else {
2354c9274b6bSCho, Yu-Chen             /* looks like we don't have a valid virtual address */
2355c9274b6bSCho, Yu-Chen             tlb_flush(cs);
2356c9274b6bSCho, Yu-Chen         }
2357c9274b6bSCho, Yu-Chen     } else {
2358c9274b6bSCho, Yu-Chen         if (vaddr & ~VADDR_PAGE_TX_MASK) {
2359c9274b6bSCho, Yu-Chen             tlb_flush_page_all_cpus_synced(cs, page);
2360c9274b6bSCho, Yu-Chen             /* XXX 31-bit hack */
2361c9274b6bSCho, Yu-Chen             tlb_flush_page_all_cpus_synced(cs, page ^ 0x80000000);
2362c9274b6bSCho, Yu-Chen         } else {
2363c9274b6bSCho, Yu-Chen             /* looks like we don't have a valid virtual address */
2364c9274b6bSCho, Yu-Chen             tlb_flush_all_cpus_synced(cs);
2365c9274b6bSCho, Yu-Chen         }
2366c9274b6bSCho, Yu-Chen     }
2367c9274b6bSCho, Yu-Chen }
2368c9274b6bSCho, Yu-Chen 
2369c9274b6bSCho, Yu-Chen /* flush local tlb */
HELPER(ptlb)2370c9274b6bSCho, Yu-Chen void HELPER(ptlb)(CPUS390XState *env)
2371c9274b6bSCho, Yu-Chen {
2372c9274b6bSCho, Yu-Chen     tlb_flush(env_cpu(env));
2373c9274b6bSCho, Yu-Chen }
2374c9274b6bSCho, Yu-Chen 
2375c9274b6bSCho, Yu-Chen /* flush global tlb */
HELPER(purge)2376c9274b6bSCho, Yu-Chen void HELPER(purge)(CPUS390XState *env)
2377c9274b6bSCho, Yu-Chen {
2378c9274b6bSCho, Yu-Chen     tlb_flush_all_cpus_synced(env_cpu(env));
2379c9274b6bSCho, Yu-Chen }
2380c9274b6bSCho, Yu-Chen 
2381c9274b6bSCho, Yu-Chen /* load real address */
HELPER(lra)23826da311a6SIlya Leoshkevich uint64_t HELPER(lra)(CPUS390XState *env, uint64_t r1, uint64_t addr)
2383c9274b6bSCho, Yu-Chen {
2384c9274b6bSCho, Yu-Chen     uint64_t asc = env->psw.mask & PSW_MASK_ASC;
2385c9274b6bSCho, Yu-Chen     uint64_t ret, tec;
2386c9274b6bSCho, Yu-Chen     int flags, exc, cc;
2387c9274b6bSCho, Yu-Chen 
2388c9274b6bSCho, Yu-Chen     /* XXX incomplete - has more corner cases */
2389c9274b6bSCho, Yu-Chen     if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
2390c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, GETPC());
2391c9274b6bSCho, Yu-Chen     }
2392c9274b6bSCho, Yu-Chen 
2393390191c6SDavid Hildenbrand     exc = mmu_translate(env, addr, MMU_S390_LRA, asc, &ret, &flags, &tec);
2394c9274b6bSCho, Yu-Chen     if (exc) {
2395c9274b6bSCho, Yu-Chen         cc = 3;
23966da311a6SIlya Leoshkevich         ret = (r1 & 0xFFFFFFFF00000000ULL) | exc | 0x80000000;
2397c9274b6bSCho, Yu-Chen     } else {
2398c9274b6bSCho, Yu-Chen         cc = 0;
2399c9274b6bSCho, Yu-Chen         ret |= addr & ~TARGET_PAGE_MASK;
2400c9274b6bSCho, Yu-Chen     }
2401c9274b6bSCho, Yu-Chen 
2402c9274b6bSCho, Yu-Chen     env->cc_op = cc;
2403c9274b6bSCho, Yu-Chen     return ret;
2404c9274b6bSCho, Yu-Chen }
2405c9274b6bSCho, Yu-Chen #endif
2406c9274b6bSCho, Yu-Chen 
2407c9274b6bSCho, Yu-Chen /* Execute instruction.  This instruction executes an insn modified with
2408c9274b6bSCho, Yu-Chen    the contents of r1.  It does not change the executed instruction in memory;
2409c9274b6bSCho, Yu-Chen    it does not change the program counter.
2410c9274b6bSCho, Yu-Chen 
2411c9274b6bSCho, Yu-Chen    Perform this by recording the modified instruction in env->ex_value.
2412c9274b6bSCho, Yu-Chen    This will be noticed by cpu_get_tb_cpu_state and thus tb translation.
2413c9274b6bSCho, Yu-Chen */
HELPER(ex)2414c9274b6bSCho, Yu-Chen void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr)
2415c9274b6bSCho, Yu-Chen {
2416ce7ca269SIlya Leoshkevich     uint64_t insn;
2417ce7ca269SIlya Leoshkevich     uint8_t opc;
2418ce7ca269SIlya Leoshkevich 
2419ce7ca269SIlya Leoshkevich     /* EXECUTE targets must be at even addresses.  */
2420ce7ca269SIlya Leoshkevich     if (addr & 1) {
2421ce7ca269SIlya Leoshkevich         tcg_s390_program_interrupt(env, PGM_SPECIFICATION, GETPC());
2422ce7ca269SIlya Leoshkevich     }
2423ce7ca269SIlya Leoshkevich 
2424ce7ca269SIlya Leoshkevich     insn = cpu_lduw_code(env, addr);
2425ce7ca269SIlya Leoshkevich     opc = insn >> 8;
2426c9274b6bSCho, Yu-Chen 
2427c9274b6bSCho, Yu-Chen     /* Or in the contents of R1[56:63].  */
2428c9274b6bSCho, Yu-Chen     insn |= r1 & 0xff;
2429c9274b6bSCho, Yu-Chen 
2430c9274b6bSCho, Yu-Chen     /* Load the rest of the instruction.  */
2431c9274b6bSCho, Yu-Chen     insn <<= 48;
2432c9274b6bSCho, Yu-Chen     switch (get_ilen(opc)) {
2433c9274b6bSCho, Yu-Chen     case 2:
2434c9274b6bSCho, Yu-Chen         break;
2435c9274b6bSCho, Yu-Chen     case 4:
2436c9274b6bSCho, Yu-Chen         insn |= (uint64_t)cpu_lduw_code(env, addr + 2) << 32;
2437c9274b6bSCho, Yu-Chen         break;
2438c9274b6bSCho, Yu-Chen     case 6:
2439c9274b6bSCho, Yu-Chen         insn |= (uint64_t)(uint32_t)cpu_ldl_code(env, addr + 2) << 16;
2440c9274b6bSCho, Yu-Chen         break;
2441c9274b6bSCho, Yu-Chen     default:
2442c9274b6bSCho, Yu-Chen         g_assert_not_reached();
2443c9274b6bSCho, Yu-Chen     }
2444c9274b6bSCho, Yu-Chen 
2445c9274b6bSCho, Yu-Chen     /* The very most common cases can be sped up by avoiding a new TB.  */
2446c9274b6bSCho, Yu-Chen     if ((opc & 0xf0) == 0xd0) {
2447c9274b6bSCho, Yu-Chen         typedef uint32_t (*dx_helper)(CPUS390XState *, uint32_t, uint64_t,
2448c9274b6bSCho, Yu-Chen                                       uint64_t, uintptr_t);
2449c9274b6bSCho, Yu-Chen         static const dx_helper dx[16] = {
2450c9274b6bSCho, Yu-Chen             [0x0] = do_helper_trt_bkwd,
2451c9274b6bSCho, Yu-Chen             [0x2] = do_helper_mvc,
2452c9274b6bSCho, Yu-Chen             [0x4] = do_helper_nc,
2453c9274b6bSCho, Yu-Chen             [0x5] = do_helper_clc,
2454c9274b6bSCho, Yu-Chen             [0x6] = do_helper_oc,
2455c9274b6bSCho, Yu-Chen             [0x7] = do_helper_xc,
2456c9274b6bSCho, Yu-Chen             [0xc] = do_helper_tr,
2457c9274b6bSCho, Yu-Chen             [0xd] = do_helper_trt_fwd,
2458c9274b6bSCho, Yu-Chen         };
2459c9274b6bSCho, Yu-Chen         dx_helper helper = dx[opc & 0xf];
2460c9274b6bSCho, Yu-Chen 
2461c9274b6bSCho, Yu-Chen         if (helper) {
2462c9274b6bSCho, Yu-Chen             uint32_t l = extract64(insn, 48, 8);
2463c9274b6bSCho, Yu-Chen             uint32_t b1 = extract64(insn, 44, 4);
2464c9274b6bSCho, Yu-Chen             uint32_t d1 = extract64(insn, 32, 12);
2465c9274b6bSCho, Yu-Chen             uint32_t b2 = extract64(insn, 28, 4);
2466c9274b6bSCho, Yu-Chen             uint32_t d2 = extract64(insn, 16, 12);
2467c9274b6bSCho, Yu-Chen             uint64_t a1 = wrap_address(env, (b1 ? env->regs[b1] : 0) + d1);
2468c9274b6bSCho, Yu-Chen             uint64_t a2 = wrap_address(env, (b2 ? env->regs[b2] : 0) + d2);
2469c9274b6bSCho, Yu-Chen 
2470c9274b6bSCho, Yu-Chen             env->cc_op = helper(env, l, a1, a2, 0);
2471c9274b6bSCho, Yu-Chen             env->psw.addr += ilen;
2472c9274b6bSCho, Yu-Chen             return;
2473c9274b6bSCho, Yu-Chen         }
2474c9274b6bSCho, Yu-Chen     } else if (opc == 0x0a) {
2475c9274b6bSCho, Yu-Chen         env->int_svc_code = extract64(insn, 48, 8);
2476c9274b6bSCho, Yu-Chen         env->int_svc_ilen = ilen;
2477c9274b6bSCho, Yu-Chen         helper_exception(env, EXCP_SVC);
2478c9274b6bSCho, Yu-Chen         g_assert_not_reached();
2479c9274b6bSCho, Yu-Chen     }
2480c9274b6bSCho, Yu-Chen 
2481c9274b6bSCho, Yu-Chen     /* Record the insn we want to execute as well as the ilen to use
2482c9274b6bSCho, Yu-Chen        during the execution of the target insn.  This will also ensure
2483c9274b6bSCho, Yu-Chen        that ex_value is non-zero, which flags that we are in a state
2484c9274b6bSCho, Yu-Chen        that requires such execution.  */
2485c9274b6bSCho, Yu-Chen     env->ex_value = insn | ilen;
2486703d03a4SIlya Leoshkevich     env->ex_target = addr;
2487c9274b6bSCho, Yu-Chen }
2488c9274b6bSCho, Yu-Chen 
HELPER(mvcos)2489c9274b6bSCho, Yu-Chen uint32_t HELPER(mvcos)(CPUS390XState *env, uint64_t dest, uint64_t src,
2490c9274b6bSCho, Yu-Chen                        uint64_t len)
2491c9274b6bSCho, Yu-Chen {
2492c9274b6bSCho, Yu-Chen     const uint8_t psw_key = (env->psw.mask & PSW_MASK_KEY) >> PSW_SHIFT_KEY;
2493c9274b6bSCho, Yu-Chen     const uint8_t psw_as = (env->psw.mask & PSW_MASK_ASC) >> PSW_SHIFT_ASC;
2494c9274b6bSCho, Yu-Chen     const uint64_t r0 = env->regs[0];
2495c9274b6bSCho, Yu-Chen     const uintptr_t ra = GETPC();
2496c9274b6bSCho, Yu-Chen     uint8_t dest_key, dest_as, dest_k, dest_a;
2497c9274b6bSCho, Yu-Chen     uint8_t src_key, src_as, src_k, src_a;
2498c9274b6bSCho, Yu-Chen     uint64_t val;
2499c9274b6bSCho, Yu-Chen     int cc = 0;
2500c9274b6bSCho, Yu-Chen 
2501c9274b6bSCho, Yu-Chen     HELPER_LOG("%s dest %" PRIx64 ", src %" PRIx64 ", len %" PRIx64 "\n",
2502c9274b6bSCho, Yu-Chen                __func__, dest, src, len);
2503c9274b6bSCho, Yu-Chen 
2504c9274b6bSCho, Yu-Chen     if (!(env->psw.mask & PSW_MASK_DAT)) {
2505c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
2506c9274b6bSCho, Yu-Chen     }
2507c9274b6bSCho, Yu-Chen 
2508c9274b6bSCho, Yu-Chen     /* OAC (operand access control) for the first operand -> dest */
2509c9274b6bSCho, Yu-Chen     val = (r0 & 0xffff0000ULL) >> 16;
2510c9274b6bSCho, Yu-Chen     dest_key = (val >> 12) & 0xf;
2511c9274b6bSCho, Yu-Chen     dest_as = (val >> 6) & 0x3;
2512c9274b6bSCho, Yu-Chen     dest_k = (val >> 1) & 0x1;
2513c9274b6bSCho, Yu-Chen     dest_a = val & 0x1;
2514c9274b6bSCho, Yu-Chen 
2515c9274b6bSCho, Yu-Chen     /* OAC (operand access control) for the second operand -> src */
2516c9274b6bSCho, Yu-Chen     val = (r0 & 0x0000ffffULL);
2517c9274b6bSCho, Yu-Chen     src_key = (val >> 12) & 0xf;
2518c9274b6bSCho, Yu-Chen     src_as = (val >> 6) & 0x3;
2519c9274b6bSCho, Yu-Chen     src_k = (val >> 1) & 0x1;
2520c9274b6bSCho, Yu-Chen     src_a = val & 0x1;
2521c9274b6bSCho, Yu-Chen 
2522c9274b6bSCho, Yu-Chen     if (!dest_k) {
2523c9274b6bSCho, Yu-Chen         dest_key = psw_key;
2524c9274b6bSCho, Yu-Chen     }
2525c9274b6bSCho, Yu-Chen     if (!src_k) {
2526c9274b6bSCho, Yu-Chen         src_key = psw_key;
2527c9274b6bSCho, Yu-Chen     }
2528c9274b6bSCho, Yu-Chen     if (!dest_a) {
2529c9274b6bSCho, Yu-Chen         dest_as = psw_as;
2530c9274b6bSCho, Yu-Chen     }
2531c9274b6bSCho, Yu-Chen     if (!src_a) {
2532c9274b6bSCho, Yu-Chen         src_as = psw_as;
2533c9274b6bSCho, Yu-Chen     }
2534c9274b6bSCho, Yu-Chen 
2535c9274b6bSCho, Yu-Chen     if (dest_a && dest_as == AS_HOME && (env->psw.mask & PSW_MASK_PSTATE)) {
2536c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
2537c9274b6bSCho, Yu-Chen     }
2538c9274b6bSCho, Yu-Chen     if (!(env->cregs[0] & CR0_SECONDARY) &&
2539c9274b6bSCho, Yu-Chen         (dest_as == AS_SECONDARY || src_as == AS_SECONDARY)) {
2540c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_SPECIAL_OP, ra);
2541c9274b6bSCho, Yu-Chen     }
2542c9274b6bSCho, Yu-Chen     if (!psw_key_valid(env, dest_key) || !psw_key_valid(env, src_key)) {
2543c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_PRIVILEGED, ra);
2544c9274b6bSCho, Yu-Chen     }
2545c9274b6bSCho, Yu-Chen 
2546c9274b6bSCho, Yu-Chen     len = wrap_length32(env, len);
2547c9274b6bSCho, Yu-Chen     if (len > 4096) {
2548c9274b6bSCho, Yu-Chen         cc = 3;
2549c9274b6bSCho, Yu-Chen         len = 4096;
2550c9274b6bSCho, Yu-Chen     }
2551c9274b6bSCho, Yu-Chen 
2552c9274b6bSCho, Yu-Chen     /* FIXME: AR-mode and proper problem state mode (using PSW keys) missing */
2553c9274b6bSCho, Yu-Chen     if (src_as == AS_ACCREG || dest_as == AS_ACCREG ||
2554c9274b6bSCho, Yu-Chen         (env->psw.mask & PSW_MASK_PSTATE)) {
2555c9274b6bSCho, Yu-Chen         qemu_log_mask(LOG_UNIMP, "%s: AR-mode and PSTATE support missing\n",
2556c9274b6bSCho, Yu-Chen                       __func__);
2557c9274b6bSCho, Yu-Chen         tcg_s390_program_interrupt(env, PGM_ADDRESSING, ra);
2558c9274b6bSCho, Yu-Chen     }
2559c9274b6bSCho, Yu-Chen 
2560c9274b6bSCho, Yu-Chen     /* FIXME: Access using correct keys and AR-mode */
2561c9274b6bSCho, Yu-Chen     if (len) {
25627ba5da81SRichard Henderson         S390Access srca, desta;
25637ba5da81SRichard Henderson 
25647ba5da81SRichard Henderson         access_prepare(&srca, env, src, len, MMU_DATA_LOAD,
2565c9274b6bSCho, Yu-Chen                        mmu_idx_from_as(src_as), ra);
25667ba5da81SRichard Henderson         access_prepare(&desta, env, dest, len, MMU_DATA_STORE,
2567c9274b6bSCho, Yu-Chen                        mmu_idx_from_as(dest_as), ra);
2568c9274b6bSCho, Yu-Chen 
2569c9274b6bSCho, Yu-Chen         access_memmove(env, &desta, &srca, ra);
2570c9274b6bSCho, Yu-Chen     }
2571c9274b6bSCho, Yu-Chen 
2572c9274b6bSCho, Yu-Chen     return cc;
2573c9274b6bSCho, Yu-Chen }
2574c9274b6bSCho, Yu-Chen 
2575c9274b6bSCho, Yu-Chen /* Decode a Unicode character.  A return value < 0 indicates success, storing
2576c9274b6bSCho, Yu-Chen    the UTF-32 result into OCHAR and the input length into OLEN.  A return
2577c9274b6bSCho, Yu-Chen    value >= 0 indicates failure, and the CC value to be returned.  */
2578c9274b6bSCho, Yu-Chen typedef int (*decode_unicode_fn)(CPUS390XState *env, uint64_t addr,
2579c9274b6bSCho, Yu-Chen                                  uint64_t ilen, bool enh_check, uintptr_t ra,
2580c9274b6bSCho, Yu-Chen                                  uint32_t *ochar, uint32_t *olen);
2581c9274b6bSCho, Yu-Chen 
2582c9274b6bSCho, Yu-Chen /* Encode a Unicode character.  A return value < 0 indicates success, storing
2583c9274b6bSCho, Yu-Chen    the bytes into ADDR and the output length into OLEN.  A return value >= 0
2584c9274b6bSCho, Yu-Chen    indicates failure, and the CC value to be returned.  */
2585c9274b6bSCho, Yu-Chen typedef int (*encode_unicode_fn)(CPUS390XState *env, uint64_t addr,
2586c9274b6bSCho, Yu-Chen                                  uint64_t ilen, uintptr_t ra, uint32_t c,
2587c9274b6bSCho, Yu-Chen                                  uint32_t *olen);
2588c9274b6bSCho, Yu-Chen 
decode_utf8(CPUS390XState * env,uint64_t addr,uint64_t ilen,bool enh_check,uintptr_t ra,uint32_t * ochar,uint32_t * olen)2589c9274b6bSCho, Yu-Chen static int decode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen,
2590c9274b6bSCho, Yu-Chen                        bool enh_check, uintptr_t ra,
2591c9274b6bSCho, Yu-Chen                        uint32_t *ochar, uint32_t *olen)
2592c9274b6bSCho, Yu-Chen {
2593c9274b6bSCho, Yu-Chen     uint8_t s0, s1, s2, s3;
2594c9274b6bSCho, Yu-Chen     uint32_t c, l;
2595c9274b6bSCho, Yu-Chen 
2596c9274b6bSCho, Yu-Chen     if (ilen < 1) {
2597c9274b6bSCho, Yu-Chen         return 0;
2598c9274b6bSCho, Yu-Chen     }
2599c9274b6bSCho, Yu-Chen     s0 = cpu_ldub_data_ra(env, addr, ra);
2600c9274b6bSCho, Yu-Chen     if (s0 <= 0x7f) {
2601c9274b6bSCho, Yu-Chen         /* one byte character */
2602c9274b6bSCho, Yu-Chen         l = 1;
2603c9274b6bSCho, Yu-Chen         c = s0;
2604c9274b6bSCho, Yu-Chen     } else if (s0 <= (enh_check ? 0xc1 : 0xbf)) {
2605c9274b6bSCho, Yu-Chen         /* invalid character */
2606c9274b6bSCho, Yu-Chen         return 2;
2607c9274b6bSCho, Yu-Chen     } else if (s0 <= 0xdf) {
2608c9274b6bSCho, Yu-Chen         /* two byte character */
2609c9274b6bSCho, Yu-Chen         l = 2;
2610c9274b6bSCho, Yu-Chen         if (ilen < 2) {
2611c9274b6bSCho, Yu-Chen             return 0;
2612c9274b6bSCho, Yu-Chen         }
2613c9274b6bSCho, Yu-Chen         s1 = cpu_ldub_data_ra(env, addr + 1, ra);
2614c9274b6bSCho, Yu-Chen         c = s0 & 0x1f;
2615c9274b6bSCho, Yu-Chen         c = (c << 6) | (s1 & 0x3f);
2616c9274b6bSCho, Yu-Chen         if (enh_check && (s1 & 0xc0) != 0x80) {
2617c9274b6bSCho, Yu-Chen             return 2;
2618c9274b6bSCho, Yu-Chen         }
2619c9274b6bSCho, Yu-Chen     } else if (s0 <= 0xef) {
2620c9274b6bSCho, Yu-Chen         /* three byte character */
2621c9274b6bSCho, Yu-Chen         l = 3;
2622c9274b6bSCho, Yu-Chen         if (ilen < 3) {
2623c9274b6bSCho, Yu-Chen             return 0;
2624c9274b6bSCho, Yu-Chen         }
2625c9274b6bSCho, Yu-Chen         s1 = cpu_ldub_data_ra(env, addr + 1, ra);
2626c9274b6bSCho, Yu-Chen         s2 = cpu_ldub_data_ra(env, addr + 2, ra);
2627c9274b6bSCho, Yu-Chen         c = s0 & 0x0f;
2628c9274b6bSCho, Yu-Chen         c = (c << 6) | (s1 & 0x3f);
2629c9274b6bSCho, Yu-Chen         c = (c << 6) | (s2 & 0x3f);
2630c9274b6bSCho, Yu-Chen         /* Fold the byte-by-byte range descriptions in the PoO into
2631c9274b6bSCho, Yu-Chen            tests against the complete value.  It disallows encodings
2632c9274b6bSCho, Yu-Chen            that could be smaller, and the UTF-16 surrogates.  */
2633c9274b6bSCho, Yu-Chen         if (enh_check
2634c9274b6bSCho, Yu-Chen             && ((s1 & 0xc0) != 0x80
2635c9274b6bSCho, Yu-Chen                 || (s2 & 0xc0) != 0x80
2636c9274b6bSCho, Yu-Chen                 || c < 0x1000
2637c9274b6bSCho, Yu-Chen                 || (c >= 0xd800 && c <= 0xdfff))) {
2638c9274b6bSCho, Yu-Chen             return 2;
2639c9274b6bSCho, Yu-Chen         }
2640c9274b6bSCho, Yu-Chen     } else if (s0 <= (enh_check ? 0xf4 : 0xf7)) {
2641c9274b6bSCho, Yu-Chen         /* four byte character */
2642c9274b6bSCho, Yu-Chen         l = 4;
2643c9274b6bSCho, Yu-Chen         if (ilen < 4) {
2644c9274b6bSCho, Yu-Chen             return 0;
2645c9274b6bSCho, Yu-Chen         }
2646c9274b6bSCho, Yu-Chen         s1 = cpu_ldub_data_ra(env, addr + 1, ra);
2647c9274b6bSCho, Yu-Chen         s2 = cpu_ldub_data_ra(env, addr + 2, ra);
2648c9274b6bSCho, Yu-Chen         s3 = cpu_ldub_data_ra(env, addr + 3, ra);
2649c9274b6bSCho, Yu-Chen         c = s0 & 0x07;
2650c9274b6bSCho, Yu-Chen         c = (c << 6) | (s1 & 0x3f);
2651c9274b6bSCho, Yu-Chen         c = (c << 6) | (s2 & 0x3f);
2652c9274b6bSCho, Yu-Chen         c = (c << 6) | (s3 & 0x3f);
2653c9274b6bSCho, Yu-Chen         /* See above.  */
2654c9274b6bSCho, Yu-Chen         if (enh_check
2655c9274b6bSCho, Yu-Chen             && ((s1 & 0xc0) != 0x80
2656c9274b6bSCho, Yu-Chen                 || (s2 & 0xc0) != 0x80
2657c9274b6bSCho, Yu-Chen                 || (s3 & 0xc0) != 0x80
2658c9274b6bSCho, Yu-Chen                 || c < 0x010000
2659c9274b6bSCho, Yu-Chen                 || c > 0x10ffff)) {
2660c9274b6bSCho, Yu-Chen             return 2;
2661c9274b6bSCho, Yu-Chen         }
2662c9274b6bSCho, Yu-Chen     } else {
2663c9274b6bSCho, Yu-Chen         /* invalid character */
2664c9274b6bSCho, Yu-Chen         return 2;
2665c9274b6bSCho, Yu-Chen     }
2666c9274b6bSCho, Yu-Chen 
2667c9274b6bSCho, Yu-Chen     *ochar = c;
2668c9274b6bSCho, Yu-Chen     *olen = l;
2669c9274b6bSCho, Yu-Chen     return -1;
2670c9274b6bSCho, Yu-Chen }
2671c9274b6bSCho, Yu-Chen 
decode_utf16(CPUS390XState * env,uint64_t addr,uint64_t ilen,bool enh_check,uintptr_t ra,uint32_t * ochar,uint32_t * olen)2672c9274b6bSCho, Yu-Chen static int decode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen,
2673c9274b6bSCho, Yu-Chen                         bool enh_check, uintptr_t ra,
2674c9274b6bSCho, Yu-Chen                         uint32_t *ochar, uint32_t *olen)
2675c9274b6bSCho, Yu-Chen {
2676c9274b6bSCho, Yu-Chen     uint16_t s0, s1;
2677c9274b6bSCho, Yu-Chen     uint32_t c, l;
2678c9274b6bSCho, Yu-Chen 
2679c9274b6bSCho, Yu-Chen     if (ilen < 2) {
2680c9274b6bSCho, Yu-Chen         return 0;
2681c9274b6bSCho, Yu-Chen     }
2682c9274b6bSCho, Yu-Chen     s0 = cpu_lduw_data_ra(env, addr, ra);
2683c9274b6bSCho, Yu-Chen     if ((s0 & 0xfc00) != 0xd800) {
2684c9274b6bSCho, Yu-Chen         /* one word character */
2685c9274b6bSCho, Yu-Chen         l = 2;
2686c9274b6bSCho, Yu-Chen         c = s0;
2687c9274b6bSCho, Yu-Chen     } else {
2688c9274b6bSCho, Yu-Chen         /* two word character */
2689c9274b6bSCho, Yu-Chen         l = 4;
2690c9274b6bSCho, Yu-Chen         if (ilen < 4) {
2691c9274b6bSCho, Yu-Chen             return 0;
2692c9274b6bSCho, Yu-Chen         }
2693c9274b6bSCho, Yu-Chen         s1 = cpu_lduw_data_ra(env, addr + 2, ra);
2694c9274b6bSCho, Yu-Chen         c = extract32(s0, 6, 4) + 1;
2695c9274b6bSCho, Yu-Chen         c = (c << 6) | (s0 & 0x3f);
2696c9274b6bSCho, Yu-Chen         c = (c << 10) | (s1 & 0x3ff);
2697c9274b6bSCho, Yu-Chen         if (enh_check && (s1 & 0xfc00) != 0xdc00) {
2698c9274b6bSCho, Yu-Chen             /* invalid surrogate character */
2699c9274b6bSCho, Yu-Chen             return 2;
2700c9274b6bSCho, Yu-Chen         }
2701c9274b6bSCho, Yu-Chen     }
2702c9274b6bSCho, Yu-Chen 
2703c9274b6bSCho, Yu-Chen     *ochar = c;
2704c9274b6bSCho, Yu-Chen     *olen = l;
2705c9274b6bSCho, Yu-Chen     return -1;
2706c9274b6bSCho, Yu-Chen }
2707c9274b6bSCho, Yu-Chen 
decode_utf32(CPUS390XState * env,uint64_t addr,uint64_t ilen,bool enh_check,uintptr_t ra,uint32_t * ochar,uint32_t * olen)2708c9274b6bSCho, Yu-Chen static int decode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen,
2709c9274b6bSCho, Yu-Chen                         bool enh_check, uintptr_t ra,
2710c9274b6bSCho, Yu-Chen                         uint32_t *ochar, uint32_t *olen)
2711c9274b6bSCho, Yu-Chen {
2712c9274b6bSCho, Yu-Chen     uint32_t c;
2713c9274b6bSCho, Yu-Chen 
2714c9274b6bSCho, Yu-Chen     if (ilen < 4) {
2715c9274b6bSCho, Yu-Chen         return 0;
2716c9274b6bSCho, Yu-Chen     }
2717c9274b6bSCho, Yu-Chen     c = cpu_ldl_data_ra(env, addr, ra);
2718c9274b6bSCho, Yu-Chen     if ((c >= 0xd800 && c <= 0xdbff) || c > 0x10ffff) {
2719c9274b6bSCho, Yu-Chen         /* invalid unicode character */
2720c9274b6bSCho, Yu-Chen         return 2;
2721c9274b6bSCho, Yu-Chen     }
2722c9274b6bSCho, Yu-Chen 
2723c9274b6bSCho, Yu-Chen     *ochar = c;
2724c9274b6bSCho, Yu-Chen     *olen = 4;
2725c9274b6bSCho, Yu-Chen     return -1;
2726c9274b6bSCho, Yu-Chen }
2727c9274b6bSCho, Yu-Chen 
encode_utf8(CPUS390XState * env,uint64_t addr,uint64_t ilen,uintptr_t ra,uint32_t c,uint32_t * olen)2728c9274b6bSCho, Yu-Chen static int encode_utf8(CPUS390XState *env, uint64_t addr, uint64_t ilen,
2729c9274b6bSCho, Yu-Chen                        uintptr_t ra, uint32_t c, uint32_t *olen)
2730c9274b6bSCho, Yu-Chen {
2731c9274b6bSCho, Yu-Chen     uint8_t d[4];
2732c9274b6bSCho, Yu-Chen     uint32_t l, i;
2733c9274b6bSCho, Yu-Chen 
2734c9274b6bSCho, Yu-Chen     if (c <= 0x7f) {
2735c9274b6bSCho, Yu-Chen         /* one byte character */
2736c9274b6bSCho, Yu-Chen         l = 1;
2737c9274b6bSCho, Yu-Chen         d[0] = c;
2738c9274b6bSCho, Yu-Chen     } else if (c <= 0x7ff) {
2739c9274b6bSCho, Yu-Chen         /* two byte character */
2740c9274b6bSCho, Yu-Chen         l = 2;
2741c9274b6bSCho, Yu-Chen         d[1] = 0x80 | extract32(c, 0, 6);
2742c9274b6bSCho, Yu-Chen         d[0] = 0xc0 | extract32(c, 6, 5);
2743c9274b6bSCho, Yu-Chen     } else if (c <= 0xffff) {
2744c9274b6bSCho, Yu-Chen         /* three byte character */
2745c9274b6bSCho, Yu-Chen         l = 3;
2746c9274b6bSCho, Yu-Chen         d[2] = 0x80 | extract32(c, 0, 6);
2747c9274b6bSCho, Yu-Chen         d[1] = 0x80 | extract32(c, 6, 6);
2748c9274b6bSCho, Yu-Chen         d[0] = 0xe0 | extract32(c, 12, 4);
2749c9274b6bSCho, Yu-Chen     } else {
2750c9274b6bSCho, Yu-Chen         /* four byte character */
2751c9274b6bSCho, Yu-Chen         l = 4;
2752c9274b6bSCho, Yu-Chen         d[3] = 0x80 | extract32(c, 0, 6);
2753c9274b6bSCho, Yu-Chen         d[2] = 0x80 | extract32(c, 6, 6);
2754c9274b6bSCho, Yu-Chen         d[1] = 0x80 | extract32(c, 12, 6);
2755c9274b6bSCho, Yu-Chen         d[0] = 0xf0 | extract32(c, 18, 3);
2756c9274b6bSCho, Yu-Chen     }
2757c9274b6bSCho, Yu-Chen 
2758c9274b6bSCho, Yu-Chen     if (ilen < l) {
2759c9274b6bSCho, Yu-Chen         return 1;
2760c9274b6bSCho, Yu-Chen     }
2761c9274b6bSCho, Yu-Chen     for (i = 0; i < l; ++i) {
2762c9274b6bSCho, Yu-Chen         cpu_stb_data_ra(env, addr + i, d[i], ra);
2763c9274b6bSCho, Yu-Chen     }
2764c9274b6bSCho, Yu-Chen 
2765c9274b6bSCho, Yu-Chen     *olen = l;
2766c9274b6bSCho, Yu-Chen     return -1;
2767c9274b6bSCho, Yu-Chen }
2768c9274b6bSCho, Yu-Chen 
encode_utf16(CPUS390XState * env,uint64_t addr,uint64_t ilen,uintptr_t ra,uint32_t c,uint32_t * olen)2769c9274b6bSCho, Yu-Chen static int encode_utf16(CPUS390XState *env, uint64_t addr, uint64_t ilen,
2770c9274b6bSCho, Yu-Chen                         uintptr_t ra, uint32_t c, uint32_t *olen)
2771c9274b6bSCho, Yu-Chen {
2772c9274b6bSCho, Yu-Chen     uint16_t d0, d1;
2773c9274b6bSCho, Yu-Chen 
2774c9274b6bSCho, Yu-Chen     if (c <= 0xffff) {
2775c9274b6bSCho, Yu-Chen         /* one word character */
2776c9274b6bSCho, Yu-Chen         if (ilen < 2) {
2777c9274b6bSCho, Yu-Chen             return 1;
2778c9274b6bSCho, Yu-Chen         }
2779c9274b6bSCho, Yu-Chen         cpu_stw_data_ra(env, addr, c, ra);
2780c9274b6bSCho, Yu-Chen         *olen = 2;
2781c9274b6bSCho, Yu-Chen     } else {
2782c9274b6bSCho, Yu-Chen         /* two word character */
2783c9274b6bSCho, Yu-Chen         if (ilen < 4) {
2784c9274b6bSCho, Yu-Chen             return 1;
2785c9274b6bSCho, Yu-Chen         }
2786c9274b6bSCho, Yu-Chen         d1 = 0xdc00 | extract32(c, 0, 10);
2787c9274b6bSCho, Yu-Chen         d0 = 0xd800 | extract32(c, 10, 6);
2788c9274b6bSCho, Yu-Chen         d0 = deposit32(d0, 6, 4, extract32(c, 16, 5) - 1);
2789c9274b6bSCho, Yu-Chen         cpu_stw_data_ra(env, addr + 0, d0, ra);
2790c9274b6bSCho, Yu-Chen         cpu_stw_data_ra(env, addr + 2, d1, ra);
2791c9274b6bSCho, Yu-Chen         *olen = 4;
2792c9274b6bSCho, Yu-Chen     }
2793c9274b6bSCho, Yu-Chen 
2794c9274b6bSCho, Yu-Chen     return -1;
2795c9274b6bSCho, Yu-Chen }
2796c9274b6bSCho, Yu-Chen 
encode_utf32(CPUS390XState * env,uint64_t addr,uint64_t ilen,uintptr_t ra,uint32_t c,uint32_t * olen)2797c9274b6bSCho, Yu-Chen static int encode_utf32(CPUS390XState *env, uint64_t addr, uint64_t ilen,
2798c9274b6bSCho, Yu-Chen                         uintptr_t ra, uint32_t c, uint32_t *olen)
2799c9274b6bSCho, Yu-Chen {
2800c9274b6bSCho, Yu-Chen     if (ilen < 4) {
2801c9274b6bSCho, Yu-Chen         return 1;
2802c9274b6bSCho, Yu-Chen     }
2803c9274b6bSCho, Yu-Chen     cpu_stl_data_ra(env, addr, c, ra);
2804c9274b6bSCho, Yu-Chen     *olen = 4;
2805c9274b6bSCho, Yu-Chen     return -1;
2806c9274b6bSCho, Yu-Chen }
2807c9274b6bSCho, Yu-Chen 
convert_unicode(CPUS390XState * env,uint32_t r1,uint32_t r2,uint32_t m3,uintptr_t ra,decode_unicode_fn decode,encode_unicode_fn encode)2808c9274b6bSCho, Yu-Chen static inline uint32_t convert_unicode(CPUS390XState *env, uint32_t r1,
2809c9274b6bSCho, Yu-Chen                                        uint32_t r2, uint32_t m3, uintptr_t ra,
2810c9274b6bSCho, Yu-Chen                                        decode_unicode_fn decode,
2811c9274b6bSCho, Yu-Chen                                        encode_unicode_fn encode)
2812c9274b6bSCho, Yu-Chen {
2813c9274b6bSCho, Yu-Chen     uint64_t dst = get_address(env, r1);
2814c9274b6bSCho, Yu-Chen     uint64_t dlen = get_length(env, r1 + 1);
2815c9274b6bSCho, Yu-Chen     uint64_t src = get_address(env, r2);
2816c9274b6bSCho, Yu-Chen     uint64_t slen = get_length(env, r2 + 1);
2817c9274b6bSCho, Yu-Chen     bool enh_check = m3 & 1;
2818c9274b6bSCho, Yu-Chen     int cc, i;
2819c9274b6bSCho, Yu-Chen 
2820c9274b6bSCho, Yu-Chen     /* Lest we fail to service interrupts in a timely manner, limit the
2821c9274b6bSCho, Yu-Chen        amount of work we're willing to do.  For now, let's cap at 256.  */
2822c9274b6bSCho, Yu-Chen     for (i = 0; i < 256; ++i) {
2823c9274b6bSCho, Yu-Chen         uint32_t c, ilen, olen;
2824c9274b6bSCho, Yu-Chen 
2825c9274b6bSCho, Yu-Chen         cc = decode(env, src, slen, enh_check, ra, &c, &ilen);
2826c9274b6bSCho, Yu-Chen         if (unlikely(cc >= 0)) {
2827c9274b6bSCho, Yu-Chen             break;
2828c9274b6bSCho, Yu-Chen         }
2829c9274b6bSCho, Yu-Chen         cc = encode(env, dst, dlen, ra, c, &olen);
2830c9274b6bSCho, Yu-Chen         if (unlikely(cc >= 0)) {
2831c9274b6bSCho, Yu-Chen             break;
2832c9274b6bSCho, Yu-Chen         }
2833c9274b6bSCho, Yu-Chen 
2834c9274b6bSCho, Yu-Chen         src += ilen;
2835c9274b6bSCho, Yu-Chen         slen -= ilen;
2836c9274b6bSCho, Yu-Chen         dst += olen;
2837c9274b6bSCho, Yu-Chen         dlen -= olen;
2838c9274b6bSCho, Yu-Chen         cc = 3;
2839c9274b6bSCho, Yu-Chen     }
2840c9274b6bSCho, Yu-Chen 
2841c9274b6bSCho, Yu-Chen     set_address(env, r1, dst);
2842c9274b6bSCho, Yu-Chen     set_length(env, r1 + 1, dlen);
2843c9274b6bSCho, Yu-Chen     set_address(env, r2, src);
2844c9274b6bSCho, Yu-Chen     set_length(env, r2 + 1, slen);
2845c9274b6bSCho, Yu-Chen 
2846c9274b6bSCho, Yu-Chen     return cc;
2847c9274b6bSCho, Yu-Chen }
2848c9274b6bSCho, Yu-Chen 
HELPER(cu12)2849c9274b6bSCho, Yu-Chen uint32_t HELPER(cu12)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
2850c9274b6bSCho, Yu-Chen {
2851c9274b6bSCho, Yu-Chen     return convert_unicode(env, r1, r2, m3, GETPC(),
2852c9274b6bSCho, Yu-Chen                            decode_utf8, encode_utf16);
2853c9274b6bSCho, Yu-Chen }
2854c9274b6bSCho, Yu-Chen 
HELPER(cu14)2855c9274b6bSCho, Yu-Chen uint32_t HELPER(cu14)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
2856c9274b6bSCho, Yu-Chen {
2857c9274b6bSCho, Yu-Chen     return convert_unicode(env, r1, r2, m3, GETPC(),
2858c9274b6bSCho, Yu-Chen                            decode_utf8, encode_utf32);
2859c9274b6bSCho, Yu-Chen }
2860c9274b6bSCho, Yu-Chen 
HELPER(cu21)2861c9274b6bSCho, Yu-Chen uint32_t HELPER(cu21)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
2862c9274b6bSCho, Yu-Chen {
2863c9274b6bSCho, Yu-Chen     return convert_unicode(env, r1, r2, m3, GETPC(),
2864c9274b6bSCho, Yu-Chen                            decode_utf16, encode_utf8);
2865c9274b6bSCho, Yu-Chen }
2866c9274b6bSCho, Yu-Chen 
HELPER(cu24)2867c9274b6bSCho, Yu-Chen uint32_t HELPER(cu24)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
2868c9274b6bSCho, Yu-Chen {
2869c9274b6bSCho, Yu-Chen     return convert_unicode(env, r1, r2, m3, GETPC(),
2870c9274b6bSCho, Yu-Chen                            decode_utf16, encode_utf32);
2871c9274b6bSCho, Yu-Chen }
2872c9274b6bSCho, Yu-Chen 
HELPER(cu41)2873c9274b6bSCho, Yu-Chen uint32_t HELPER(cu41)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
2874c9274b6bSCho, Yu-Chen {
2875c9274b6bSCho, Yu-Chen     return convert_unicode(env, r1, r2, m3, GETPC(),
2876c9274b6bSCho, Yu-Chen                            decode_utf32, encode_utf8);
2877c9274b6bSCho, Yu-Chen }
2878c9274b6bSCho, Yu-Chen 
HELPER(cu42)2879c9274b6bSCho, Yu-Chen uint32_t HELPER(cu42)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3)
2880c9274b6bSCho, Yu-Chen {
2881c9274b6bSCho, Yu-Chen     return convert_unicode(env, r1, r2, m3, GETPC(),
2882c9274b6bSCho, Yu-Chen                            decode_utf32, encode_utf16);
2883c9274b6bSCho, Yu-Chen }
2884c9274b6bSCho, Yu-Chen 
probe_write_access(CPUS390XState * env,uint64_t addr,uint64_t len,uintptr_t ra)2885c9274b6bSCho, Yu-Chen void probe_write_access(CPUS390XState *env, uint64_t addr, uint64_t len,
2886c9274b6bSCho, Yu-Chen                         uintptr_t ra)
2887c9274b6bSCho, Yu-Chen {
288890b7022eSRichard Henderson     const int mmu_idx = s390x_env_mmu_index(env, false);
288990b7022eSRichard Henderson 
2890c9274b6bSCho, Yu-Chen     /* test the actual access, not just any access to the page due to LAP */
2891c9274b6bSCho, Yu-Chen     while (len) {
2892c9274b6bSCho, Yu-Chen         const uint64_t pagelen = -(addr | TARGET_PAGE_MASK);
2893c9274b6bSCho, Yu-Chen         const uint64_t curlen = MIN(pagelen, len);
2894c9274b6bSCho, Yu-Chen 
289590b7022eSRichard Henderson         probe_write(env, addr, curlen, mmu_idx, ra);
2896c9274b6bSCho, Yu-Chen         addr = wrap_address(env, addr + curlen);
2897c9274b6bSCho, Yu-Chen         len -= curlen;
2898c9274b6bSCho, Yu-Chen     }
2899c9274b6bSCho, Yu-Chen }
2900c9274b6bSCho, Yu-Chen 
HELPER(probe_write_access)2901c9274b6bSCho, Yu-Chen void HELPER(probe_write_access)(CPUS390XState *env, uint64_t addr, uint64_t len)
2902c9274b6bSCho, Yu-Chen {
2903c9274b6bSCho, Yu-Chen     probe_write_access(env, addr, len, GETPC());
2904c9274b6bSCho, Yu-Chen }
2905