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