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

#include "cpu.h"
22
#include "exec/helper-proto.h"
P
Paolo Bonzini 已提交
23
#include "exec/cpu_ldst.h"
24
#include "hw/s390x/storage-keys.h"
25 26 27 28 29 30 31 32 33

/*****************************************************************************/
/* Softmmu support */
#if !defined(CONFIG_USER_ONLY)

/* try to fill the TLB and return an exception if error. If retaddr is
   NULL, it means that the function was called in C code (i.e. not
   from generated code or from helper.c) */
/* XXX: fix it to restore all registers */
34
void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx,
35 36 37 38
              uintptr_t retaddr)
{
    int ret;

39
    ret = s390_cpu_handle_mmu_fault(cs, addr, is_write, mmu_idx);
40 41 42
    if (unlikely(ret != 0)) {
        if (likely(retaddr)) {
            /* now we have a real cpu fault */
43
            cpu_restore_state(cs, retaddr);
44
        }
45
        cpu_loop_exit(cs);
46 47 48 49 50 51 52 53 54 55 56 57
    }
}

#endif

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

58 59 60 61 62 63 64 65 66 67 68
/* Reduce the length so that addr + len doesn't cross a page boundary.  */
static inline uint64_t adj_len_to_page(uint64_t len, uint64_t addr)
{
#ifndef CONFIG_USER_ONLY
    if ((addr & ~TARGET_PAGE_MASK) + len - 1 >= TARGET_PAGE_SIZE) {
        return -addr & ~TARGET_PAGE_MASK;
    }
#endif
    return len;
}

69 70
static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte,
                        uint32_t l)
71
{
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
    int mmu_idx = cpu_mmu_index(env);

    while (l > 0) {
        void *p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
        if (p) {
            /* Access to the whole page in write mode granted.  */
            int l_adj = adj_len_to_page(l, dest);
            memset(p, byte, l_adj);
            dest += l_adj;
            l -= l_adj;
        } else {
            /* We failed to get access to the whole page. The next write
               access will likely fill the QEMU TLB for the next iteration.  */
            cpu_stb_data(env, dest, byte);
            dest++;
            l--;
        }
89 90 91
    }
}

92 93
static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src,
                         uint32_t l)
94
{
95
    int mmu_idx = cpu_mmu_index(env);
96

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
    while (l > 0) {
        void *src_p = tlb_vaddr_to_host(env, src, MMU_DATA_LOAD, mmu_idx);
        void *dest_p = tlb_vaddr_to_host(env, dest, MMU_DATA_STORE, mmu_idx);
        if (src_p && dest_p) {
            /* Access to both whole pages granted.  */
            int l_adj = adj_len_to_page(l, src);
            l_adj = adj_len_to_page(l_adj, dest);
            memmove(dest_p, src_p, l_adj);
            src += l_adj;
            dest += l_adj;
            l -= l_adj;
        } else {
            /* We failed to get access to one or both whole pages. The next
               read or write access will likely fill the QEMU TLB for the
               next iteration.  */
            cpu_stb_data(env, dest, cpu_ldub_data(env, src));
            src++;
            dest++;
            l--;
        }
117 118 119 120
    }
}

/* and on array */
121 122
uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
                    uint64_t src)
123 124 125 126 127 128 129 130
{
    int i;
    unsigned char x;
    uint32_t cc = 0;

    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
               __func__, l, dest, src);
    for (i = 0; i <= l; i++) {
131
        x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
132 133 134
        if (x) {
            cc = 1;
        }
135
        cpu_stb_data(env, dest + i, x);
136 137 138 139 140
    }
    return cc;
}

/* xor on array */
141 142
uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
                    uint64_t src)
143 144 145 146 147 148 149 150 151 152
{
    int i;
    unsigned char x;
    uint32_t cc = 0;

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

    /* xor with itself is the same as memset(0) */
    if (src == dest) {
153
        fast_memset(env, dest, 0, l + 1);
154 155 156 157
        return 0;
    }

    for (i = 0; i <= l; i++) {
158
        x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
159 160 161
        if (x) {
            cc = 1;
        }
162
        cpu_stb_data(env, dest + i, x);
163 164 165 166 167
    }
    return cc;
}

