mem_helper.c 27.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
/*
 *  S/390 memory access helper routines
 *
 *  Copyright (c) 2009 Ulrich Hecht
 *  Copyright (c) 2009 Alexander Graf
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 */

#include "cpu.h"
#include "helper.h"

/*****************************************************************************/
/* Softmmu support */
#if !defined(CONFIG_USER_ONLY)
27
#include "exec/softmmu_exec.h"
28 29 30 31

#define MMUSUFFIX _mmu

#define SHIFT 0
32
#include "exec/softmmu_template.h"
33 34

#define SHIFT 1
35
#include "exec/softmmu_template.h"
36 37

#define SHIFT 2
38
#include "exec/softmmu_template.h"
39 40

#define SHIFT 3
41
#include "exec/softmmu_template.h"
42 43 44 45 46

/* try to fill the TLB and return an exception if error. If retaddr is
   NULL, it means that the function was called in C code (i.e. not
   from generated code or from helper.c) */
/* XXX: fix it to restore all registers */
47
void tlb_fill(CPUS390XState *env, target_ulong addr, int is_write, int mmu_idx,
48 49 50 51 52 53 54 55
              uintptr_t retaddr)
{
    int ret;

    ret = cpu_s390x_handle_mmu_fault(env, addr, is_write, mmu_idx);
    if (unlikely(ret != 0)) {
        if (likely(retaddr)) {
            /* now we have a real cpu fault */
B
Blue Swirl 已提交
56
            cpu_restore_state(env, retaddr);
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
        }
        cpu_loop_exit(env);
    }
}

#endif

/* #define DEBUG_HELPER */
#ifdef DEBUG_HELPER
#define HELPER_LOG(x...) qemu_log(x)
#else
#define HELPER_LOG(x...)
#endif

#ifndef CONFIG_USER_ONLY
static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest,
                            uint8_t byte)
{
A
Avi Kivity 已提交
75 76
    hwaddr dest_phys;
    hwaddr len = l;
77 78 79 80 81
    void *dest_p;
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
    int flags;

    if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
82
        cpu_stb_data(env, dest, byte);
83 84 85 86 87 88 89 90 91 92 93 94 95 96
        cpu_abort(env, "should never reach here");
    }
    dest_phys |= dest & ~TARGET_PAGE_MASK;

    dest_p = cpu_physical_memory_map(dest_phys, &len, 1);

    memset(dest_p, byte, len);

    cpu_physical_memory_unmap(dest_p, 1, len, len);
}

static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
                             uint64_t src)
{
A
Avi Kivity 已提交
97 98 99
    hwaddr dest_phys;
    hwaddr src_phys;
    hwaddr len = l;
100 101 102 103 104 105
    void *dest_p;
    void *src_p;
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
    int flags;

    if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
106
        cpu_stb_data(env, dest, 0);
107 108 109 110 111
        cpu_abort(env, "should never reach here");
    }
    dest_phys |= dest & ~TARGET_PAGE_MASK;

    if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) {
112
        cpu_ldub_data(env, src);
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
        cpu_abort(env, "should never reach here");
    }
    src_phys |= src & ~TARGET_PAGE_MASK;

    dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
    src_p = cpu_physical_memory_map(src_phys, &len, 0);

    memmove(dest_p, src_p, len);

    cpu_physical_memory_unmap(dest_p, 1, len, len);
    cpu_physical_memory_unmap(src_p, 0, len, len);
}
#endif

/* and on array */
128 129
uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
                    uint64_t src)
130 131 132 133 134 135 136 137
{
    int i;
    unsigned char x;
    uint32_t cc = 0;

    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
               __func__, l, dest, src);
    for (i = 0; i <= l; i++) {
138
        x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
139 140 141
        if (x) {
            cc = 1;
        }
142
        cpu_stb_data(env, dest + i, x);
143 144 145 146 147
    }
    return cc;
}

/* xor on array */
148 149
uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
                    uint64_t src)
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
{
    int i;
    unsigned char x;
    uint32_t cc = 0;

    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
               __func__, l, dest, src);

