qemu/target-s390x/mem_helper.c
<<
>>
Prefs
   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 */
  33void 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
  58static 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
  81static 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 */
 116uint32_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 */
 136uint32_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 */
 171uint32_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 */
 191void 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 */
 237uint32_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 */
 264uint32_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
 295static 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
 304static 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
 316static 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) */
 322uint64_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) */
 357uint64_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 */
 394void 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) */
 406uint64_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
 433static 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*/
 471uint32_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 */
 533void 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 */
 548void 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 */
 563uint32_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 */
 604uint32_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 */
 654uint32_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 */
 692uint64_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
 737void 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
 783void 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)
 797void 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
 817void 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
 836void 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
 851void 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
 866uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
 867{
 868    /* XXX implement */
 869
 870    return 0;
 871}
 872
 873/* insert storage key extended */
 874uint64_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 */
 886void 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 */
 898uint32_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 */
 924uint32_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
 947static 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
 986uint32_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
 994uint32_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 */
1003void 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 */
1030void 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 */
1038void 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 */
1046uint64_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
1075