/* or on array */
168 169
uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
                    uint64_t src)
170 171 172 173 174 175 176 177
{
    int i;
    unsigned char x;
    uint32_t cc = 0;

    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
               __func__, l, dest, src);
    for (i = 0; i <= l; i++) {
178
        x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
179 180 181
        if (x) {
            cc = 1;
        }
182
        cpu_stb_data(env, dest + i, x);
183 184 185 186 187
    }
    return cc;
}

/* memmove */
188
void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
189 190 191 192 193 194
{
    int i = 0;

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

195 196 197 198 199 200
    /* mvc with source pointing to the byte after the destination is the
       same as memset with the first source byte */
    if (dest == (src + 1)) {
        fast_memset(env, dest, cpu_ldub_data(env, src), l + 1);
        return;
    }
201

202
    /* mvc and memmove do not behave the same when areas overlap! */
203
    if ((dest < src) || (src + l < dest)) {
204
        fast_memmove(env, dest, src, l + 1);
205 206 207
        return;
    }

208
    /* slow version with byte accesses which always work */
209
    for (i = 0; i <= l; i++) {
210
        cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
211 212 213 214
    }
}

/* compare unsigned byte arrays */
215
uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
216 217 218 219 220 221 222 223
{
    int i;
    unsigned char x, y;
    uint32_t cc;

    HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
               __func__, l, s1, s2);
    for (i = 0; i <= l; i++) {
224 225
        x = cpu_ldub_data(env, s1 + i);
        y = cpu_ldub_data(env, s2 + i);
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241
        HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
        if (x < y) {
            cc = 1;
            goto done;
        } else if (x > y) {
            cc = 2;
            goto done;
        }
    }
    cc = 0;
 done:
    HELPER_LOG("\n");
    return cc;
}

/* compare logical under mask */
242 243
uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
                     uint64_t addr)
244 245 246 247 248 249 250 251 252
{
    uint8_t r, d;
    uint32_t cc;

    HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
               mask, addr);
    cc = 0;
    while (mask) {
        if (mask & 8) {
253
            d = cpu_ldub_data(env, addr);
254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
            r = (r1 & 0xff000000UL) >> 24;
            HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
                       addr);
            if (r < d) {
                cc = 1;
                break;
            } else if (r > d) {
                cc = 2;
                break;
            }
            addr++;
        }
        mask = (mask << 1) & 0xf;
        r1 <<= 8;
    }
    HELPER_LOG("\n");
    return cc;
}

273 274 275 276 277 278 279 280 281
static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
{
    /* 31-Bit mode */
    if (!(env->psw.mask & PSW_MASK_64)) {
        a &= 0x7fffffff;
    }
    return a;
}

282
static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
283 284 285 286 287 288 289 290
{
    uint64_t r = d2;
    if (x2) {
        r += env->regs[x2];
    }
    if (b2) {
        r += env->regs[b2];
    }
291
    return fix_address(env, r);
292 293
}

294
static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
295
{
296
    return fix_address(env, env->regs[reg]);
297 298 299
}

/* search string (c is byte to search, r2 is string, r1 end of string) */
R
Richard Henderson 已提交
300 301
uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
                      uint64_t str)