#ifndef CONFIG_USER_ONLY
    /* xor with itself is the same as memset(0) */
    if ((l > 32) && (src == dest) &&
        (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) {
        mvc_fast_memset(env, l + 1, dest, 0);
        return 0;
    }
#else
    if (src == dest) {
        memset(g2h(dest), 0, l + 1);
        return 0;
    }
#endif

    for (i = 0; i <= l; i++) {
173
        x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
174 175 176
        if (x) {
            cc = 1;
        }
177
        cpu_stb_data(env, dest + i, x);
178 179 180 181 182
    }
    return cc;
}

/* or on array */
183 184
uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
                    uint64_t src)
185 186 187 188 189 190 191 192
{
    int i;
    unsigned char x;
    uint32_t cc = 0;

    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
               __func__, l, dest, src);
    for (i = 0; i <= l; i++) {
193
        x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
194 195 196
        if (x) {
            cc = 1;
        }
197
        cpu_stb_data(env, dest + i, x);
198 199 200 201 202
    }
    return cc;
}

/* memmove */
203
void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
204 205 206 207 208 209 210 211 212 213 214 215 216
{
    int i = 0;
    int x = 0;
    uint32_t l_64 = (l + 1) / 8;

    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
               __func__, l, dest, src);

#ifndef CONFIG_USER_ONLY
    if ((l > 32) &&
        (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) &&
        (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) {
        if (dest == (src + 1)) {
217
            mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src));
218 219 220 221 222 223 224 225
            return;
        } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
            mvc_fast_memmove(env, l + 1, dest, src);
            return;
        }
    }
#else
    if (dest == (src + 1)) {
226
        memset(g2h(dest), cpu_ldub_data(env, src), l + 1);
227 228 229 230 231 232 233 234 235 236
        return;
    } else {
        memmove(g2h(dest), g2h(src), l + 1);
        return;
    }
#endif

    /* handle the parts that fit into 8-byte loads/stores */
    if (dest != (src + 1)) {
        for (i = 0; i < l_64; i++) {
237
            cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
238 239 240 241 242 243
            x += 8;
        }
    }

    /* slow version crossing pages with byte accesses */
    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 252 253 254 255 256 257
{
    int i;
    unsigned char x, y;
    uint32_t cc;

    HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
               __func__, l, s1, s2);
    for (i = 0; i <= l; i++) {
258 259
        x = cpu_ldub_data(env, s1 + i);
        y = cpu_ldub_data(env, s2 + i);
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
        HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
        if (x < y) {
            cc = 1;
            goto done;
        } else if (x > y) {
            cc = 2;
            goto done;
        }
    }
    cc = 0;
 done:
    HELPER_LOG("\n");
    return cc;
}

/* compare logical under mask */
276 277
uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
                     uint64_t addr)
278 279 280 281 282 283 284 285 286
{
    uint8_t r, d;
    uint32_t cc;

    HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
               mask, addr);
    cc = 0;
    while (mask) {
        if (mask & 8) {
287
            d = cpu_ldub_data(env, addr);
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306
            r = (r1 & 0xff000000UL) >> 24;
            HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
                       addr);
            if (r < d) {
                cc = 1;
                break;
            } else if (r > d) {
                cc = 2;
                break;
            }
            addr++;
        }
        mask = (mask << 1) & 0xf;
        r1 <<= 8;
    }
    HELPER_LOG("\n");
    return cc;
}

307 308 309 310 311 312 313 314 315
static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
{
    /* 31-Bit mode */
    if (!(env->psw.mask & PSW_MASK_64)) {
        a &= 0x7fffffff;
    }
    return a;
}

316
static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
317 318 319 320 321 322 323 324
{
    uint64_t r = d2;
    if (x2) {
        r += env->regs[x2];
    }
    if (b2) {
        r += env->regs[b2];
    }
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) */
R
Richard Henderson 已提交
334 335
uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
                      uint64_t str)
