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