302
{
R
Richard Henderson 已提交
303 304
    uint32_t len;
    uint8_t v, c = r0;
305

R
Richard Henderson 已提交
306 307
    str = fix_address(env, str);
    end = fix_address(env, end);
308

R
Richard Henderson 已提交
309 310 311 312
    /* Assume for now that R2 is unmodified.  */
    env->retxl = str;

    /* Lest we fail to service interrupts in a timely manner, limit the
313
       amount of work we're willing to do.  For now, let's cap at 8k.  */
R
Richard Henderson 已提交
314 315 316 317 318 319 320 321 322 323 324
    for (len = 0; len < 0x2000; ++len) {
        if (str + len == end) {
            /* Character not found.  R1 & R2 are unmodified.  */
            env->cc_op = 2;
            return end;
        }
        v = cpu_ldub_data(env, str + len);
        if (v == c) {
            /* Character found.  Set R1 to the location; R2 is unmodified.  */
            env->cc_op = 1;
            return str + len;
325 326 327
        }
    }

R
Richard Henderson 已提交
328 329 330 331
    /* CPU-determined bytes processed.  Advance R2 to next byte to process.  */
    env->retxl = str + len;
    env->cc_op = 3;
    return end;
332 333 334
}

/* unsigned string compare (c is string terminator) */
335
uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
336
{
337
    uint32_t len;
338 339

    c = c & 0xff;
340 341 342 343
    s1 = fix_address(env, s1);
    s2 = fix_address(env, s2);

    /* Lest we fail to service interrupts in a timely manner, limit the
344
       amount of work we're willing to do.  For now, let's cap at 8k.  */
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
    for (len = 0; len < 0x2000; ++len) {
        uint8_t v1 = cpu_ldub_data(env, s1 + len);
        uint8_t v2 = cpu_ldub_data(env, s2 + len);
        if (v1 == v2) {
            if (v1 == c) {
                /* Equal.  CC=0, and don't advance the registers.  */
                env->cc_op = 0;
                env->retxl = s2;
                return s1;
            }
        } else {
            /* Unequal.  CC={1,2}, and advance the registers.  Note that
               the terminator need not be zero, but the string that contains
               the terminator is by definition "low".  */
            env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
            env->retxl = s2 + len;
            return s1 + len;
362 363 364
        }
    }

365 366 367 368
    /* CPU-determined bytes equal; advance the registers.  */
    env->cc_op = 3;
    env->retxl = s2 + len;
    return s1 + len;
369 370 371
}

/* move page */
372
void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
373 374
{
    /* XXX missing r0 handling */
R
Richard Henderson 已提交
375
    env->cc_op = 0;
376
    fast_memmove(env, r1, r2, TARGET_PAGE_SIZE);
377 378 379
}

/* string copy (c is string terminator) */
380
uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
381
{
382
    uint32_t len;
383 384

    c = c & 0xff;
385 386 387 388
    d = fix_address(env, d);
    s = fix_address(env, s);

    /* Lest we fail to service interrupts in a timely manner, limit the
389
       amount of work we're willing to do.  For now, let's cap at 8k.  */
390 391 392
    for (len = 0; len < 0x2000; ++len) {
        uint8_t v = cpu_ldub_data(env, s + len);
        cpu_stb_data(env, d + len, v);
393
        if (v == c) {
394 395 396 397
            /* Complete.  Set CC=1 and advance R1.  */
            env->cc_op = 1;
            env->retxl = s;
            return d + len;
398 399
        }
    }
400 401 402 403 404

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

407 408
static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
                           uint32_t mask)
409 410 411 412 413 414 415 416 417 418
{
    int pos = 24; /* top of the lower half of r1 */
    uint64_t rmask = 0xff000000ULL;
    uint8_t val = 0;
    int ccd = 0;
    uint32_t cc = 0;

    while (mask) {
        if (mask & 8) {
            env->regs[r1] &= ~rmask;
419
            val = cpu_ldub_data(env, address);
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
            if ((val & 0x80) && !ccd) {
                cc = 1;
            }
            ccd = 1;
            if (val && cc == 0) {
                cc = 2;
            }
            env->regs[r1] |= (uint64_t)val << pos;
            address++;
        }
        mask = (mask << 1) & 0xf;
        pos -= 8;
        rmask >>= 8;
    }

    return cc;
}

/* execute instruction
   this instruction executes an insn modified with the contents of r1
   it does not change the executed instruction in memory
   it does not change the program counter
   in other words: tricky...
   currently implemented by interpreting the cases it is most commonly used in
*/
445 446
uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
                    uint64_t addr, uint64_t ret)