336
{
R
Richard Henderson 已提交
337 338
    uint32_t len;
    uint8_t v, c = r0;
339

R
Richard Henderson 已提交
340 341
    str = fix_address(env, str);
    end = fix_address(env, end);
342

R
Richard Henderson 已提交
343 344 345 346
    /* Assume for now that R2 is unmodified.  */
    env->retxl = str;

    /* 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.  */
R
Richard Henderson 已提交
348 349 350 351 352 353 354 355 356 357 358
    for (len = 0; len < 0x2000; ++len) {
        if (str + len == end) {
            /* Character not found.  R1 & R2 are unmodified.  */
            env->cc_op = 2;
            return end;
        }
        v = cpu_ldub_data(env, str + len);
        if (v == c) {
            /* Character found.  Set R1 to the location; R2 is unmodified.  */
            env->cc_op = 1;
            return str + len;
359 360 361
        }
    }

R
Richard Henderson 已提交
362 363 364 365
    /* CPU-determined bytes processed.  Advance R2 to next byte to process.  */
    env->retxl = str + len;
    env->cc_op = 3;
    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 375 376 377
    s1 = fix_address(env, s1);
    s2 = fix_address(env, s2);

    /* 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 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
    for (len = 0; len < 0x2000; ++len) {
        uint8_t v1 = cpu_ldub_data(env, s1 + len);
        uint8_t v2 = cpu_ldub_data(env, s2 + len);
        if (v1 == v2) {
            if (v1 == c) {
                /* Equal.  CC=0, and don't advance the registers.  */
                env->cc_op = 0;
                env->retxl = s2;
                return s1;
            }
        } else {
            /* Unequal.  CC={1,2}, and advance the registers.  Note that
               the terminator need not be zero, but the string that contains
               the terminator is by definition "low".  */
            env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
            env->retxl = s2 + len;
            return s1 + len;
396 397 398
        }
    }

399 400 401 402
    /* CPU-determined bytes equal; advance the registers.  */
    env->cc_op = 3;
    env->retxl = s2 + len;
    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 */
R
Richard Henderson 已提交
409
    env->cc_op = 0;
410
#ifdef CONFIG_USER_ONLY
R
Richard Henderson 已提交
411
    memmove(g2h(r1), g2h(r2), TARGET_PAGE_SIZE);
412 413 414 415 416 417
#else
    mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
#endif
}

/* 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 424 425 426
    d = fix_address(env, d);
    s = fix_address(env, s);

    /* 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 429 430
    for (len = 0; len < 0x2000; ++len) {
        uint8_t v = cpu_ldub_data(env, s + len);
        cpu_stb_data(env, d + len, v);
431
        if (v == c) {
432 433 434 435
            /* Complete.  Set CC=1 and advance R1.  */
            env->cc_op = 1;
            env->retxl = s;
            return d + len;
436 437
        }
    }
438 439 440 441 442

    /* Incomplete.  Set CC=3 and signal to advance R1 and R2.  */
    env->cc_op = 3;
    env->retxl = s + len;
    return d + len;
443 444
}

445 446
static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
                           uint32_t mask)
447 448 449 450 451 452 453 454 455 456
{
    int pos = 24; /* top of the lower half of r1 */
    uint64_t rmask = 0xff000000ULL;
    uint8_t val = 0;
    int ccd = 0;
    uint32_t cc = 0;

    while (mask) {
        if (mask & 8) {
            env->regs[r1] &= ~rmask;
457
            val = cpu_ldub_data(env, address);
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
            if ((val & 0x80) && !ccd) {
                cc = 1;
            }
            ccd = 1;
            if (val && cc == 0) {
                cc = 2;
            }
            env->regs[r1] |= (uint64_t)val << pos;
            address++;
        }
        mask = (mask << 1) & 0xf;
        pos -= 8;
        rmask >>= 8;
    }

    return cc;
}

/* execute instruction
   this instruction executes an insn modified with the contents of r1
   it does not change the executed instruction in memory
   it does not change the program counter
   in other words: tricky...
   currently implemented by interpreting the cases it is most commonly used in
*/
483 484
uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
                    uint64_t addr, uint64_t ret)
