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