447
{
448
    S390CPU *cpu = s390_env_get_cpu(env);
449
    uint16_t insn = cpu_lduw_code(env, addr);
450 451 452 453 454 455 456

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

        l = v1 & 0xff;
457
        insn2 = cpu_ldl_code(env, addr + 2);
458 459 460 461 462 463
        b1 = (insn2 >> 28) & 0xf;
        b2 = (insn2 >> 12) & 0xf;
        d1 = (insn2 >> 16) & 0xfff;
        d2 = insn2 & 0xfff;
        switch (insn & 0xf00) {
        case 0x200:
464 465
            helper_mvc(env, l, get_address(env, 0, b1, d1),
                       get_address(env, 0, b2, d2));
466
            break;
467 468 469 470
        case 0x400:
            cc = helper_nc(env, l, get_address(env, 0, b1, d1),
                            get_address(env, 0, b2, d2));
            break;
471
        case 0x500:
472 473
            cc = helper_clc(env, l, get_address(env, 0, b1, d1),
                            get_address(env, 0, b2, d2));
474
            break;
475 476 477 478
        case 0x600:
            cc = helper_oc(env, l, get_address(env, 0, b1, d1),
                            get_address(env, 0, b2, d2));
            break;
479
        case 0x700:
480 481
            cc = helper_xc(env, l, get_address(env, 0, b1, d1),
                           get_address(env, 0, b2, d2));
482 483
            break;
        case 0xc00:
484 485
            helper_tr(env, l, get_address(env, 0, b1, d1),
                      get_address(env, 0, b2, d2));
486
            break;
487 488 489
        case 0xd00:
            cc = helper_trt(env, l, get_address(env, 0, b1, d1),
                            get_address(env, 0, b2, d2));
490 491 492 493 494 495 496 497 498
            break;
        default:
            goto abort;
        }
    } else if ((insn & 0xff00) == 0x0a00) {
        /* supervisor call */
        HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
        env->psw.addr = ret - 4;
        env->int_svc_code = (insn | v1) & 0xff;
499
        env->int_svc_ilen = 4;
500
        helper_exception(env, EXCP_SVC);
501 502 503
    } else if ((insn & 0xff00) == 0xbf00) {
        uint32_t insn2, r1, r3, b2, d2;

504
        insn2 = cpu_ldl_code(env, addr + 2);
505 506 507 508
        r1 = (insn2 >> 20) & 0xf;
        r3 = (insn2 >> 16) & 0xf;
        b2 = (insn2 >> 12) & 0xf;
        d2 = insn2 & 0xfff;
509
        cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
510 511
    } else {
    abort:
512
        cpu_abort(CPU(cpu), "EXECUTE on instruction prefix 0x%x not implemented\n",
513 514 515 516 517 518
                  insn);
    }
    return cc;
}

/* load access registers r1 to r3 from memory at a2 */
519
void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
520 521 522 523
{
    int i;

    for (i = r1;; i = (i + 1) % 16) {
524
        env->aregs[i] = cpu_ldl_data(env, a2);
525 526 527 528 529 530 531 532 533
        a2 += 4;

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

/* store access registers r1 to r3 in memory at a2 */
534
void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
535 536 537 538
{
    int i;

    for (i = r1;; i = (i + 1) % 16) {
539
        cpu_stl_data(env, a2, env->aregs[i]);
540 541 542 543 544 545 546 547 548
        a2 += 4;

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

/* move long */
549
uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
550 551
{
    uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
552
    uint64_t dest = get_address_31fix(env, r1);
553
    uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
554
    uint64_t src = get_address_31fix(env, r2);
555
    uint8_t pad = env->regs[r2 + 1] >> 24;
556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
    uint8_t v;
    uint32_t cc;

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

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

    for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
572 573
        v = cpu_ldub_data(env, src);
        cpu_stb_data(env, dest, v);
574 575 576
    }

    for (; destlen; dest++, destlen--) {
577
        cpu_stb_data(env, dest, pad);
578 579 580 581 582 583 584 585 586 587 588 589
    }

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

    return cc;
}

/* move long extended another memcopy insn with more bells and whistles */
590 591
uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
                       uint32_t r3)
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
{
    uint64_t destlen = env->regs[r1 + 1];
    uint64_t dest = env->regs[r1];
    uint64_t srclen = env->regs[r3 + 1];
    uint64_t src = env->regs[r3];
    uint8_t pad = a2 & 0xff;
    uint8_t v;
    uint32_t cc;

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

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

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

    for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
621 622
        v = cpu_ldub_data(env, src);
        cpu_stb_data(env, dest, v);
623 624 625
    }

    for (; destlen; dest++, destlen--) {
626
        cpu_stb_data(env, dest, pad);
627 628 629 630 631 632 633 634 635 636 637 638 639
    }

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

    return cc;
}

/* compare logical long extended memcompare insn with padding */
640 641
uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
                       uint32_t r3)