485
{
486
    uint16_t insn = cpu_lduw_code(env, addr);
487 488 489 490 491 492 493

    HELPER_LOG("%s: v1 0x%lx addr 0x%lx insn 0x%x\n", __func__, v1, addr,
               insn);
    if ((insn & 0xf0ff) == 0xd000) {
        uint32_t l, insn2, b1, b2, d1, d2;

        l = v1 & 0xff;
494
        insn2 = cpu_ldl_code(env, addr + 2);
495 496 497 498 499 500
        b1 = (insn2 >> 28) & 0xf;
        b2 = (insn2 >> 12) & 0xf;
        d1 = (insn2 >> 16) & 0xfff;
        d2 = insn2 & 0xfff;
        switch (insn & 0xf00) {
        case 0x200:
501 502
            helper_mvc(env, l, get_address(env, 0, b1, d1),
                       get_address(env, 0, b2, d2));
503 504
            break;
        case 0x500:
505 506
            cc = helper_clc(env, l, get_address(env, 0, b1, d1),
                            get_address(env, 0, b2, d2));
507 508
            break;
        case 0x700:
509 510
            cc = helper_xc(env, l, get_address(env, 0, b1, d1),
                           get_address(env, 0, b2, d2));
511 512
            break;
        case 0xc00:
513 514
            helper_tr(env, l, get_address(env, 0, b1, d1),
                      get_address(env, 0, b2, d2));
515 516 517 518 519 520 521 522 523
            break;
        default:
            goto abort;
        }
    } else if ((insn & 0xff00) == 0x0a00) {
        /* supervisor call */
        HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
        env->psw.addr = ret - 4;
        env->int_svc_code = (insn | v1) & 0xff;
524
        env->int_svc_ilen = 4;
525
        helper_exception(env, EXCP_SVC);
526 527 528
    } else if ((insn & 0xff00) == 0xbf00) {
        uint32_t insn2, r1, r3, b2, d2;

529
        insn2 = cpu_ldl_code(env, addr + 2);
530 531 532 533
        r1 = (insn2 >> 20) & 0xf;
        r3 = (insn2 >> 16) & 0xf;
        b2 = (insn2 >> 12) & 0xf;
        d2 = insn2 & 0xfff;
534
        cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
535 536 537 538 539 540 541 542 543
    } else {
    abort:
        cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
                  insn);
    }
    return cc;
}

/* load access registers r1 to r3 from memory at a2 */
544
void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
545 546 547 548
{
    int i;

    for (i = r1;; i = (i + 1) % 16) {
549
        env->aregs[i] = cpu_ldl_data(env, a2);
550 551 552 553 554 555 556 557 558
        a2 += 4;

        if (i == r3) {
            break;
        }
    }
}

/* store access registers r1 to r3 in memory at a2 */
559
void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
560 561 562 563
{
    int i;

    for (i = r1;; i = (i + 1) % 16) {
564
        cpu_stl_data(env, a2, env->aregs[i]);
565 566 567 568 569 570 571 572 573
        a2 += 4;

        if (i == r3) {
            break;
        }
    }
}

/* move long */
574
uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
575 576
{
    uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
577
    uint64_t dest = get_address_31fix(env, r1);
578
    uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
579
    uint64_t src = get_address_31fix(env, r2);
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596
    uint8_t pad = src >> 24;
    uint8_t v;
    uint32_t cc;

    if (destlen == srclen) {
        cc = 0;
    } else if (destlen < srclen) {
        cc = 1;
    } else {
        cc = 2;
    }

    if (srclen > destlen) {
        srclen = destlen;
    }

    for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
597 598
        v = cpu_ldub_data(env, src);
        cpu_stb_data(env, dest, v);
599 600 601
    }

    for (; destlen; dest++, destlen--) {
602
        cpu_stb_data(env, dest, pad);
603 604 605 606 607 608 609 610 611 612 613 614
    }

    env->regs[r1 + 1] = destlen;
    /* can't use srclen here, we trunc'ed it */
    env->regs[r2 + 1] -= src - env->regs[r2];
    env->regs[r1] = dest;
    env->regs[r2] = src;

    return cc;
}

