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