642 643
{
    uint64_t destlen = env->regs[r1 + 1];
644
    uint64_t dest = get_address_31fix(env, r1);
645
    uint64_t srclen = env->regs[r3 + 1];
646
    uint64_t src = get_address_31fix(env, r3);
647 648 649 650 651 652 653 654 655 656 657 658 659
    uint8_t pad = a2 & 0xff;
    uint8_t v1 = 0, v2 = 0;
    uint32_t cc = 0;

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

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

    for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
660 661
        v1 = srclen ? cpu_ldub_data(env, src) : pad;
        v2 = destlen ? cpu_ldub_data(env, dest) : pad;
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
        if (v1 != v2) {
            cc = (v1 < v2) ? 1 : 2;
            break;
        }
    }

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

    return cc;
}

/* checksum */
R
Richard Henderson 已提交
678 679
uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
                      uint64_t src, uint64_t src_len)
680
{
R
Richard Henderson 已提交
681 682
    uint64_t max_len, len;
    uint64_t cksm = (uint32_t)r1;
683

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

R
Richard Henderson 已提交
688 689 690
    /* Process full words as available.  */
    for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
        cksm += (uint32_t)cpu_ldl_data(env, src);
691 692
    }

R
Richard Henderson 已提交
693
    switch (max_len - len) {
694
    case 1:
695
        cksm += cpu_ldub_data(env, src) << 24;
R
Richard Henderson 已提交
696
        len += 1;
697 698
        break;
    case 2:
699
        cksm += cpu_lduw_data(env, src) << 16;
R
Richard Henderson 已提交
700
        len += 2;
701 702
        break;
    case 3:
703 704
        cksm += cpu_lduw_data(env, src) << 16;
        cksm += cpu_ldub_data(env, src + 2) << 8;
R
Richard Henderson 已提交
705
        len += 3;
706 707 708
        break;
    }

R
Richard Henderson 已提交
709 710 711 712 713 714 715 716
    /* Fold the carry from the checksum.  Note that we can see carry-out
       during folding more than once (but probably not more than twice).  */
    while (cksm > 0xffffffffull) {
        cksm = (uint32_t)cksm + (cksm >> 32);
    }

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

R
Richard Henderson 已提交
718 719 720
    /* Return both cksm and processed length.  */
    env->retxl = cksm;
    return len;
721 722
}

723 724
void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
                  uint64_t src)
725 726 727 728 729 730 731 732 733 734
{
    int len_dest = len >> 4;
    int len_src = len & 0xf;
    uint8_t b;
    int second_nibble = 0;

    dest += len_dest;
    src += len_src;

    /* last byte is special, it only flips the nibbles */
735 736
    b = cpu_ldub_data(env, src);
    cpu_stb_data(env, dest, (b << 4) | (b >> 4));
737 738 739 740 741 742 743 744 745
    src--;
    len_src--;

    /* now pad every nibble with 0xf0 */

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

        if (len_src > 0) {
746
            cur_byte = cpu_ldub_data(env, src);
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
        }

        len_dest--;
        dest--;

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

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

765
        cpu_stb_data(env, dest, cur_byte);
766 767 768
    }
}

