]> git.proxmox.com Git - mirror_qemu.git/blame - target-s390x/mem_helper.c
cpu: move exec-all.h inclusion out of cpu.h
[mirror_qemu.git] / target-s390x / mem_helper.c
CommitLineData
8ef7f78e
BS
1/*
2 * S/390 memory access helper routines
3 *
4 * Copyright (c) 2009 Ulrich Hecht
5 * Copyright (c) 2009 Alexander Graf
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19 */
20
9615495a 21#include "qemu/osdep.h"
8ef7f78e 22#include "cpu.h"
2ef6175a 23#include "exec/helper-proto.h"
63c91552 24#include "exec/exec-all.h"
f08b6170 25#include "exec/cpu_ldst.h"
741da0d3
PB
26
27#if !defined(CONFIG_USER_ONLY)
0f5f6691 28#include "hw/s390x/storage-keys.h"
741da0d3 29#endif
8ef7f78e
BS
30
31/*****************************************************************************/
32/* Softmmu support */
33#if !defined(CONFIG_USER_ONLY)
8ef7f78e 34
8ef7f78e
BS
35/* try to fill the TLB and return an exception if error. If retaddr is
36 NULL, it means that the function was called in C code (i.e. not
37 from generated code or from helper.c) */
38/* XXX: fix it to restore all registers */
d5a11fef 39void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
8ef7f78e
BS
40 uintptr_t retaddr)
41{
8ef7f78e
BS
42 int ret;
43
d5a11fef 44 ret = s390_cpu_handle_mmu_fault(cs, addr, is_write, mmu_idx);
8ef7f78e
BS
45 if (unlikely(ret != 0)) {
46 if (likely(retaddr)) {
47 /* now we have a real cpu fault */
3f38f309 48 cpu_restore_state(cs, retaddr);
8ef7f78e 49 }
5638d180 50 cpu_loop_exit(cs);
8ef7f78e 51 }
8ef7f78e
BS
52}
53
54#endif
55
56/* #define DEBUG_HELPER */
57#ifdef DEBUG_HELPER
58#define HELPER_LOG(x...) qemu_log(x)
59#else
60#define HELPER_LOG(x...)
61#endif
62
d7ce6b7a
AJ
63/* Reduce the length so that addr + len doesn't cross a page boundary. */
64static inline uint64_t adj_len_to_page(uint64_t len, uint64_t addr)
65{
66#ifndef CONFIG_USER_ONLY
67 if ((addr & ~TARGET_PAGE_MASK) + len - 1 >= TARGET_PAGE_SIZE) {
68 return -addr & ~TARGET_PAGE_MASK;
69 }
70#endif
71 return len;
72}
73
fc89efe6
AJ
74static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte,
75 uint32_t l)
8ef7f78e 76{
97ed5ccd 77 int mmu_idx = cpu_mmu_index(env, false);
fc89efe6
AJ
78
79 while (l > 0) {
80 void *p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
81 if (p) {
82 /* Access to the whole page in write mode granted. */
83 int l_adj = adj_len_to_page(l, dest);
84 memset(p, byte, l_adj);
85 dest += l_adj;
86 l -= l_adj;
87 } else {
88 /* We failed to get access to the whole page. The next write
89 access will likely fill the QEMU TLB for the next iteration. */
90 cpu_stb_data(env, dest, byte);
91 dest++;
92 l--;
93 }
8ef7f78e 94 }
8ef7f78e
BS
95}
96
6da528d1
AJ
97static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src,
98 uint32_t l)
8ef7f78e 99{
97ed5ccd 100 int mmu_idx = cpu_mmu_index(env, false);
8ef7f78e 101
6da528d1
AJ
102 while (l > 0) {
103 void *src_p = tlb_vaddr_to_host(env, src, MMU_DATA_LOAD, mmu_idx);
104 void *dest_p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
105 if (src_p && dest_p) {
106 /* Access to both whole pages granted. */
107 int l_adj = adj_len_to_page(l, src);
108 l_adj = adj_len_to_page(l_adj, dest);
109 memmove(dest_p, src_p, l_adj);
110 src += l_adj;
111 dest += l_adj;
112 l -= l_adj;
113 } else {
114 /* We failed to get access to one or both whole pages. The next
115 read or write access will likely fill the QEMU TLB for the
116 next iteration. */
117 cpu_stb_data(env, dest, cpu_ldub_data(env, src));
118 src++;
119 dest++;
120 l--;
121 }
8ef7f78e 122 }
8ef7f78e 123}
8ef7f78e
BS
124
125/* and on array */
19b0516f
BS
126uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
127 uint64_t src)
8ef7f78e
BS
128{
129 int i;
130 unsigned char x;
131 uint32_t cc = 0;
132
133 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
134 __func__, l, dest, src);
135 for (i = 0; i <= l; i++) {
19b0516f 136 x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
8ef7f78e
BS
137 if (x) {
138 cc = 1;
139 }
19b0516f 140 cpu_stb_data(env, dest + i, x);
8ef7f78e
BS
141 }
142 return cc;
143}
144
145/* xor on array */
19b0516f
BS
146uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
147 uint64_t src)
8ef7f78e
BS
148{
149 int i;
150 unsigned char x;
151 uint32_t cc = 0;
152
153 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
154 __func__, l, dest, src);
155
8ef7f78e 156 /* xor with itself is the same as memset(0) */
8ef7f78e 157 if (src == dest) {
fc89efe6 158 fast_memset(env, dest, 0, l + 1);
8ef7f78e
BS
159 return 0;
160 }
8ef7f78e
BS
161
162 for (i = 0; i <= l; i++) {
19b0516f 163 x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
8ef7f78e
BS
164 if (x) {
165 cc = 1;
166 }
19b0516f 167 cpu_stb_data(env, dest + i, x);
8ef7f78e
BS
168 }
169 return cc;
170}
171
172/* or on array */
19b0516f
BS
173uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
174 uint64_t src)
8ef7f78e
BS
175{
176 int i;
177 unsigned char x;
178 uint32_t cc = 0;
179
180 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
181 __func__, l, dest, src);
182 for (i = 0; i <= l; i++) {
19b0516f 183 x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
8ef7f78e
BS
184 if (x) {
185 cc = 1;
186 }
19b0516f 187 cpu_stb_data(env, dest + i, x);
8ef7f78e
BS
188 }
189 return cc;
190}
191
192/* memmove */
19b0516f 193void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
8ef7f78e
BS
194{
195 int i = 0;
8ef7f78e
BS
196
197 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
198 __func__, l, dest, src);
199
fc89efe6
AJ
200 /* mvc with source pointing to the byte after the destination is the
201 same as memset with the first source byte */
202 if (dest == (src + 1)) {
203 fast_memset(env, dest, cpu_ldub_data(env, src), l + 1);
204 return;
205 }
6da528d1 206
068593de 207 /* mvc and memmove do not behave the same when areas overlap! */
fc89efe6 208 if ((dest < src) || (src + l < dest)) {
6da528d1 209 fast_memmove(env, dest, src, l + 1);
8ef7f78e
BS
210 return;
211 }
8ef7f78e 212
068593de 213 /* slow version with byte accesses which always work */
6da528d1 214 for (i = 0; i <= l; i++) {
19b0516f 215 cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
8ef7f78e
BS
216 }
217}
218
219/* compare unsigned byte arrays */
19b0516f 220uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
8ef7f78e
BS
221{
222 int i;
223 unsigned char x, y;
224 uint32_t cc;
225
226 HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
227 __func__, l, s1, s2);
228 for (i = 0; i <= l; i++) {
19b0516f
BS
229 x = cpu_ldub_data(env, s1 + i);
230 y = cpu_ldub_data(env, s2 + i);
8ef7f78e
BS
231 HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
232 if (x < y) {
233 cc = 1;
234 goto done;
235 } else if (x > y) {
236 cc = 2;
237 goto done;
238 }
239 }
240 cc = 0;
241 done:
242 HELPER_LOG("\n");
243 return cc;
244}
245
246/* compare logical under mask */
19b0516f
BS
247uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
248 uint64_t addr)
8ef7f78e
BS
249{
250 uint8_t r, d;
251 uint32_t cc;
252
253 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
254 mask, addr);
255 cc = 0;
256 while (mask) {
257 if (mask & 8) {
19b0516f 258 d = cpu_ldub_data(env, addr);
8ef7f78e
BS
259 r = (r1 & 0xff000000UL) >> 24;
260 HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
261 addr);
262 if (r < d) {
263 cc = 1;
264 break;
265 } else if (r > d) {
266 cc = 2;
267 break;
268 }
269 addr++;
270 }
271 mask = (mask << 1) & 0xf;
272 r1 <<= 8;
273 }
274 HELPER_LOG("\n");
275 return cc;
276}
277
aa31bf60
RH
278static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
279{
280 /* 31-Bit mode */
281 if (!(env->psw.mask & PSW_MASK_64)) {
282 a &= 0x7fffffff;
283 }
284 return a;
285}
286
19b0516f 287static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
8ef7f78e
BS
288{
289 uint64_t r = d2;
8ef7f78e
BS
290 if (x2) {
291 r += env->regs[x2];
292 }
8ef7f78e
BS
293 if (b2) {
294 r += env->regs[b2];
295 }
aa31bf60 296 return fix_address(env, r);
8ef7f78e
BS
297}
298
19b0516f 299static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
8ef7f78e 300{
aa31bf60 301 return fix_address(env, env->regs[reg]);
8ef7f78e
BS
302}
303
304/* search string (c is byte to search, r2 is string, r1 end of string) */
4600c994
RH
305uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
306 uint64_t str)
8ef7f78e 307{
4600c994
RH
308 uint32_t len;
309 uint8_t v, c = r0;
8ef7f78e 310
4600c994
RH
311 str = fix_address(env, str);
312 end = fix_address(env, end);
8ef7f78e 313
4600c994
RH
314 /* Assume for now that R2 is unmodified. */
315 env->retxl = str;
316
317 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 318 amount of work we're willing to do. For now, let's cap at 8k. */
4600c994
RH
319 for (len = 0; len < 0x2000; ++len) {
320 if (str + len == end) {
321 /* Character not found. R1 & R2 are unmodified. */
322 env->cc_op = 2;
323 return end;
324 }
325 v = cpu_ldub_data(env, str + len);
326 if (v == c) {
327 /* Character found. Set R1 to the location; R2 is unmodified. */
328 env->cc_op = 1;
329 return str + len;
8ef7f78e
BS
330 }
331 }
332
4600c994
RH
333 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
334 env->retxl = str + len;
335 env->cc_op = 3;
336 return end;
8ef7f78e
BS
337}
338
339/* unsigned string compare (c is string terminator) */
aa31bf60 340uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
8ef7f78e 341{
aa31bf60 342 uint32_t len;
8ef7f78e
BS
343
344 c = c & 0xff;
aa31bf60
RH
345 s1 = fix_address(env, s1);
346 s2 = fix_address(env, s2);
347
348 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 349 amount of work we're willing to do. For now, let's cap at 8k. */
aa31bf60
RH
350 for (len = 0; len < 0x2000; ++len) {
351 uint8_t v1 = cpu_ldub_data(env, s1 + len);
352 uint8_t v2 = cpu_ldub_data(env, s2 + len);
353 if (v1 == v2) {
354 if (v1 == c) {
355 /* Equal. CC=0, and don't advance the registers. */
356 env->cc_op = 0;
357 env->retxl = s2;
358 return s1;
359 }
360 } else {
361 /* Unequal. CC={1,2}, and advance the registers. Note that
362 the terminator need not be zero, but the string that contains
363 the terminator is by definition "low". */
364 env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
365 env->retxl = s2 + len;
366 return s1 + len;
8ef7f78e 367 }
8ef7f78e
BS
368 }
369
aa31bf60
RH
370 /* CPU-determined bytes equal; advance the registers. */
371 env->cc_op = 3;
372 env->retxl = s2 + len;
373 return s1 + len;
8ef7f78e
BS
374}
375
376/* move page */
19b0516f 377void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
8ef7f78e
BS
378{
379 /* XXX missing r0 handling */
ee6c38d5 380 env->cc_op = 0;
6da528d1 381 fast_memmove(env, r1, r2, TARGET_PAGE_SIZE);
8ef7f78e
BS
382}
383
384/* string copy (c is string terminator) */
aa31bf60 385uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
8ef7f78e 386{
aa31bf60 387 uint32_t len;
8ef7f78e
BS
388
389 c = c & 0xff;
aa31bf60
RH
390 d = fix_address(env, d);
391 s = fix_address(env, s);
392
393 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 394 amount of work we're willing to do. For now, let's cap at 8k. */
aa31bf60
RH
395 for (len = 0; len < 0x2000; ++len) {
396 uint8_t v = cpu_ldub_data(env, s + len);
397 cpu_stb_data(env, d + len, v);
8ef7f78e 398 if (v == c) {
aa31bf60
RH
399 /* Complete. Set CC=1 and advance R1. */
400 env->cc_op = 1;
401 env->retxl = s;
402 return d + len;
8ef7f78e 403 }
8ef7f78e 404 }
aa31bf60
RH
405
406 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
407 env->cc_op = 3;
408 env->retxl = s + len;
409 return d + len;
8ef7f78e
BS
410}
411
19b0516f
BS
412static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
413 uint32_t mask)
8ef7f78e
BS
414{
415 int pos = 24; /* top of the lower half of r1 */
416 uint64_t rmask = 0xff000000ULL;
417 uint8_t val = 0;
418 int ccd = 0;
419 uint32_t cc = 0;
420
421 while (mask) {
422 if (mask & 8) {
423 env->regs[r1] &= ~rmask;
19b0516f 424 val = cpu_ldub_data(env, address);
8ef7f78e
BS
425 if ((val & 0x80) && !ccd) {
426 cc = 1;
427 }
428 ccd = 1;
429 if (val && cc == 0) {
430 cc = 2;
431 }
432 env->regs[r1] |= (uint64_t)val << pos;
433 address++;
434 }
435 mask = (mask << 1) & 0xf;
436 pos -= 8;
437 rmask >>= 8;
438 }
439
440 return cc;
441}
442
443/* execute instruction
444 this instruction executes an insn modified with the contents of r1
445 it does not change the executed instruction in memory
446 it does not change the program counter
447 in other words: tricky...
448 currently implemented by interpreting the cases it is most commonly used in
449*/
19b0516f
BS
450uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
451 uint64_t addr, uint64_t ret)
8ef7f78e 452{
a47dddd7 453 S390CPU *cpu = s390_env_get_cpu(env);
19b0516f 454 uint16_t insn = cpu_lduw_code(env, addr);
8ef7f78e
BS
455
456 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
457 insn);
458 if ((insn & 0xf0ff) == 0xd000) {
459 uint32_t l, insn2, b1, b2, d1, d2;
460
461 l = v1 & 0xff;
19b0516f 462 insn2 = cpu_ldl_code(env, addr + 2);
8ef7f78e
BS
463 b1 = (insn2 >> 28) & 0xf;
464 b2 = (insn2 >> 12) & 0xf;
465 d1 = (insn2 >> 16) & 0xfff;
466 d2 = insn2 & 0xfff;
467 switch (insn & 0xf00) {
468 case 0x200:
19b0516f
BS
469 helper_mvc(env, l, get_address(env, 0, b1, d1),
470 get_address(env, 0, b2, d2));
8ef7f78e 471 break;
8cf02f93
PB
472 case 0x400:
473 cc = helper_nc(env, l, get_address(env, 0, b1, d1),
474 get_address(env, 0, b2, d2));
475 break;
8ef7f78e 476 case 0x500:
19b0516f
BS
477 cc = helper_clc(env, l, get_address(env, 0, b1, d1),
478 get_address(env, 0, b2, d2));
8ef7f78e 479 break;
8cf02f93
PB
480 case 0x600:
481 cc = helper_oc(env, l, get_address(env, 0, b1, d1),
482 get_address(env, 0, b2, d2));
483 break;
8ef7f78e 484 case 0x700:
19b0516f
BS
485 cc = helper_xc(env, l, get_address(env, 0, b1, d1),
486 get_address(env, 0, b2, d2));
8ef7f78e
BS
487 break;
488 case 0xc00:
19b0516f
BS
489 helper_tr(env, l, get_address(env, 0, b1, d1),
490 get_address(env, 0, b2, d2));
c9c19b49 491 break;
54f00775
AJ
492 case 0xd00:
493 cc = helper_trt(env, l, get_address(env, 0, b1, d1),
494 get_address(env, 0, b2, d2));
8ef7f78e
BS
495 break;
496 default:
497 goto abort;
8ef7f78e
BS
498 }
499 } else if ((insn & 0xff00) == 0x0a00) {
500 /* supervisor call */
501 HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
502 env->psw.addr = ret - 4;
503 env->int_svc_code = (insn | v1) & 0xff;
d5a103cd 504 env->int_svc_ilen = 4;
089f5c06 505 helper_exception(env, EXCP_SVC);
8ef7f78e
BS
506 } else if ((insn & 0xff00) == 0xbf00) {
507 uint32_t insn2, r1, r3, b2, d2;
508
19b0516f 509 insn2 = cpu_ldl_code(env, addr + 2);
8ef7f78e
BS
510 r1 = (insn2 >> 20) & 0xf;
511 r3 = (insn2 >> 16) & 0xf;
512 b2 = (insn2 >> 12) & 0xf;
513 d2 = insn2 & 0xfff;
19b0516f 514 cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
8ef7f78e
BS
515 } else {
516 abort:
a47dddd7 517 cpu_abort(CPU(cpu), "EXECUTE on instruction prefix 0x%x not implemented\n",
8ef7f78e
BS
518 insn);
519 }
520 return cc;
521}
522
8ef7f78e 523/* load access registers r1 to r3 from memory at a2 */
19b0516f 524void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
525{
526 int i;
527
528 for (i = r1;; i = (i + 1) % 16) {
19b0516f 529 env->aregs[i] = cpu_ldl_data(env, a2);
8ef7f78e
BS
530 a2 += 4;
531
532 if (i == r3) {
533 break;
534 }
535 }
536}
537
538/* store access registers r1 to r3 in memory at a2 */
19b0516f 539void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
540{
541 int i;
542
543 for (i = r1;; i = (i + 1) % 16) {
19b0516f 544 cpu_stl_data(env, a2, env->aregs[i]);
8ef7f78e
BS
545 a2 += 4;
546
547 if (i == r3) {
548 break;
549 }
550 }
551}
552
553/* move long */
19b0516f 554uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
8ef7f78e
BS
555{
556 uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
19b0516f 557 uint64_t dest = get_address_31fix(env, r1);
8ef7f78e 558 uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
19b0516f 559 uint64_t src = get_address_31fix(env, r2);
b5edcddd 560 uint8_t pad = env->regs[r2 + 1] >> 24;
8ef7f78e
BS
561 uint8_t v;
562 uint32_t cc;
563
564 if (destlen == srclen) {
565 cc = 0;
566 } else if (destlen < srclen) {
567 cc = 1;
568 } else {
569 cc = 2;
570 }
571
572 if (srclen > destlen) {
573 srclen = destlen;
574 }
575
576 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
19b0516f
BS
577 v = cpu_ldub_data(env, src);
578 cpu_stb_data(env, dest, v);
8ef7f78e
BS
579 }
580
581 for (; destlen; dest++, destlen--) {
19b0516f 582 cpu_stb_data(env, dest, pad);
8ef7f78e
BS
583 }
584
585 env->regs[r1 + 1] = destlen;
586 /* can't use srclen here, we trunc'ed it */
587 env->regs[r2 + 1] -= src - env->regs[r2];
588 env->regs[r1] = dest;
589 env->regs[r2] = src;
590
591 return cc;
592}
593
594/* move long extended another memcopy insn with more bells and whistles */
19b0516f
BS
595uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
596 uint32_t r3)
8ef7f78e
BS
597{
598 uint64_t destlen = env->regs[r1 + 1];
599 uint64_t dest = env->regs[r1];
600 uint64_t srclen = env->regs[r3 + 1];
601 uint64_t src = env->regs[r3];
602 uint8_t pad = a2 & 0xff;
603 uint8_t v;
604 uint32_t cc;
605
606 if (!(env->psw.mask & PSW_MASK_64)) {
607 destlen = (uint32_t)destlen;
608 srclen = (uint32_t)srclen;
609 dest &= 0x7fffffff;
610 src &= 0x7fffffff;
611 }
612
613 if (destlen == srclen) {
614 cc = 0;
615 } else if (destlen < srclen) {
616 cc = 1;
617 } else {
618 cc = 2;
619 }
620
621 if (srclen > destlen) {
622 srclen = destlen;
623 }
624
625 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
19b0516f
BS
626 v = cpu_ldub_data(env, src);
627 cpu_stb_data(env, dest, v);
8ef7f78e
BS
628 }
629
630 for (; destlen; dest++, destlen--) {
19b0516f 631 cpu_stb_data(env, dest, pad);
8ef7f78e
BS
632 }
633
634 env->regs[r1 + 1] = destlen;
635 /* can't use srclen here, we trunc'ed it */
636 /* FIXME: 31-bit mode! */
637 env->regs[r3 + 1] -= src - env->regs[r3];
638 env->regs[r1] = dest;
639 env->regs[r3] = src;
640
641 return cc;
642}
643
644/* compare logical long extended memcompare insn with padding */
19b0516f
BS
645uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
646 uint32_t r3)
8ef7f78e
BS
647{
648 uint64_t destlen = env->regs[r1 + 1];
19b0516f 649 uint64_t dest = get_address_31fix(env, r1);
8ef7f78e 650 uint64_t srclen = env->regs[r3 + 1];
19b0516f 651 uint64_t src = get_address_31fix(env, r3);
8ef7f78e
BS
652 uint8_t pad = a2 & 0xff;
653 uint8_t v1 = 0, v2 = 0;
654 uint32_t cc = 0;
655
656 if (!(destlen || srclen)) {
657 return cc;
658 }
659
660 if (srclen > destlen) {
661 srclen = destlen;
662 }
663
664 for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
19b0516f
BS
665 v1 = srclen ? cpu_ldub_data(env, src) : pad;
666 v2 = destlen ? cpu_ldub_data(env, dest) : pad;
8ef7f78e
BS
667 if (v1 != v2) {
668 cc = (v1 < v2) ? 1 : 2;
669 break;
670 }
671 }
672
673 env->regs[r1 + 1] = destlen;
674 /* can't use srclen here, we trunc'ed it */
675 env->regs[r3 + 1] -= src - env->regs[r3];
676 env->regs[r1] = dest;
677 env->regs[r3] = src;
678
679 return cc;
680}
681
682/* checksum */
374724f9
RH
683uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
684 uint64_t src, uint64_t src_len)
8ef7f78e 685{
374724f9
RH
686 uint64_t max_len, len;
687 uint64_t cksm = (uint32_t)r1;
8ef7f78e 688
374724f9 689 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 690 amount of work we're willing to do. For now, let's cap at 8k. */
374724f9 691 max_len = (src_len > 0x2000 ? 0x2000 : src_len);
8ef7f78e 692
374724f9
RH
693 /* Process full words as available. */
694 for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
695 cksm += (uint32_t)cpu_ldl_data(env, src);
8ef7f78e
BS
696 }
697
374724f9 698 switch (max_len - len) {
8ef7f78e 699 case 1:
19b0516f 700 cksm += cpu_ldub_data(env, src) << 24;
374724f9 701 len += 1;
8ef7f78e
BS
702 break;
703 case 2:
19b0516f 704 cksm += cpu_lduw_data(env, src) << 16;
374724f9 705 len += 2;
8ef7f78e
BS
706 break;
707 case 3:
19b0516f
BS
708 cksm += cpu_lduw_data(env, src) << 16;
709 cksm += cpu_ldub_data(env, src + 2) << 8;
374724f9 710 len += 3;
8ef7f78e
BS
711 break;
712 }
713
374724f9
RH
714 /* Fold the carry from the checksum. Note that we can see carry-out
715 during folding more than once (but probably not more than twice). */
716 while (cksm > 0xffffffffull) {
717 cksm = (uint32_t)cksm + (cksm >> 32);
718 }
719
720 /* Indicate whether or not we've processed everything. */
721 env->cc_op = (len == src_len ? 0 : 3);
8ef7f78e 722
374724f9
RH
723 /* Return both cksm and processed length. */
724 env->retxl = cksm;
725 return len;
8ef7f78e
BS
726}
727
19b0516f
BS
728void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
729 uint64_t src)
8ef7f78e
BS
730{
731 int len_dest = len >> 4;
732 int len_src = len & 0xf;
733 uint8_t b;
734 int second_nibble = 0;
735
736 dest += len_dest;
737 src += len_src;
738
739 /* last byte is special, it only flips the nibbles */
19b0516f
BS
740 b = cpu_ldub_data(env, src);
741 cpu_stb_data(env, dest, (b << 4) | (b >> 4));
8ef7f78e
BS
742 src--;
743 len_src--;
744
745 /* now pad every nibble with 0xf0 */
746
747 while (len_dest > 0) {
748 uint8_t cur_byte = 0;
749
750 if (len_src > 0) {
19b0516f 751 cur_byte = cpu_ldub_data(env, src);
8ef7f78e
BS
752 }
753
754 len_dest--;
755 dest--;
756
757 /* only advance one nibble at a time */
758 if (second_nibble) {
759 cur_byte >>= 4;
760 len_src--;
761 src--;
762 }
763 second_nibble = !second_nibble;
764
765 /* digit */
766 cur_byte = (cur_byte & 0xf);
767 /* zone bits */
768 cur_byte |= 0xf0;
769
19b0516f 770 cpu_stb_data(env, dest, cur_byte);
8ef7f78e
BS
771 }
772}
773
19b0516f
BS
774void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
775 uint64_t trans)
8ef7f78e
BS
776{
777 int i;
778
779 for (i = 0; i <= len; i++) {
19b0516f
BS
780 uint8_t byte = cpu_ldub_data(env, array + i);
781 uint8_t new_byte = cpu_ldub_data(env, trans + byte);
8ef7f78e 782
19b0516f 783 cpu_stb_data(env, array + i, new_byte);
8ef7f78e
BS
784 }
785}
786
3f4de675
AJ
787uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
788 uint64_t len, uint64_t trans)
789{
790 uint8_t end = env->regs[0] & 0xff;
791 uint64_t l = len;
792 uint64_t i;
793
794 if (!(env->psw.mask & PSW_MASK_64)) {
795 array &= 0x7fffffff;
796 l = (uint32_t)l;
797 }
798
799 /* Lest we fail to service interrupts in a timely manner, limit the
800 amount of work we're willing to do. For now, let's cap at 8k. */
801 if (l > 0x2000) {
802 l = 0x2000;
803 env->cc_op = 3;
804 } else {
805 env->cc_op = 0;
806 }
807
808 for (i = 0; i < l; i++) {
809 uint8_t byte, new_byte;
810
811 byte = cpu_ldub_data(env, array + i);
812
813 if (byte == end) {
814 env->cc_op = 1;
815 break;
816 }
817
818 new_byte = cpu_ldub_data(env, trans + byte);
819 cpu_stb_data(env, array + i, new_byte);
820 }
821
822 env->retxl = len - i;
823 return array + i;
824}
825
54f00775
AJ
826uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
827 uint64_t trans)
828{
829 uint32_t cc = 0;
830 int i;
831
832 for (i = 0; i <= len; i++) {
833 uint8_t byte = cpu_ldub_data(env, array + i);
834 uint8_t sbyte = cpu_ldub_data(env, trans + byte);
835
836 if (sbyte != 0) {
837 env->regs[1] = array + i;
838 env->regs[2] = (env->regs[2] & ~0xff) | sbyte;
839 cc = (i == len) ? 2 : 1;
840 break;
841 }
842 }
843
844 return cc;
845}
846
8ef7f78e 847#if !defined(CONFIG_USER_ONLY)
19b0516f 848void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e 849{
00c8cb0a 850 S390CPU *cpu = s390_env_get_cpu(env);
311918b9 851 bool PERchanged = false;
8ef7f78e
BS
852 int i;
853 uint64_t src = a2;
311918b9 854 uint64_t val;
8ef7f78e
BS
855
856 for (i = r1;; i = (i + 1) % 16) {
311918b9
AJ
857 val = cpu_ldq_data(env, src);
858 if (env->cregs[i] != val && i >= 9 && i <= 11) {
859 PERchanged = true;
860 }
861 env->cregs[i] = val;
8ef7f78e
BS
862 HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
863 i, src, env->cregs[i]);
864 src += sizeof(uint64_t);
865
866 if (i == r3) {
867 break;
868 }
869 }
870
311918b9
AJ
871 if (PERchanged && env->psw.mask & PSW_MASK_PER) {
872 s390_cpu_recompute_watchpoints(CPU(cpu));
873 }
874
00c8cb0a 875 tlb_flush(CPU(cpu), 1);
8ef7f78e
BS
876}
877
19b0516f 878void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e 879{
00c8cb0a 880 S390CPU *cpu = s390_env_get_cpu(env);
311918b9 881 bool PERchanged = false;
8ef7f78e
BS
882 int i;
883 uint64_t src = a2;
311918b9 884 uint32_t val;
8ef7f78e
BS
885
886 for (i = r1;; i = (i + 1) % 16) {
311918b9
AJ
887 val = cpu_ldl_data(env, src);
888 if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) {
889 PERchanged = true;
890 }
891 env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) | val;
8ef7f78e
BS
892 src += sizeof(uint32_t);
893
894 if (i == r3) {
895 break;
896 }
897 }
898
311918b9
AJ
899 if (PERchanged && env->psw.mask & PSW_MASK_PER) {
900 s390_cpu_recompute_watchpoints(CPU(cpu));
901 }
902
00c8cb0a 903 tlb_flush(CPU(cpu), 1);
8ef7f78e
BS
904}
905
19b0516f 906void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
907{
908 int i;
909 uint64_t dest = a2;
910
911 for (i = r1;; i = (i + 1) % 16) {
19b0516f 912 cpu_stq_data(env, dest, env->cregs[i]);
8ef7f78e
BS
913 dest += sizeof(uint64_t);
914
915 if (i == r3) {
916 break;
917 }
918 }
919}
920
19b0516f 921void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
922{
923 int i;
924 uint64_t dest = a2;
925
926 for (i = r1;; i = (i + 1) % 16) {
19b0516f 927 cpu_stl_data(env, dest, env->cregs[i]);
8ef7f78e
BS
928 dest += sizeof(uint32_t);
929
930 if (i == r3) {
931 break;
932 }
933 }
934}
935
936uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
937{
938 /* XXX implement */
939
940 return 0;
941}
942
943/* insert storage key extended */
19b0516f 944uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
8ef7f78e 945{
0f5f6691
JH
946 static S390SKeysState *ss;
947 static S390SKeysClass *skeyclass;
19b0516f 948 uint64_t addr = get_address(env, 0, 0, r2);
0f5f6691 949 uint8_t key;
8ef7f78e
BS
950
951 if (addr > ram_size) {
952 return 0;
953 }
954
0f5f6691
JH
955 if (unlikely(!ss)) {
956 ss = s390_get_skeys_device();
957 skeyclass = S390_SKEYS_GET_CLASS(ss);
958 }
959
960 if (skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key)) {
961 return 0;
962 }
963 return key;
8ef7f78e
BS
964}
965
966/* set storage key extended */
2bbde27f 967void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
8ef7f78e 968{
0f5f6691
JH
969 static S390SKeysState *ss;
970 static S390SKeysClass *skeyclass;
19b0516f 971 uint64_t addr = get_address(env, 0, 0, r2);
0f5f6691 972 uint8_t key;
8ef7f78e
BS
973
974 if (addr > ram_size) {
975 return;
976 }
977
0f5f6691
JH
978 if (unlikely(!ss)) {
979 ss = s390_get_skeys_device();
980 skeyclass = S390_SKEYS_GET_CLASS(ss);
981 }
982
983 key = (uint8_t) r1;
984 skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
8ef7f78e
BS
985}
986
987/* reset reference bit extended */
5cc69c54 988uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
8ef7f78e 989{
0f5f6691
JH
990 static S390SKeysState *ss;
991 static S390SKeysClass *skeyclass;
992 uint8_t re, key;
8ef7f78e
BS
993
994 if (r2 > ram_size) {
995 return 0;
996 }
997
0f5f6691
JH
998 if (unlikely(!ss)) {
999 ss = s390_get_skeys_device();
1000 skeyclass = S390_SKEYS_GET_CLASS(ss);
1001 }
1002
1003 if (skeyclass->get_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) {
1004 return 0;
1005 }
1006
8ef7f78e 1007 re = key & (SK_R | SK_C);
0f5f6691
JH
1008 key &= ~SK_R;
1009
1010 if (skeyclass->set_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) {
1011 return 0;
1012 }
8ef7f78e
BS
1013
1014 /*
1015 * cc
1016 *
1017 * 0 Reference bit zero; change bit zero
1018 * 1 Reference bit zero; change bit one
1019 * 2 Reference bit one; change bit zero
1020 * 3 Reference bit one; change bit one
1021 */
1022
1023 return re >> 1;
1024}
1025
1026/* compare and swap and purge */
3d596f49 1027uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
8ef7f78e 1028{
00c8cb0a 1029 S390CPU *cpu = s390_env_get_cpu(env);
8ef7f78e
BS
1030 uint32_t cc;
1031 uint32_t o1 = env->regs[r1];
3d596f49 1032 uint64_t a2 = r2 & ~3ULL;
19b0516f 1033 uint32_t o2 = cpu_ldl_data(env, a2);
8ef7f78e
BS
1034
1035 if (o1 == o2) {
19b0516f 1036 cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
3d596f49 1037 if (r2 & 0x3) {
8ef7f78e 1038 /* flush TLB / ALB */
00c8cb0a 1039 tlb_flush(CPU(cpu), 1);
8ef7f78e
BS
1040 }
1041 cc = 0;
1042 } else {
1043 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
1044 cc = 1;
1045 }
1046
1047 return cc;
1048}
1049
a3084e80 1050uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
8ef7f78e 1051{
a3084e80 1052 int cc = 0, i;
8ef7f78e 1053
a3084e80
AJ
1054 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1055 __func__, l, a1, a2);
1056
1057 if (l > 256) {
8ef7f78e
BS
1058 /* max 256 */
1059 l = 256;
1060 cc = 3;
1061 }
1062
8ef7f78e
BS
1063 /* XXX replace w/ memcpy */
1064 for (i = 0; i < l; i++) {
a3084e80 1065 cpu_stb_secondary(env, a1 + i, cpu_ldub_primary(env, a2 + i));
8ef7f78e
BS
1066 }
1067
1068 return cc;
1069}
1070
a3084e80 1071uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
8ef7f78e 1072{
a3084e80
AJ
1073 int cc = 0, i;
1074
8ef7f78e
BS
1075 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1076 __func__, l, a1, a2);
1077
a3084e80
AJ
1078 if (l > 256) {
1079 /* max 256 */
1080 l = 256;
1081 cc = 3;
1082 }
8ef7f78e 1083
a3084e80
AJ
1084 /* XXX replace w/ memcpy */
1085 for (i = 0; i < l; i++) {
1086 cpu_stb_primary(env, a1 + i, cpu_ldub_secondary(env, a2 + i));
1087 }
8ef7f78e 1088
a3084e80 1089 return cc;
8ef7f78e
BS
1090}
1091
1092/* invalidate pte */
19b0516f 1093void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
8ef7f78e 1094{
2efc6be2 1095 CPUState *cs = CPU(s390_env_get_cpu(env));
8ef7f78e
BS
1096 uint64_t page = vaddr & TARGET_PAGE_MASK;
1097 uint64_t pte = 0;
1098
1099 /* XXX broadcast to other CPUs */
1100
1101 /* XXX Linux is nice enough to give us the exact pte address.
1102 According to spec we'd have to find it out ourselves */
1103 /* XXX Linux is fine with overwriting the pte, the spec requires
1104 us to only set the invalid bit */
f606604f 1105 stq_phys(cs->as, pte_addr, pte | _PAGE_INVALID);
8ef7f78e
BS
1106
1107 /* XXX we exploit the fact that Linux passes the exact virtual
1108 address here - it's not obliged to! */
31b030d4 1109 tlb_flush_page(cs, page);
8ef7f78e
BS
1110
1111 /* XXX 31-bit hack */
1112 if (page & 0x80000000) {
31b030d4 1113 tlb_flush_page(cs, page & ~0x80000000);
8ef7f78e 1114 } else {
31b030d4 1115 tlb_flush_page(cs, page | 0x80000000);
8ef7f78e
BS
1116 }
1117}
1118
1119/* flush local tlb */
19b0516f 1120void HELPER(ptlb)(CPUS390XState *env)
8ef7f78e 1121{
00c8cb0a
AF
1122 S390CPU *cpu = s390_env_get_cpu(env);
1123
1124 tlb_flush(CPU(cpu), 1);
8ef7f78e
BS
1125}
1126
9c3fd85b
RH
1127/* load using real address */
1128uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr)
1129{
1130 CPUState *cs = CPU(s390_env_get_cpu(env));
1131
1132 return (uint32_t)ldl_phys(cs->as, get_address(env, 0, 0, addr));
1133}
1134
1135uint64_t HELPER(lurag)(CPUS390XState *env, uint64_t addr)
1136{
1137 CPUState *cs = CPU(s390_env_get_cpu(env));
1138
1139 return ldq_phys(cs->as, get_address(env, 0, 0, addr));
1140}
1141
8ef7f78e 1142/* store using real address */
204504e2 1143void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
8ef7f78e 1144{
2efc6be2
AF
1145 CPUState *cs = CPU(s390_env_get_cpu(env));
1146
81822c2f 1147 stl_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1);
2f543949
AJ
1148
1149 if ((env->psw.mask & PSW_MASK_PER) &&
1150 (env->cregs[9] & PER_CR9_EVENT_STORE) &&
1151 (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) {
1152 /* PSW is saved just before calling the helper. */
1153 env->per_address = env->psw.addr;
1154 env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env);
1155 }
8ef7f78e
BS
1156}
1157
9c3fd85b
RH
1158void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1159{
1160 CPUState *cs = CPU(s390_env_get_cpu(env));
1161
1162 stq_phys(cs->as, get_address(env, 0, 0, addr), v1);
2f543949
AJ
1163
1164 if ((env->psw.mask & PSW_MASK_PER) &&
1165 (env->cregs[9] & PER_CR9_EVENT_STORE) &&
1166 (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) {
1167 /* PSW is saved just before calling the helper. */
1168 env->per_address = env->psw.addr;
1169 env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env);
1170 }
9c3fd85b
RH
1171}
1172
8ef7f78e 1173/* load real address */
d8fe4a9c 1174uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
8ef7f78e 1175{
27103424 1176 CPUState *cs = CPU(s390_env_get_cpu(env));
8ef7f78e 1177 uint32_t cc = 0;
27103424 1178 int old_exc = cs->exception_index;
8ef7f78e
BS
1179 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1180 uint64_t ret;
1181 int flags;
1182
1183 /* XXX incomplete - has more corner cases */
1184 if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1185 program_interrupt(env, PGM_SPECIAL_OP, 2);
1186 }
1187
27103424 1188 cs->exception_index = old_exc;
e3e09d87 1189 if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) {
8ef7f78e
BS
1190 cc = 3;
1191 }
27103424 1192 if (cs->exception_index == EXCP_PGM) {
8ef7f78e
BS
1193 ret = env->int_pgm_code | 0x80000000;
1194 } else {
1195 ret |= addr & ~TARGET_PAGE_MASK;
1196 }
27103424 1197 cs->exception_index = old_exc;
8ef7f78e 1198
d8fe4a9c
RH
1199 env->cc_op = cc;
1200 return ret;
8ef7f78e 1201}
8ef7f78e 1202#endif