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