769 770
void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
                uint64_t trans)
771 772 773 774
{
    int i;

    for (i = 0; i <= len; i++) {
775 776
        uint8_t byte = cpu_ldub_data(env, array + i);
        uint8_t new_byte = cpu_ldub_data(env, trans + byte);
777

778
        cpu_stb_data(env, array + i, new_byte);
779 780 781
    }
}

782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820
uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
                     uint64_t len, uint64_t trans)
{
    uint8_t end = env->regs[0] & 0xff;
    uint64_t l = len;
    uint64_t i;

    if (!(env->psw.mask & PSW_MASK_64)) {
        array &= 0x7fffffff;
        l = (uint32_t)l;
    }

    /* Lest we fail to service interrupts in a timely manner, limit the
       amount of work we're willing to do.  For now, let's cap at 8k.  */
    if (l > 0x2000) {
        l = 0x2000;
        env->cc_op = 3;
    } else {
        env->cc_op = 0;
    }

    for (i = 0; i < l; i++) {
        uint8_t byte, new_byte;

        byte = cpu_ldub_data(env, array + i);

        if (byte == end) {
            env->cc_op = 1;
            break;
        }

        new_byte = cpu_ldub_data(env, trans + byte);
        cpu_stb_data(env, array + i, new_byte);
    }

    env->retxl = len - i;
    return array + i;
}

821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841
uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
                     uint64_t trans)
{
    uint32_t cc = 0;
    int i;

    for (i = 0; i <= len; i++) {
        uint8_t byte = cpu_ldub_data(env, array + i);
        uint8_t sbyte = cpu_ldub_data(env, trans + byte);

        if (sbyte != 0) {
            env->regs[1] = array + i;
            env->regs[2] = (env->regs[2] & ~0xff) | sbyte;
            cc = (i == len) ? 2 : 1;
            break;
        }
    }

    return cc;
}

842
#if !defined(CONFIG_USER_ONLY)
843
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
844
{
845
    S390CPU *cpu = s390_env_get_cpu(env);
846
    bool PERchanged = false;
847 848
    int i;
    uint64_t src = a2;
849
    uint64_t val;
850 851

    for (i = r1;; i = (i + 1) % 16) {
852 853 854 855 856
        val = cpu_ldq_data(env, src);
        if (env->cregs[i] != val && i >= 9 && i <= 11) {
            PERchanged = true;
        }
        env->cregs[i] = val;
857 858 859 860 861 862 863 864 865
        HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
                   i, src, env->cregs[i]);
        src += sizeof(uint64_t);

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

866 867 868 869
    if (PERchanged && env->psw.mask & PSW_MASK_PER) {
        s390_cpu_recompute_watchpoints(CPU(cpu));
    }

870
    tlb_flush(CPU(cpu), 1);
871 872
}

873
void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
874
{
875
    S390CPU *cpu = s390_env_get_cpu(env);
876
    bool PERchanged = false;
877 878
    int i;
    uint64_t src = a2;
879
    uint32_t val;
880 881

    for (i = r1;; i = (i + 1) % 16) {
882 883 884 885 886
        val = cpu_ldl_data(env, src);
        if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) {
            PERchanged = true;
        }
        env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) | val;
887 888 889 890 891 892 893
        src += sizeof(uint32_t);

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

894 895 896 897
    if (PERchanged && env->psw.mask & PSW_MASK_PER) {
        s390_cpu_recompute_watchpoints(CPU(cpu));
    }

898
    tlb_flush(CPU(cpu), 1);
899 900
}

