]> Git Repo - qemu.git/blame - target-s390x/mem_helper.c
block/ssh: Drop superfluous libssh2_session_last_errno() calls
[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
BS
53 if (unlikely(ret != 0)) {
54 if (likely(retaddr)) {
55 /* now we have a real cpu fault */
3f38f309 56 cpu_restore_state(cs, retaddr);
8ef7f78e 57 }
5638d180 58 cpu_loop_exit(cs);
8ef7f78e 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{
a47dddd7 75 S390CPU *cpu = s390_env_get_cpu(env);
a8170e5e
AK
76 hwaddr dest_phys;
77 hwaddr len = l;
8ef7f78e
BS
78 void *dest_p;
79 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
80 int flags;
81
82 if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
19b0516f 83 cpu_stb_data(env, dest, byte);
a47dddd7 84 cpu_abort(CPU(cpu), "should never reach here");
8ef7f78e
BS
85 }
86 dest_phys |= dest & ~TARGET_PAGE_MASK;
87
88 dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
89
90 memset(dest_p, byte, len);
91
92 cpu_physical_memory_unmap(dest_p, 1, len, len);
93}
94
95static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
96 uint64_t src)
97{
a47dddd7 98 S390CPU *cpu = s390_env_get_cpu(env);
a8170e5e
AK
99 hwaddr dest_phys;
100 hwaddr src_phys;
101 hwaddr len = l;
8ef7f78e
BS
102 void *dest_p;
103 void *src_p;
104 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
105 int flags;
106
107 if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
19b0516f 108 cpu_stb_data(env, dest, 0);
a47dddd7 109 cpu_abort(CPU(cpu), "should never reach here");
8ef7f78e
BS
110 }
111 dest_phys |= dest & ~TARGET_PAGE_MASK;
112
113 if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) {
19b0516f 114 cpu_ldub_data(env, src);
a47dddd7 115 cpu_abort(CPU(cpu), "should never reach here");
8ef7f78e
BS
116 }
117 src_phys |= src & ~TARGET_PAGE_MASK;
118
119 dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
120 src_p = cpu_physical_memory_map(src_phys, &len, 0);
121
122 memmove(dest_p, src_p, len);
123
124 cpu_physical_memory_unmap(dest_p, 1, len, len);
125 cpu_physical_memory_unmap(src_p, 0, len, len);
126}
127#endif
128
129/* and on array */
19b0516f
BS
130uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
131 uint64_t src)
8ef7f78e
BS
132{
133 int i;
134 unsigned char x;
135 uint32_t cc = 0;
136
137 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
138 __func__, l, dest, src);
139 for (i = 0; i <= l; i++) {
19b0516f 140 x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
8ef7f78e
BS
141 if (x) {
142 cc = 1;
143 }
19b0516f 144 cpu_stb_data(env, dest + i, x);
8ef7f78e
BS
145 }
146 return cc;
147}
148
149/* xor on array */
19b0516f
BS
150uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
151 uint64_t src)
8ef7f78e
BS
152{
153 int i;
154 unsigned char x;
155 uint32_t cc = 0;
156
157 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
158 __func__, l, dest, src);
159
160#ifndef CONFIG_USER_ONLY
161 /* xor with itself is the same as memset(0) */
162 if ((l > 32) && (src == dest) &&
163 (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) {
164 mvc_fast_memset(env, l + 1, dest, 0);
165 return 0;
166 }
167#else
168 if (src == dest) {
169 memset(g2h(dest), 0, l + 1);
170 return 0;
171 }
172#endif
173
174 for (i = 0; i <= l; i++) {
19b0516f 175 x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
8ef7f78e
BS
176 if (x) {
177 cc = 1;
178 }
19b0516f 179 cpu_stb_data(env, dest + i, x);
8ef7f78e
BS
180 }
181 return cc;
182}
183
184/* or on array */
19b0516f
BS
185uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
186 uint64_t src)
8ef7f78e
BS
187{
188 int i;
189 unsigned char x;
190 uint32_t cc = 0;
191
192 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
193 __func__, l, dest, src);
194 for (i = 0; i <= l; i++) {
19b0516f 195 x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
8ef7f78e
BS
196 if (x) {
197 cc = 1;
198 }
19b0516f 199 cpu_stb_data(env, dest + i, x);
8ef7f78e
BS
200 }
201 return cc;
202}
203
204/* memmove */
19b0516f 205void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
8ef7f78e
BS
206{
207 int i = 0;
208 int x = 0;
209 uint32_t l_64 = (l + 1) / 8;
210
211 HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
212 __func__, l, dest, src);
213
214#ifndef CONFIG_USER_ONLY
215 if ((l > 32) &&
216 (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) &&
217 (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) {
218 if (dest == (src + 1)) {
19b0516f 219 mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src));
8ef7f78e
BS
220 return;
221 } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
222 mvc_fast_memmove(env, l + 1, dest, src);
223 return;
224 }
225 }
226#else
227 if (dest == (src + 1)) {
19b0516f 228 memset(g2h(dest), cpu_ldub_data(env, src), l + 1);
8ef7f78e
BS
229 return;
230 } else {
231 memmove(g2h(dest), g2h(src), l + 1);
232 return;
233 }
234#endif
235
236 /* handle the parts that fit into 8-byte loads/stores */
237 if (dest != (src + 1)) {
238 for (i = 0; i < l_64; i++) {
19b0516f 239 cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
8ef7f78e
BS
240 x += 8;
241 }
242 }
243
244 /* slow version crossing pages with byte accesses */
245 for (i = x; i <= l; i++) {
19b0516f 246 cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
8ef7f78e
BS
247 }
248}
249
250/* compare unsigned byte arrays */
19b0516f 251uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
8ef7f78e
BS
252{
253 int i;
254 unsigned char x, y;
255 uint32_t cc;
256
257 HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
258 __func__, l, s1, s2);
259 for (i = 0; i <= l; i++) {
19b0516f
BS
260 x = cpu_ldub_data(env, s1 + i);
261 y = cpu_ldub_data(env, s2 + i);
8ef7f78e
BS
262 HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
263 if (x < y) {
264 cc = 1;
265 goto done;
266 } else if (x > y) {
267 cc = 2;
268 goto done;
269 }
270 }
271 cc = 0;
272 done:
273 HELPER_LOG("\n");
274 return cc;
275}
276
277/* compare logical under mask */
19b0516f
BS
278uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
279 uint64_t addr)
8ef7f78e
BS
280{
281 uint8_t r, d;
282 uint32_t cc;
283
284 HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
285 mask, addr);
286 cc = 0;
287 while (mask) {
288 if (mask & 8) {
19b0516f 289 d = cpu_ldub_data(env, addr);
8ef7f78e
BS
290 r = (r1 & 0xff000000UL) >> 24;
291 HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
292 addr);
293 if (r < d) {
294 cc = 1;
295 break;
296 } else if (r > d) {
297 cc = 2;
298 break;
299 }
300 addr++;
301 }
302 mask = (mask << 1) & 0xf;
303 r1 <<= 8;
304 }
305 HELPER_LOG("\n");
306 return cc;
307}
308
aa31bf60
RH
309static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
310{
311 /* 31-Bit mode */
312 if (!(env->psw.mask & PSW_MASK_64)) {
313 a &= 0x7fffffff;
314 }
315 return a;
316}
317
19b0516f 318static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
8ef7f78e
BS
319{
320 uint64_t r = d2;
8ef7f78e
BS
321 if (x2) {
322 r += env->regs[x2];
323 }
8ef7f78e
BS
324 if (b2) {
325 r += env->regs[b2];
326 }
aa31bf60 327 return fix_address(env, r);
8ef7f78e
BS
328}
329
19b0516f 330static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
8ef7f78e 331{
aa31bf60 332 return fix_address(env, env->regs[reg]);
8ef7f78e
BS
333}
334
335/* search string (c is byte to search, r2 is string, r1 end of string) */
4600c994
RH
336uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
337 uint64_t str)
8ef7f78e 338{
4600c994
RH
339 uint32_t len;
340 uint8_t v, c = r0;
8ef7f78e 341
4600c994
RH
342 str = fix_address(env, str);
343 end = fix_address(env, end);
8ef7f78e 344
4600c994
RH
345 /* Assume for now that R2 is unmodified. */
346 env->retxl = str;
347
348 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 349 amount of work we're willing to do. For now, let's cap at 8k. */
4600c994
RH
350 for (len = 0; len < 0x2000; ++len) {
351 if (str + len == end) {
352 /* Character not found. R1 & R2 are unmodified. */
353 env->cc_op = 2;
354 return end;
355 }
356 v = cpu_ldub_data(env, str + len);
357 if (v == c) {
358 /* Character found. Set R1 to the location; R2 is unmodified. */
359 env->cc_op = 1;
360 return str + len;
8ef7f78e
BS
361 }
362 }
363
4600c994
RH
364 /* CPU-determined bytes processed. Advance R2 to next byte to process. */
365 env->retxl = str + len;
366 env->cc_op = 3;
367 return end;
8ef7f78e
BS
368}
369
370/* unsigned string compare (c is string terminator) */
aa31bf60 371uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
8ef7f78e 372{
aa31bf60 373 uint32_t len;
8ef7f78e
BS
374
375 c = c & 0xff;
aa31bf60
RH
376 s1 = fix_address(env, s1);
377 s2 = fix_address(env, s2);
378
379 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 380 amount of work we're willing to do. For now, let's cap at 8k. */
aa31bf60
RH
381 for (len = 0; len < 0x2000; ++len) {
382 uint8_t v1 = cpu_ldub_data(env, s1 + len);
383 uint8_t v2 = cpu_ldub_data(env, s2 + len);
384 if (v1 == v2) {
385 if (v1 == c) {
386 /* Equal. CC=0, and don't advance the registers. */
387 env->cc_op = 0;
388 env->retxl = s2;
389 return s1;
390 }
391 } else {
392 /* Unequal. CC={1,2}, and advance the registers. Note that
393 the terminator need not be zero, but the string that contains
394 the terminator is by definition "low". */
395 env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
396 env->retxl = s2 + len;
397 return s1 + len;
8ef7f78e 398 }
8ef7f78e
BS
399 }
400
aa31bf60
RH
401 /* CPU-determined bytes equal; advance the registers. */
402 env->cc_op = 3;
403 env->retxl = s2 + len;
404 return s1 + len;
8ef7f78e
BS
405}
406
407/* move page */
19b0516f 408void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
8ef7f78e
BS
409{
410 /* XXX missing r0 handling */
ee6c38d5 411 env->cc_op = 0;
8ef7f78e 412#ifdef CONFIG_USER_ONLY
ee6c38d5 413 memmove(g2h(r1), g2h(r2), TARGET_PAGE_SIZE);
8ef7f78e
BS
414#else
415 mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
416#endif
417}
418
419/* string copy (c is string terminator) */
aa31bf60 420uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
8ef7f78e 421{
aa31bf60 422 uint32_t len;
8ef7f78e
BS
423
424 c = c & 0xff;
aa31bf60
RH
425 d = fix_address(env, d);
426 s = fix_address(env, s);
427
428 /* Lest we fail to service interrupts in a timely manner, limit the
e03ba136 429 amount of work we're willing to do. For now, let's cap at 8k. */
aa31bf60
RH
430 for (len = 0; len < 0x2000; ++len) {
431 uint8_t v = cpu_ldub_data(env, s + len);
432 cpu_stb_data(env, d + len, v);
8ef7f78e 433 if (v == c) {
aa31bf60
RH
434 /* Complete. Set CC=1 and advance R1. */
435 env->cc_op = 1;
436 env->retxl = s;
437 return d + len;
8ef7f78e 438 }
8ef7f78e 439 }
aa31bf60
RH
440
441 /* Incomplete. Set CC=3 and signal to advance R1 and R2. */
442 env->cc_op = 3;
443 env->retxl = s + len;
444 return d + len;
8ef7f78e
BS
445}
446
19b0516f
BS
447static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
448 uint32_t mask)
8ef7f78e
BS
449{
450 int pos = 24; /* top of the lower half of r1 */
451 uint64_t rmask = 0xff000000ULL;
452 uint8_t val = 0;
453 int ccd = 0;
454 uint32_t cc = 0;
455
456 while (mask) {
457 if (mask & 8) {
458 env->regs[r1] &= ~rmask;
19b0516f 459 val = cpu_ldub_data(env, address);
8ef7f78e
BS
460 if ((val & 0x80) && !ccd) {
461 cc = 1;
462 }
463 ccd = 1;
464 if (val && cc == 0) {
465 cc = 2;
466 }
467 env->regs[r1] |= (uint64_t)val << pos;
468 address++;
469 }
470 mask = (mask << 1) & 0xf;
471 pos -= 8;
472 rmask >>= 8;
473 }
474
475 return cc;
476}
477
478/* execute instruction
479 this instruction executes an insn modified with the contents of r1
480 it does not change the executed instruction in memory
481 it does not change the program counter
482 in other words: tricky...
483 currently implemented by interpreting the cases it is most commonly used in
484*/
19b0516f
BS
485uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
486 uint64_t addr, uint64_t ret)
8ef7f78e 487{
a47dddd7 488 S390CPU *cpu = s390_env_get_cpu(env);
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:
a47dddd7 540 cpu_abort(CPU(cpu), "EXECUTE on instruction prefix 0x%x not implemented\n",
8ef7f78e
BS
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 812{
00c8cb0a 813 S390CPU *cpu = s390_env_get_cpu(env);
8ef7f78e
BS
814 int i;
815 uint64_t src = a2;
816
817 for (i = r1;; i = (i + 1) % 16) {
19b0516f 818 env->cregs[i] = cpu_ldq_data(env, src);
8ef7f78e
BS
819 HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
820 i, src, env->cregs[i]);
821 src += sizeof(uint64_t);
822
823 if (i == r3) {
824 break;
825 }
826 }
827
00c8cb0a 828 tlb_flush(CPU(cpu), 1);
8ef7f78e
BS
829}
830
19b0516f 831void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e 832{
00c8cb0a 833 S390CPU *cpu = s390_env_get_cpu(env);
8ef7f78e
BS
834 int i;
835 uint64_t src = a2;
836
837 for (i = r1;; i = (i + 1) % 16) {
19b0516f
BS
838 env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
839 cpu_ldl_data(env, src);
8ef7f78e
BS
840 src += sizeof(uint32_t);
841
842 if (i == r3) {
843 break;
844 }
845 }
846
00c8cb0a 847 tlb_flush(CPU(cpu), 1);
8ef7f78e
BS
848}
849
19b0516f 850void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
851{
852 int i;
853 uint64_t dest = a2;
854
855 for (i = r1;; i = (i + 1) % 16) {
19b0516f 856 cpu_stq_data(env, dest, env->cregs[i]);
8ef7f78e
BS
857 dest += sizeof(uint64_t);
858
859 if (i == r3) {
860 break;
861 }
862 }
863}
864
19b0516f 865void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
8ef7f78e
BS
866{
867 int i;
868 uint64_t dest = a2;
869
870 for (i = r1;; i = (i + 1) % 16) {
19b0516f 871 cpu_stl_data(env, dest, env->cregs[i]);
8ef7f78e
BS
872 dest += sizeof(uint32_t);
873
874 if (i == r3) {
875 break;
876 }
877 }
878}
879
880uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
881{
882 /* XXX implement */
883
884 return 0;
885}
886
887/* insert storage key extended */
19b0516f 888uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
8ef7f78e 889{
19b0516f 890 uint64_t addr = get_address(env, 0, 0, r2);
8ef7f78e
BS
891
892 if (addr > ram_size) {
893 return 0;
894 }
895
896 return env->storage_keys[addr / TARGET_PAGE_SIZE];
897}
898
899/* set storage key extended */
2bbde27f 900void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
8ef7f78e 901{
19b0516f 902 uint64_t addr = get_address(env, 0, 0, r2);
8ef7f78e
BS
903
904 if (addr > ram_size) {
905 return;
906 }
907
908 env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
909}
910
911/* reset reference bit extended */
5cc69c54 912uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
8ef7f78e
BS
913{
914 uint8_t re;
915 uint8_t key;
916
917 if (r2 > ram_size) {
918 return 0;
919 }
920
921 key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
922 re = key & (SK_R | SK_C);
923 env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
924
925 /*
926 * cc
927 *
928 * 0 Reference bit zero; change bit zero
929 * 1 Reference bit zero; change bit one
930 * 2 Reference bit one; change bit zero
931 * 3 Reference bit one; change bit one
932 */
933
934 return re >> 1;
935}
936
937/* compare and swap and purge */
3d596f49 938uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
8ef7f78e 939{
00c8cb0a 940 S390CPU *cpu = s390_env_get_cpu(env);
8ef7f78e
BS
941 uint32_t cc;
942 uint32_t o1 = env->regs[r1];
3d596f49 943 uint64_t a2 = r2 & ~3ULL;
19b0516f 944 uint32_t o2 = cpu_ldl_data(env, a2);
8ef7f78e
BS
945
946 if (o1 == o2) {
19b0516f 947 cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
3d596f49 948 if (r2 & 0x3) {
8ef7f78e 949 /* flush TLB / ALB */
00c8cb0a 950 tlb_flush(CPU(cpu), 1);
8ef7f78e
BS
951 }
952 cc = 0;
953 } else {
954 env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
955 cc = 1;
956 }
957
958 return cc;
959}
960
19b0516f
BS
961static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
962 uint64_t mode1, uint64_t a2, uint64_t mode2)
8ef7f78e 963{
2efc6be2 964 CPUState *cs = CPU(s390_env_get_cpu(env));
8ef7f78e
BS
965 target_ulong src, dest;
966 int flags, cc = 0, i;
967
968 if (!l) {
969 return 0;
970 } else if (l > 256) {
971 /* max 256 */
972 l = 256;
973 cc = 3;
974 }
975
976 if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
5638d180 977 cpu_loop_exit(CPU(s390_env_get_cpu(env)));
8ef7f78e
BS
978 }
979 dest |= a1 & ~TARGET_PAGE_MASK;
980
981 if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
5638d180 982 cpu_loop_exit(CPU(s390_env_get_cpu(env)));
8ef7f78e
BS
983 }
984 src |= a2 & ~TARGET_PAGE_MASK;
985
986 /* XXX replace w/ memcpy */
987 for (i = 0; i < l; i++) {
988 /* XXX be more clever */
989 if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
990 (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
19b0516f 991 mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
8ef7f78e
BS
992 break;
993 }
db3be60d 994 stb_phys(cs->as, dest + i, ldub_phys(cs->as, src + i));
8ef7f78e
BS
995 }
996
997 return cc;
998}
999
19b0516f 1000uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
8ef7f78e
BS
1001{
1002 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1003 __func__, l, a1, a2);
1004
19b0516f 1005 return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
8ef7f78e
BS
1006}
1007
19b0516f 1008uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
8ef7f78e
BS
1009{
1010 HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1011 __func__, l, a1, a2);
1012
19b0516f 1013 return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
8ef7f78e
BS
1014}
1015
1016/* invalidate pte */
19b0516f 1017void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
8ef7f78e 1018{
2efc6be2 1019 CPUState *cs = CPU(s390_env_get_cpu(env));
8ef7f78e
BS
1020 uint64_t page = vaddr & TARGET_PAGE_MASK;
1021 uint64_t pte = 0;
1022
1023 /* XXX broadcast to other CPUs */
1024
1025 /* XXX Linux is nice enough to give us the exact pte address.
1026 According to spec we'd have to find it out ourselves */
1027 /* XXX Linux is fine with overwriting the pte, the spec requires
1028 us to only set the invalid bit */
f606604f 1029 stq_phys(cs->as, pte_addr, pte | _PAGE_INVALID);
8ef7f78e
BS
1030
1031 /* XXX we exploit the fact that Linux passes the exact virtual
1032 address here - it's not obliged to! */
31b030d4 1033 tlb_flush_page(cs, page);
8ef7f78e
BS
1034
1035 /* XXX 31-bit hack */
1036 if (page & 0x80000000) {
31b030d4 1037 tlb_flush_page(cs, page & ~0x80000000);
8ef7f78e 1038 } else {
31b030d4 1039 tlb_flush_page(cs, page | 0x80000000);
8ef7f78e
BS
1040 }
1041}
1042
1043/* flush local tlb */
19b0516f 1044void HELPER(ptlb)(CPUS390XState *env)
8ef7f78e 1045{
00c8cb0a
AF
1046 S390CPU *cpu = s390_env_get_cpu(env);
1047
1048 tlb_flush(CPU(cpu), 1);
8ef7f78e
BS
1049}
1050
1051/* store using real address */
204504e2 1052void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
8ef7f78e 1053{
2efc6be2
AF
1054 CPUState *cs = CPU(s390_env_get_cpu(env));
1055
5ce5944d 1056 stw_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1);
8ef7f78e
BS
1057}
1058
1059/* load real address */
d8fe4a9c 1060uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
8ef7f78e 1061{
27103424 1062 CPUState *cs = CPU(s390_env_get_cpu(env));
8ef7f78e 1063 uint32_t cc = 0;
27103424 1064 int old_exc = cs->exception_index;
8ef7f78e
BS
1065 uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1066 uint64_t ret;
1067 int flags;
1068
1069 /* XXX incomplete - has more corner cases */
1070 if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
1071 program_interrupt(env, PGM_SPECIAL_OP, 2);
1072 }
1073
27103424 1074 cs->exception_index = old_exc;
8ef7f78e
BS
1075 if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1076 cc = 3;
1077 }
27103424 1078 if (cs->exception_index == EXCP_PGM) {
8ef7f78e
BS
1079 ret = env->int_pgm_code | 0x80000000;
1080 } else {
1081 ret |= addr & ~TARGET_PAGE_MASK;
1082 }
27103424 1083 cs->exception_index = old_exc;
8ef7f78e 1084
d8fe4a9c
RH
1085 env->cc_op = cc;
1086 return ret;
8ef7f78e 1087}
8ef7f78e 1088#endif
This page took 0.33813 seconds and 4 git commands to generate.