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