901
void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
902 903 904 905 906
{
    int i;
    uint64_t dest = a2;

    for (i = r1;; i = (i + 1) % 16) {
907
        cpu_stq_data(env, dest, env->cregs[i]);
908 909 910 911 912 913 914 915
        dest += sizeof(uint64_t);

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

916
void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
917 918 919 920 921
{
    int i;
    uint64_t dest = a2;

    for (i = r1;; i = (i + 1) % 16) {
922
        cpu_stl_data(env, dest, env->cregs[i]);
923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938
        dest += sizeof(uint32_t);

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

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

    return 0;
}

/* insert storage key extended */
939
uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
940
{
941 942
    static S390SKeysState *ss;
    static S390SKeysClass *skeyclass;
943
    uint64_t addr = get_address(env, 0, 0, r2);
944
    uint8_t key;
945 946 947 948 949

    if (addr > ram_size) {
        return 0;
    }

950 951 952 953 954 955 956 957 958
    if (unlikely(!ss)) {
        ss = s390_get_skeys_device();
        skeyclass = S390_SKEYS_GET_CLASS(ss);
    }

    if (skeyclass->get_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key)) {
        return 0;
    }
    return key;
959 960 961
}

/* set storage key extended */
R
Richard Henderson 已提交
962
void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
963
{
964 965
    static S390SKeysState *ss;
    static S390SKeysClass *skeyclass;
966
    uint64_t addr = get_address(env, 0, 0, r2);
967
    uint8_t key;
968 969 970 971 972

    if (addr > ram_size) {
        return;
    }

973 974 975 976 977 978 979
    if (unlikely(!ss)) {
        ss = s390_get_skeys_device();
        skeyclass = S390_SKEYS_GET_CLASS(ss);
    }

    key = (uint8_t) r1;
    skeyclass->set_skeys(ss, addr / TARGET_PAGE_SIZE, 1, &key);
980 981 982
}

/* reset reference bit extended */
R
Richard Henderson 已提交
983
uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
984
{
985 986 987
    static S390SKeysState *ss;
    static S390SKeysClass *skeyclass;
    uint8_t re, key;
988 989 990 991 992

    if (r2 > ram_size) {
        return 0;
    }

993 994 995 996 997 998 999 1000 1001
    if (unlikely(!ss)) {
        ss = s390_get_skeys_device();
        skeyclass = S390_SKEYS_GET_CLASS(ss);
    }

    if (skeyclass->get_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) {
        return 0;
    }

1002
    re = key & (SK_R | SK_C);
1003 1004 1005 1006 1007
    key &= ~SK_R;

    if (skeyclass->set_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) {
        return 0;
    }
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021

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

    return re >> 1;
}

/* compare and swap and purge */
R
Richard Henderson 已提交
1022
uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
1023
{
1024
    S390CPU *cpu = s390_env_get_cpu(env);
1025 1026
    uint32_t cc;
    uint32_t o1 = env->regs[r1];
R
Richard Henderson 已提交
1027
    uint64_t a2 = r2 & ~3ULL;
1028
    uint32_t o2 = cpu_ldl_data(env, a2);
1029 1030

    if (o1 == o2) {
1031
        cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
R
Richard Henderson 已提交
1032
        if (r2 & 0x3) {
1033
            /* flush TLB / ALB */
1034
            tlb_flush(CPU(cpu), 1);
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044
        }
        cc = 0;
    } else {
        env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
        cc = 1;
    }

    return cc;
}

1045
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1046
{
1047
    int cc = 0, i;
1048

1049 1050 1051 1052
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
               __func__, l, a1, a2);

    if (l > 256) {
1053 1054 1055 1056 1057 1058 1059
        /* max 256 */
        l = 256;
        cc = 3;
    }

    /* XXX replace w/ memcpy */
    for (i = 0; i < l; i++) {
1060
        cpu_stb_secondary(env, a1 + i, cpu_ldub_primary(env, a2 + i));
1061 1062 1063 1064 1065
    }

    return cc;
}

1066
uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1067
{
1068 1069
    int cc = 0, i;

1070 1071 1072
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
               __func__, l, a1, a2);

1073 1074 1075 1076 1077
    if (l > 256) {
        /* max 256 */
        l = 256;
        cc = 3;
    }
1078

1079 1080 1081 1082
    /* XXX replace w/ memcpy */
    for (i = 0; i < l; i++) {
        cpu_stb_primary(env, a1 + i, cpu_ldub_secondary(env, a2 + i));
    }
1083

1084
    return cc;
1085 1086 1087
}

