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