/* move long extended another memcopy insn with more bells and whistles */
615 616
uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
                       uint32_t r3)
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
{
    uint64_t destlen = env->regs[r1 + 1];
    uint64_t dest = env->regs[r1];
    uint64_t srclen = env->regs[r3 + 1];
    uint64_t src = env->regs[r3];
    uint8_t pad = a2 & 0xff;
    uint8_t v;
    uint32_t cc;

    if (!(env->psw.mask & PSW_MASK_64)) {
        destlen = (uint32_t)destlen;
        srclen = (uint32_t)srclen;
        dest &= 0x7fffffff;
        src &= 0x7fffffff;
    }

    if (destlen == srclen) {
        cc = 0;
    } else if (destlen < srclen) {
        cc = 1;
    } else {
        cc = 2;
    }

    if (srclen > destlen) {
        srclen = destlen;
    }

    for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
646 647
        v = cpu_ldub_data(env, src);
        cpu_stb_data(env, dest, v);
648 649 650
    }

    for (; destlen; dest++, destlen--) {
651
        cpu_stb_data(env, dest, pad);
652 653 654 655 656 657 658 659 660 661 662 663 664
    }

    env->regs[r1 + 1] = destlen;
    /* can't use srclen here, we trunc'ed it */
    /* FIXME: 31-bit mode! */
    env->regs[r3 + 1] -= src - env->regs[r3];
    env->regs[r1] = dest;
    env->regs[r3] = src;

    return cc;
}

/* compare logical long extended memcompare insn with padding */
665 666
uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
                       uint32_t r3)
667 668
{
    uint64_t destlen = env->regs[r1 + 1];
669
    uint64_t dest = get_address_31fix(env, r1);
670
    uint64_t srclen = env->regs[r3 + 1];
671
    uint64_t src = get_address_31fix(env, r3);
672 673 674 675 676 677 678 679 680 681 682 683 684
    uint8_t pad = a2 & 0xff;
    uint8_t v1 = 0, v2 = 0;
    uint32_t cc = 0;

    if (!(destlen || srclen)) {
        return cc;
    }

    if (srclen > destlen) {
        srclen = destlen;
    }

    for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
685 686
        v1 = srclen ? cpu_ldub_data(env, src) : pad;
        v2 = destlen ? cpu_ldub_data(env, dest) : pad;
687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
        if (v1 != v2) {
            cc = (v1 < v2) ? 1 : 2;
            break;
        }
    }

    env->regs[r1 + 1] = destlen;
    /* can't use srclen here, we trunc'ed it */
    env->regs[r3 + 1] -= src - env->regs[r3];
    env->regs[r1] = dest;
    env->regs[r3] = src;

    return cc;
}

/* checksum */
R
Richard Henderson 已提交
703 704
uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
                      uint64_t src, uint64_t src_len)
705
{
R
Richard Henderson 已提交
706 707
    uint64_t max_len, len;
    uint64_t cksm = (uint32_t)r1;
708

R
Richard Henderson 已提交
709
    /* Lest we fail to service interrupts in a timely manner, limit the
710
       amount of work we're willing to do.  For now, let's cap at 8k.  */
R
Richard Henderson 已提交
711
    max_len = (src_len > 0x2000 ? 0x2000 : src_len);
712

R
Richard Henderson 已提交
713 714 715
    /* Process full words as available.  */
    for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
        cksm += (uint32_t)cpu_ldl_data(env, src);
716 717
    }

R
Richard Henderson 已提交
718
    switch (max_len - len) {
719
    case 1:
720
        cksm += cpu_ldub_data(env, src) << 24;
R
Richard Henderson 已提交
721
        len += 1;
722 723
        break;
    case 2:
724
        cksm += cpu_lduw_data(env, src) << 16;
R
Richard Henderson 已提交
725
        len += 2;
726 727
        break;
    case 3:
728 729
        cksm += cpu_lduw_data(env, src) << 16;
        cksm += cpu_ldub_data(env, src + 2) << 8;
R
Richard Henderson 已提交
730
        len += 3;
731 732 733
        break;
    }

R
Richard Henderson 已提交
734 735 736 737 738 739 740 741
    /* Fold the carry from the checksum.  Note that we can see carry-out
       during folding more than once (but probably not more than twice).  */
    while (cksm > 0xffffffffull) {
        cksm = (uint32_t)cksm + (cksm >> 32);
    }

    /* Indicate whether or not we've processed everything.  */
    env->cc_op = (len == src_len ? 0 : 3);
