]> git.proxmox.com Git - qemu.git/blame - target-s390x/mem_helper.c
virtio-net: properly check the vhost status during status set
[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
aa31bf60
RH
307static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
308{
309 /* 31-Bit mode */
310 if (!(env->psw.mask & PSW_MASK_64)) {
311 a &= 0x7fffffff;
312 }
313 return a;
314}
315
19b0516f 316static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
8ef7f78e
BS
317{
318 uint64_t r = d2;
8ef7f78e
BS
319 if (x2) {
320 r += env->regs[x2];
321 }
8ef7f78e
BS
322 if (b2) {
323 r += env->regs[b2];
324 }
aa31bf60 325 return fix_address(env, r);
8ef7f78e
BS
326}
327
19b0516f 328static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
8ef7f78e 329{
aa31bf60 330 return fix_address(env, env->regs[reg]);
8ef7f78e
BS
331}
332
333/* search string (c is byte to search, r2 is string, r1 end of string) */
4600c994
RH
334uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
335 uint64_t str)
8ef7f78e 336{
4600c994
RH
337 uint32_t len;
338 uint8_t v, c = r0;
8ef7f78e 339
4600c994
RH
340 str = fix_address(env, str);
341 end = fix_address(env, end);
8ef7f78e 342
4600c994
RH
343 /* Assume for now that R2 is unmodified. */
344 env->retxl = str;
345
346 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 347 amount of work we're willing to do. For now, let's cap at 8k. */
4600c994
RH
348 for (len = 0; len < 0x2000; ++len) {
349 if (str + len == end) {
350 /* Character not found. R1 & R2 are unmodified. */
351 env->cc_op = 2;
352 return end;
353 }
354 v = cpu_ldub_data(env, str + len);
355 if (v == c) {
356 /* Character found. Set R1 to the location; R2 is unmodified. */
357 env->cc_op = 1;
358 return str + len;
8ef7f78e
BS
359 }
360 }
361
4600c994
RH
362 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
363 env->retxl = str + len;
364 env->cc_op = 3;
365 return end;
8ef7f78e
BS
366}
367
368/* unsigned string compare (c is string terminator) */
aa31bf60 369uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
8ef7f78e 370{
aa31bf60 371 uint32_t len;
8ef7f78e
BS
372
373 c = c & 0xff;
aa31bf60
RH
374 s1 = fix_address(env, s1);
375 s2 = fix_address(env, s2);
376
377 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 378 amount of work we're willing to do. For now, let's cap at 8k. */
aa31bf60
RH
379 for (len = 0; len < 0x2000; ++len) {
380 uint8_t v1 = cpu_ldub_data(env, s1 + len);
381 uint8_t v2 = cpu_ldub_data(env, s2 + len);
382 if (v1 == v2) {
383 if (v1 == c) {
384 /* Equal. CC=0, and don't advance the registers. */
385 env->cc_op = 0;
386 env->retxl = s2;
387 return s1;
388 }
389 } else {
390 /* Unequal. CC={1,2}, and advance the registers. Note that
391 the terminator need not be zero, but the string that contains
392 the terminator is by definition "low". */
393 env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
394 env->retxl = s2 + len;
395 return s1 + len;
8ef7f78e 396 }
8ef7f78e
BS
397 }
398
aa31bf60
RH
399 /* CPU-determined bytes equal; advance the registers. */
400 env->cc_op = 3;
401 env->retxl = s2 + len;
402 return s1 + len;
8ef7f78e
BS
403}
404
405/* move page */
19b0516f 406void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
8ef7f78e
BS
407{
408 /* XXX missing r0 handling */
ee6c38d5 409 env->cc_op = 0;
8ef7f78e 410#ifdef CONFIG_USER_ONLY
ee6c38d5 411 memmove(g2h(r1), g2h(r2), TARGET_PAGE_SIZE);
8ef7f78e
BS
412#else
413 mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
414#endif
415}
416
417/* string copy (c is string terminator) */
aa31bf60 418uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
8ef7f78e 419{
aa31bf60 420 uint32_t len;
8ef7f78e
BS
421
422 c = c & 0xff;
aa31bf60
RH
423 d = fix_address(env, d);
424 s = fix_address(env, s);
425
426 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 427 amount of work we're willing to do. For now, let's cap at 8k. */
aa31bf60
RH
428 for (len = 0; len < 0x2000; ++len) {
429 uint8_t v = cpu_ldub_data(env, s + len);
430 cpu_stb_data(env, d + len, v);
8ef7f78e 431 if (v == c) {
aa31bf60
RH
432 /* Complete. Set CC=1 and advance R1. */
433 env->cc_op = 1;
434 env->retxl = s;
435 return d + len;
8ef7f78e 436 }
8ef7f78e 437 }
aa31bf60
RH
438
439 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
440 env->cc_op = 3;
441 env->retxl = s + len;
442 return d + len;
8ef7f78e
BS
443}
444
19b0516f
BS
445static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
446 uint32_t mask)
8ef7f78e
BS
447{
448 int pos = 24; /* top of the lower half of r1 */
449 uint64_t rmask = 0xff000000ULL;
450 uint8_t val = 0;
451 int ccd = 0;
452 uint32_t cc = 0;
453
454 while (mask) {
455 if (mask & 8) {
456 env->regs[r1] &= ~rmask;
19b0516f 457 val = cpu_ldub_data(env, address);
8ef7f78e
BS
458 if ((val & 0x80) && !ccd) {
459 cc = 1;
460 }
461 ccd = 1;
462 if (val && cc == 0) {
463 cc = 2;
464 }
465 env->regs[r1] |= (uint64_t)val << pos;
466 address++;
467 }
468 mask = (mask << 1) & 0xf;
469 pos -= 8;
470 rmask >>= 8;
471 }
472
473 return cc;
474}
475
476/* execute instruction
477 this instruction executes an insn modified with the contents of r1
478 it does not change the executed instruction in memory
479 it does not change the program counter
480 in other words: tricky...
481 currently implemented by interpreting the cases it is most commonly used in
482*/
19b0516f
BS
483uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
484 uint64_t addr, uint64_t ret)
8ef7f78e 485{
19b0516f 486 uint16_t insn = cpu_lduw_code(env, addr);
8ef7f78e
BS
487
488 HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
489 insn);
490 if ((insn & 0xf0ff) == 0xd000) {
491 uint32_t l, insn2, b1, b2, d1, d2;
492
493 l = v1 & 0xff;
19b0516f 494 insn2 = cpu_ldl_code(env, addr + 2);
8ef7f78e
BS
495 b1 = (insn2 >> 28) & 0xf;
496 b2 = (insn2 >> 12) & 0xf;
497 d1 = (insn2 >> 16) & 0xfff;
498 d2 = insn2 & 0xfff;
499 switch (insn & 0xf00) {
500 case 0x200:
19b0516f
BS
501 helper_mvc(env, l, get_address(env, 0, b1, d1),
502 get_address(env, 0, b2, d2));
8ef7f78e
BS
503 break;
504 case 0x500:
19b0516f
BS
505 cc = helper_clc(env, l, get_address(env, 0, b1, d1),
506 get_address(env, 0, b2, d2));
8ef7f78e
BS
507 break;
508 case 0x700:
19b0516f
BS
509 cc = helper_xc(env, l, get_address(env, 0, b1, d1),
510 get_address(env, 0, b2, d2));
8ef7f78e
BS
511 break;
512 case 0xc00:
19b0516f
BS
513 helper_tr(env, l, get_address(env, 0, b1, d1),
514 get_address(env, 0, b2, d2));
8ef7f78e
BS
515 break;
516 default:
517 goto abort;
518 break;
519 }
520 } else if ((insn & 0xff00) == 0x0a00) {
521 /* supervisor call */
522 HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
523 env->psw.addr = ret - 4;
524 env->int_svc_code = (insn | v1) & 0xff;
d5a103cd 525 env->int_svc_ilen = 4;
089f5c06 526 helper_exception(env, EXCP_SVC);
8ef7f78e
BS
527 } else if ((insn & 0xff00) == 0xbf00) {
528 uint32_t insn2, r1, r3, b2, d2;
529
19b0516f 530 insn2 = cpu_ldl_code(env, addr + 2);
8ef7f78e
BS
531 r1 = (insn2 >> 20) & 0xf;
532 r3 = (insn2 >> 16) & 0xf;
533 b2 = (insn2 >> 12) & 0xf;
534 d2 = insn2 & 0xfff;
19b0516f 535 cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
8ef7f78e
BS
536 } else {
537 abort:
538 cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
539 insn);
540 }
541 return cc;
542}
543
8ef7f78e 544/* load access registers r1 to r3 from memory at a2 */
19b0516f 545void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
546{
547 int i;
548
549 for (i = r1;; i = (i + 1) % 16) {
19b0516f 550 env->aregs[i] = cpu_ldl_data(env, a2);
8ef7f78e
BS
551 a2 += 4;
552
553 if (i == r3) {
554 break;
555 }
556 }
557}
558
559/* store access registers r1 to r3 in memory at a2 */
19b0516f 560void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
561{
562 int i;
563
564 for (i = r1;; i = (i + 1) % 16) {
19b0516f 565 cpu_stl_data(env, a2, env->aregs[i]);
8ef7f78e
BS
566 a2 += 4;
567
568 if (i == r3) {
569 break;
570 }
571 }
572}
573
574/* move long */
19b0516f 575uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
8ef7f78e
BS
576{
577 uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
19b0516f 578 uint64_t dest = get_address_31fix(env, r1);
8ef7f78e 579 uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
19b0516f 580 uint64_t src = get_address_31fix(env, r2);
8ef7f78e
BS
581 uint8_t pad = src >> 24;
582 uint8_t v;
583 uint32_t cc;
584
585 if (destlen == srclen) {
586 cc = 0;
587 } else if (destlen < srclen) {
588 cc = 1;
589 } else {
590 cc = 2;
591 }
592
593 if (srclen > destlen) {
594 srclen = destlen;
595 }
596
597 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
19b0516f
BS
598 v = cpu_ldub_data(env, src);
599 cpu_stb_data(env, dest, v);
8ef7f78e
BS
600 }
601
602 for (; destlen; dest++, destlen--) {
19b0516f 603 cpu_stb_data(env, dest, pad);
8ef7f78e
BS
604 }
605
606 env->regs[r1 + 1] = destlen;
607 /* can't use srclen here, we trunc'ed it */
608 env->regs[r2 + 1] -= src - env->regs[r2];
609 env->regs[r1] = dest;
610 env->regs[r2] = src;
611
612 return cc;
613}
614
615/* move long extended another memcopy insn with more bells and whistles */
19b0516f
BS
616uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
617 uint32_t r3)
8ef7f78e
BS
618{
619 uint64_t destlen = env->regs[r1 + 1];
620 uint64_t dest = env->regs[r1];
621 uint64_t srclen = env->regs[r3 + 1];
622 uint64_t src = env->regs[r3];
623 uint8_t pad = a2 & 0xff;
624 uint8_t v;
625 uint32_t cc;
626
627 if (!(env->psw.mask & PSW_MASK_64)) {
628 destlen = (uint32_t)destlen;
629 srclen = (uint32_t)srclen;
630 dest &= 0x7fffffff;
631 src &= 0x7fffffff;
632 }
633
634 if (destlen == srclen) {
635 cc = 0;
636 } else if (destlen < srclen) {
637 cc = 1;
638 } else {
639 cc = 2;
640 }
641
642 if (srclen > destlen) {
643 srclen = destlen;
644 }
645
646 for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
19b0516f
BS
647 v = cpu_ldub_data(env, src);
648 cpu_stb_data(env, dest, v);
8ef7f78e
BS
649 }
650
651 for (; destlen; dest++, destlen--) {
19b0516f 652 cpu_stb_data(env, dest, pad);
8ef7f78e
BS
653 }
654
655 env->regs[r1 + 1] = destlen;
656 /* can't use srclen here, we trunc'ed it */
657 /* FIXME: 31-bit mode! */
658 env->regs[r3 + 1] -= src - env->regs[r3];
659 env->regs[r1] = dest;
660 env->regs[r3] = src;
661
662 return cc;
663}
664
665/* compare logical long extended memcompare insn with padding */
19b0516f
BS
666uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
667 uint32_t r3)
8ef7f78e
BS
668{
669 uint64_t destlen = env->regs[r1 + 1];
19b0516f 670 uint64_t dest = get_address_31fix(env, r1);
8ef7f78e 671 uint64_t srclen = env->regs[r3 + 1];
19b0516f 672 uint64_t src = get_address_31fix(env, r3);
8ef7f78e
BS
673 uint8_t pad = a2 & 0xff;
674 uint8_t v1 = 0, v2 = 0;
675 uint32_t cc = 0;
676
677 if (!(destlen || srclen)) {
678 return cc;
679 }
680
681 if (srclen > destlen) {
682 srclen = destlen;
683 }
684
685 for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
19b0516f
BS
686 v1 = srclen ? cpu_ldub_data(env, src) : pad;
687 v2 = destlen ? cpu_ldub_data(env, dest) : pad;
8ef7f78e
BS
688 if (v1 != v2) {
689 cc = (v1 < v2) ? 1 : 2;
690 break;
691 }
692 }
693
694 env->regs[r1 + 1] = destlen;
695 /* can't use srclen here, we trunc'ed it */
696 env->regs[r3 + 1] -= src - env->regs[r3];
697 env->regs[r1] = dest;
698 env->regs[r3] = src;
699
700 return cc;
701}
702
703/* checksum */
374724f9
RH
704uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
705 uint64_t src, uint64_t src_len)
8ef7f78e 706{
374724f9
RH
707 uint64_t max_len, len;
708 uint64_t cksm = (uint32_t)r1;
8ef7f78e 709
374724f9 710 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 711 amount of work we're willing to do. For now, let's cap at 8k. */
374724f9 712 max_len = (src_len > 0x2000 ? 0x2000 : src_len);
8ef7f78e 713
374724f9
RH
714 /* Process full words as available. */
715 for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
716 cksm += (uint32_t)cpu_ldl_data(env, src);
8ef7f78e
BS
717 }
718
374724f9 719 switch (max_len - len) {
8ef7f78e 720 case 1:
19b0516f 721 cksm += cpu_ldub_data(env, src) << 24;
374724f9 722 len += 1;
8ef7f78e
BS
723 break;
724 case 2:
19b0516f 725 cksm += cpu_lduw_data(env, src) << 16;
374724f9 726 len += 2;
8ef7f78e
BS
727 break;
728 case 3:
19b0516f
BS
729 cksm += cpu_lduw_data(env, src) << 16;
730 cksm += cpu_ldub_data(env, src + 2) << 8;
374724f9 731 len += 3;
8ef7f78e
BS
732 break;
733 }
734
374724f9
RH
735 /* Fold the carry from the checksum. Note that we can see carry-out
736 during folding more than once (but probably not more than twice). */
737 while (cksm > 0xffffffffull) {
738 cksm = (uint32_t)cksm + (cksm >> 32);
739 }
740
741 /* Indicate whether or not we've processed everything. */
742 env->cc_op = (len == src_len ? 0 : 3);
8ef7f78e 743
374724f9
RH
744 /* Return both cksm and processed length. */
745 env->retxl = cksm;
746 return len;
8ef7f78e
BS
747}
748
19b0516f
BS
749void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
750 uint64_t src)
8ef7f78e
BS
751{
752 int len_dest = len >> 4;
753 int len_src = len & 0xf;
754 uint8_t b;
755 int second_nibble = 0;
756
757 dest += len_dest;
758 src += len_src;
759
760 /* last byte is special, it only flips the nibbles */
19b0516f
BS
761 b = cpu_ldub_data(env, src);
762 cpu_stb_data(env, dest, (b << 4) | (b >> 4));
8ef7f78e
BS
763 src--;
764 len_src--;
765
766 /* now pad every nibble with 0xf0 */
767
768 while (len_dest > 0) {
769 uint8_t cur_byte = 0;
770
771 if (len_src > 0) {
19b0516f 772 cur_byte = cpu_ldub_data(env, src);
8ef7f78e
BS
773 }
774
775 len_dest--;
776 dest--;
777
778 /* only advance one nibble at a time */
779 if (second_nibble) {
780 cur_byte >>= 4;
781 len_src--;
782 src--;
783 }
784 second_nibble = !second_nibble;
785
786 /* digit */
787 cur_byte = (cur_byte & 0xf);
788 /* zone bits */
789 cur_byte |= 0xf0;
790
19b0516f 791 cpu_stb_data(env, dest, cur_byte);
8ef7f78e
BS
792 }
793}
794
19b0516f
BS
795void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
796 uint64_t trans)
8ef7f78e
BS
797{
798 int i;
799
800 for (i = 0; i <= len; i++) {
19b0516f
BS
801 uint8_t byte = cpu_ldub_data(env, array + i);
802 uint8_t new_byte = cpu_ldub_data(env, trans + byte);
8ef7f78e 803
19b0516f 804 cpu_stb_data(env, array + i, new_byte);
8ef7f78e
BS
805 }
806}
807
808#if !defined(CONFIG_USER_ONLY)
19b0516f 809void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
810{
811 int i;
812 uint64_t src = a2;
813
814 for (i = r1;; i = (i + 1) % 16) {
19b0516f 815 env->cregs[i] = cpu_ldq_data(env, src);
8ef7f78e
BS
816 HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
817 i, src, env->cregs[i]);
818 src += sizeof(uint64_t);
819
820 if (i == r3) {
821 break;
822 }
823 }
824
825 tlb_flush(env, 1);
826}
827
19b0516f 828void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
829{
830 int i;
831 uint64_t src = a2;
832
833 for (i = r1;; i = (i + 1) % 16) {
19b0516f
BS
834 env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
835 cpu_ldl_data(env, src);
8ef7f78e
BS
836 src += sizeof(uint32_t);
837
838 if (i == r3) {
839 break;
840 }
841 }
842
843 tlb_flush(env, 1);
844}
845
19b0516f 846void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
847{
848 int i;
849 uint64_t dest = a2;
850
851 for (i = r1;; i = (i + 1) % 16) {
19b0516f 852 cpu_stq_data(env, dest, env->cregs[i]);
8ef7f78e
BS
853 dest += sizeof(uint64_t);
854
855 if (i == r3) {
856 break;
857 }
858 }
859}
860
19b0516f 861void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
862{
863 int i;
864 uint64_t dest = a2;
865
866 for (i = r1;; i = (i + 1) % 16) {
19b0516f 867 cpu_stl_data(env, dest, env->cregs[i]);
8ef7f78e
BS
868 dest += sizeof(uint32_t);
869
870 if (i == r3) {
871 break;
872 }
873 }
874}
875
876uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
877{
878 /* XXX implement */
879
880 return 0;
881}
882
883/* insert storage key extended */
19b0516f 884uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
8ef7f78e 885{
19b0516f 886 uint64_t addr = get_address(env, 0, 0, r2);
8ef7f78e
BS
887
888 if (addr > ram_size) {
889 return 0;
890 }
891
892 return env->storage_keys[addr / TARGET_PAGE_SIZE];
893}
894
895/* set storage key extended */
2bbde27f 896void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
8ef7f78e 897{
19b0516f 898 uint64_t addr = get_address(env, 0, 0, r2);
8ef7f78e
BS
899
900 if (addr > ram_size) {
901 return;
902 }
903
904 env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
905}
906
907/* reset reference bit extended */
5cc69c54 908uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
8ef7f78e
BS
909{
910 uint8_t re;
911 uint8_t key;
912
913 if (r2 > ram_size) {
914 return 0;
915 }
916
917 key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
918 re = key & (SK_R | SK_C);
919 env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
920
921 /*
922 * cc
923 *
924 * 0 Reference bit zero; change bit zero
925 * 1 Reference bit zero; change bit one
926 * 2 Reference bit one; change bit zero
927 * 3 Reference bit one; change bit one
928 */
929
930 return re >> 1;
931}
932
933/* compare and swap and purge */
3d596f49 934uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
8ef7f78e
BS
935{
936 uint32_t cc;
937 uint32_t o1 = env->regs[r1];
3d596f49 938 uint64_t a2 = r2 & ~3ULL;
19b0516f 939 uint32_t o2 = cpu_ldl_data(env, a2);
8ef7f78e
BS
940
941 if (o1 == o2) {
19b0516f 942 cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
3d596f49 943 if (r2 & 0x3) {
8ef7f78e
BS
944 /* flush TLB / ALB */
945 tlb_flush(env, 1);
946 }
947 cc = 0;
948 } else {
949 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
950 cc = 1;
951 }
952
953 return cc;
954}
955
19b0516f
BS
956static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
957 uint64_t mode1, uint64_t a2, uint64_t mode2)
8ef7f78e
BS
958{
959 target_ulong src, dest;
960 int flags, cc = 0, i;
961
962 if (!l) {
963 return 0;
964 } else if (l > 256) {
965 /* max 256 */
966 l = 256;
967 cc = 3;
968 }
969
970 if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
971 cpu_loop_exit(env);
972 }
973 dest |= a1 & ~TARGET_PAGE_MASK;
974
975 if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
976 cpu_loop_exit(env);
977 }
978 src |= a2 & ~TARGET_PAGE_MASK;
979
980 /* XXX replace w/ memcpy */
981 for (i = 0; i < l; i++) {
982 /* XXX be more clever */
983 if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
984 (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
19b0516f 985 mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
8ef7f78e
BS
986 break;
987 }
988 stb_phys(dest + i, ldub_phys(src + i));
989 }
990
991 return cc;
992}
993
19b0516f 994uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
8ef7f78e
BS
995{
996 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
997 __func__, l, a1, a2);
998
19b0516f 999 return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
8ef7f78e
BS
1000}
1001
19b0516f 1002uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
8ef7f78e
BS
1003{
1004 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1005 __func__, l, a1, a2);
1006
19b0516f 1007 return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
8ef7f78e
BS
1008}
1009
1010/* invalidate pte */
19b0516f 1011void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
8ef7f78e
BS
1012{
1013 uint64_t page = vaddr & TARGET_PAGE_MASK;
1014 uint64_t pte = 0;
1015
1016 /* XXX broadcast to other CPUs */
1017
1018 /* XXX Linux is nice enough to give us the exact pte address.
1019 According to spec we'd have to find it out ourselves */
1020 /* XXX Linux is fine with overwriting the pte, the spec requires
1021 us to only set the invalid bit */
1022 stq_phys(pte_addr, pte | _PAGE_INVALID);
1023
1024 /* XXX we exploit the fact that Linux passes the exact virtual
1025 address here - it's not obliged to! */
1026 tlb_flush_page(env, page);
1027
1028 /* XXX 31-bit hack */
1029 if (page & 0x80000000) {
1030 tlb_flush_page(env, page & ~0x80000000);
1031 } else {
1032 tlb_flush_page(env, page | 0x80000000);
1033 }
1034}
1035
1036/* flush local tlb */
19b0516f 1037void HELPER(ptlb)(CPUS390XState *env)
8ef7f78e
BS
1038{
1039 tlb_flush(env, 1);
1040}
1041
1042/* store using real address */
204504e2 1043void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
8ef7f78e 1044{
204504e2 1045 stw_phys(get_address(env, 0, 0, addr), (uint32_t)v1);
8ef7f78e
BS
1046}
1047
1048/* load real address */
d8fe4a9c 1049uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
8ef7f78e
BS
1050{
1051 uint32_t cc = 0;
1052 int old_exc = env->exception_index;
1053 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1054 uint64_t ret;
1055 int flags;
1056
1057 /* XXX incomplete - has more corner cases */
1058 if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1059 program_interrupt(env, PGM_SPECIAL_OP, 2);
1060 }
1061
1062 env->exception_index = old_exc;
1063 if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1064 cc = 3;
1065 }
1066 if (env->exception_index == EXCP_PGM) {
1067 ret = env->int_pgm_code | 0x80000000;
1068 } else {
1069 ret |= addr & ~TARGET_PAGE_MASK;
1070 }
1071 env->exception_index = old_exc;
1072
d8fe4a9c
RH
1073 env->cc_op = cc;
1074 return ret;
8ef7f78e 1075}
8ef7f78e 1076#endif