/* invalidate pte */
1088
void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1089
{
1090
    CPUState *cs = CPU(s390_env_get_cpu(env));
1091 1092 1093 1094 1095 1096 1097 1098 1099
    uint64_t page = vaddr & TARGET_PAGE_MASK;
    uint64_t pte = 0;

    /* XXX broadcast to other CPUs */

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

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

    /* XXX 31-bit hack */
    if (page & 0x80000000) {
1108
        tlb_flush_page(cs, page & ~0x80000000);
1109
    } else {
1110
        tlb_flush_page(cs, page | 0x80000000);
1111 1112 1113 1114
    }
}

/* flush local tlb */
1115
void HELPER(ptlb)(CPUS390XState *env)
1116
{
1117 1118 1119
    S390CPU *cpu = s390_env_get_cpu(env);

    tlb_flush(CPU(cpu), 1);
1120 1121
}

1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
/* load using real address */
uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr)
{
    CPUState *cs = CPU(s390_env_get_cpu(env));

    return (uint32_t)ldl_phys(cs->as, get_address(env, 0, 0, addr));
}

uint64_t HELPER(lurag)(CPUS390XState *env, uint64_t addr)
{
    CPUState *cs = CPU(s390_env_get_cpu(env));

    return ldq_phys(cs->as, get_address(env, 0, 0, addr));
}

1137
/* store using real address */
R
Richard Henderson 已提交
1138
void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1139
{
1140 1141
    CPUState *cs = CPU(s390_env_get_cpu(env));

R
Richard Henderson 已提交
1142
    stl_phys(cs->as, get_address(env, 0, 0, addr), (uint32_t)v1);
1143 1144 1145 1146 1147 1148 1149 1150

    if ((env->psw.mask & PSW_MASK_PER) &&
        (env->cregs[9] & PER_CR9_EVENT_STORE) &&
        (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) {
        /* PSW is saved just before calling the helper.  */
        env->per_address = env->psw.addr;
        env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env);
    }
1151 1152
}

1153 1154 1155 1156 1157
void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1)
{
    CPUState *cs = CPU(s390_env_get_cpu(env));

    stq_phys(cs->as, get_address(env, 0, 0, addr), v1);
1158 1159 1160 1161 1162 1163 1164 1165

    if ((env->psw.mask & PSW_MASK_PER) &&
        (env->cregs[9] & PER_CR9_EVENT_STORE) &&
        (env->cregs[9] & PER_CR9_EVENT_STORE_REAL)) {
        /* PSW is saved just before calling the helper.  */
        env->per_address = env->psw.addr;
        env->per_perc_atmid = PER_CODE_EVENT_STORE_REAL | get_per_atmid(env);
    }
1166 1167
}

1168
/* load real address */
R
Richard Henderson 已提交
1169
uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1170
{
1171
    CPUState *cs = CPU(s390_env_get_cpu(env));
1172
    uint32_t cc = 0;
1173
    int old_exc = cs->exception_index;
1174 1175 1176 1177 1178 1179 1180 1181 1182
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
    uint64_t ret;
    int flags;

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

1183
    cs->exception_index = old_exc;
1184
    if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) {
1185 1186
        cc = 3;
    }
1187
    if (cs->exception_index == EXCP_PGM) {
1188 1189 1190 1191
        ret = env->int_pgm_code | 0x80000000;
    } else {
        ret |= addr & ~TARGET_PAGE_MASK;
    }
1192
    cs->exception_index = old_exc;
1193

R
Richard Henderson 已提交
1194 1195
    env->cc_op = cc;
    return ret;
1196 1197
}
#endif