742

R
Richard Henderson 已提交
743 744 745
    /* Return both cksm and processed length.  */
    env->retxl = cksm;
    return len;
746 747
}

748 749
void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
                  uint64_t src)
750 751 752 753 754 755 756 757 758 759
{
    int len_dest = len >> 4;
    int len_src = len & 0xf;
    uint8_t b;
    int second_nibble = 0;

    dest += len_dest;
    src += len_src;

    /* last byte is special, it only flips the nibbles */
760 761
    b = cpu_ldub_data(env, src);
    cpu_stb_data(env, dest, (b << 4) | (b >> 4));
762 763 764 765 766 767 768 769 770
    src--;
    len_src--;

    /* now pad every nibble with 0xf0 */

    while (len_dest > 0) {
        uint8_t cur_byte = 0;

        if (len_src > 0) {
771
            cur_byte = cpu_ldub_data(env, src);
772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
        }

        len_dest--;
        dest--;

        /* only advance one nibble at a time */
        if (second_nibble) {
            cur_byte >>= 4;
            len_src--;
            src--;
        }
        second_nibble = !second_nibble;

        /* digit */
        cur_byte = (cur_byte & 0xf);
        /* zone bits */
        cur_byte |= 0xf0;

790
        cpu_stb_data(env, dest, cur_byte);
791 792 793
    }
}

794 795
void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
                uint64_t trans)
796 797 798 799
{
    int i;

    for (i = 0; i <= len; i++) {
800 801
        uint8_t byte = cpu_ldub_data(env, array + i);
        uint8_t new_byte = cpu_ldub_data(env, trans + byte);
802

803
        cpu_stb_data(env, array + i, new_byte);
804 805 806 807
    }
}

#if !defined(CONFIG_USER_ONLY)
808
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
809 810 811 812 813
{
    int i;
    uint64_t src = a2;

    for (i = r1;; i = (i + 1) % 16) {
814
        env->cregs[i] = cpu_ldq_data(env, src);
815 816 817 818 819 820 821 822 823 824 825 826
        HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
                   i, src, env->cregs[i]);
        src += sizeof(uint64_t);

        if (i == r3) {
            break;
        }
    }

    tlb_flush(env, 1);
}

827
void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
828 829 830 831 832
{
    int i;
    uint64_t src = a2;

    for (i = r1;; i = (i + 1) % 16) {
833 834
        env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
            cpu_ldl_data(env, src);
835 836 837 838 839 840 841 842 843 844
        src += sizeof(uint32_t);

        if (i == r3) {
            break;
        }
    }

    tlb_flush(env, 1);
}

845
void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
846 847 848 849 850
{
    int i;
    uint64_t dest = a2;

    for (i = r1;; i = (i + 1) % 16) {
851
        cpu_stq_data(env, dest, env->cregs[i]);
852 853 854 855 856 857 858 859
        dest += sizeof(uint64_t);

        if (i == r3) {
            break;
        }
    }
}

860
void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
861 862 863 864 865
{
    int i;
    uint64_t dest = a2;

    for (i = r1;; i = (i + 1) % 16) {
866
        cpu_stl_data(env, dest, env->cregs[i]);
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
        dest += sizeof(uint32_t);

        if (i == r3) {
            break;
        }
    }
}

uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
{
    /* XXX implement */

    return 0;
}

/* insert storage key extended */
883
uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
884
{
885
    uint64_t addr = get_address(env, 0, 0, r2);
886 887 888 889 890 891 892 893 894

    if (addr > ram_size) {
        return 0;
    }

    return env->storage_keys[addr / TARGET_PAGE_SIZE];
}

/* set storage key extended */
R
Richard Henderson 已提交
895
void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
896
{
897
    uint64_t addr = get_address(env, 0, 0, r2);
898 899 900 901 902 903 904 905 906

    if (addr > ram_size) {
        return;
    }

    env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
}

/* reset reference bit extended */
R
Richard Henderson 已提交
907
uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
{
    uint8_t re;
    uint8_t key;

    if (r2 > ram_size) {
        return 0;
    }

    key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
    re = key & (SK_R | SK_C);
    env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);

    /*
     * cc
     *
     * 0  Reference bit zero; change bit zero
     * 1  Reference bit zero; change bit one
     * 2  Reference bit one; change bit zero
     * 3  Reference bit one; change bit one
     */

    return re >> 1;
}

/* compare and swap and purge */
R
Richard Henderson 已提交
933
uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
934 935 936
{
    uint32_t cc;
    uint32_t o1 = env->regs[r1];
R
Richard Henderson 已提交
937
    uint64_t a2 = r2 & ~3ULL;
938
    uint32_t o2 = cpu_ldl_data(env, a2);
939 940

    if (o1 == o2) {
941
        cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
R
Richard Henderson 已提交
942
        if (r2 & 0x3) {
943 944 945 946 947 948 949 950 951 952 953 954
            /* flush TLB / ALB */
            tlb_flush(env, 1);
        }
        cc = 0;
    } else {
        env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
        cc = 1;
    }

    return cc;
}

955 956
static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
                        uint64_t mode1, uint64_t a2, uint64_t mode2)
957
{
958
    CPUState *cs = ENV_GET_CPU(env);
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
    target_ulong src, dest;
    int flags, cc = 0, i;

    if (!l) {
        return 0;
    } else if (l > 256) {
        /* max 256 */
        l = 256;
        cc = 3;
    }

    if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
        cpu_loop_exit(env);
    }
    dest |= a1 & ~TARGET_PAGE_MASK;

    if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
        cpu_loop_exit(env);
    }
    src |= a2 & ~TARGET_PAGE_MASK;

    /* XXX replace w/ memcpy */
    for (i = 0; i < l; i++) {
        /* XXX be more clever */
        if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
            (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
985
            mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
986 987
            break;
        }
988
        stb_phys(dest + i, ldub_phys(cs->as, src + i));
989 990 991 992 993
    }

    return cc;
}

994
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
995 996 997 998
{
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
               __func__, l, a1, a2);

999
    return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
1000 1001
}

1002
uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1003 1004 1005 1006
{
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
               __func__, l, a1, a2);

1007
    return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
1008 1009 1010
}

/* invalidate pte */
1011
void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036
{
    uint64_t page = vaddr & TARGET_PAGE_MASK;
    uint64_t pte = 0;

    /* XXX broadcast to other CPUs */

    /* XXX Linux is nice enough to give us the exact pte address.
       According to spec we'd have to find it out ourselves */
    /* XXX Linux is fine with overwriting the pte, the spec requires
       us to only set the invalid bit */
    stq_phys(pte_addr, pte | _PAGE_INVALID);

    /* XXX we exploit the fact that Linux passes the exact virtual
       address here - it's not obliged to! */
    tlb_flush_page(env, page);

    /* XXX 31-bit hack */
    if (page & 0x80000000) {
        tlb_flush_page(env, page & ~0x80000000);
    } else {
        tlb_flush_page(env, page | 0x80000000);
    }
}

/* flush local tlb */
1037
void HELPER(ptlb)(CPUS390XState *env)
1038 1039 1040 1041 1042
{
    tlb_flush(env, 1);
}

/* store using real address */
R
Richard Henderson 已提交
1043
void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1044
{
R
Richard Henderson 已提交
1045
    stw_phys(get_address(env, 0, 0, addr), (uint32_t)v1);
1046 1047 1048
}

/* load real address */
R
Richard Henderson 已提交
1049
uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072
{
    uint32_t cc = 0;
    int old_exc = env->exception_index;
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
    uint64_t ret;
    int flags;

    /* XXX incomplete - has more corner cases */
    if (!(env->psw.mask & PSW_MASK_64) && (addr >> 32)) {
        program_interrupt(env, PGM_SPECIAL_OP, 2);
    }

    env->exception_index = old_exc;
    if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
        cc = 3;
    }
    if (env->exception_index == EXCP_PGM) {
        ret = env->int_pgm_code | 0x80000000;
    } else {
        ret |= addr & ~TARGET_PAGE_MASK;
    }
    env->exception_index = old_exc;

R
Richard Henderson 已提交
1073 1074
    env->cc_op = cc;
    return ret;
1075 1076
}
#endif