mem_helper.c 44.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/*
 *  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/>.
 */

P
Peter Maydell 已提交
21
#include "qemu/osdep.h"
22
#include "cpu.h"
23
#include "exec/address-spaces.h"
24
#include "exec/helper-proto.h"
25
#include "exec/exec-all.h"
P
Paolo Bonzini 已提交
26
#include "exec/cpu_ldst.h"
27
#include "qemu/int128.h"
28 29

#if !defined(CONFIG_USER_ONLY)
30
#include "hw/s390x/storage-keys.h"
31
#endif
32 33 34 35 36 37 38 39 40

/*****************************************************************************/
/* 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 */
41 42
void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
              int mmu_idx, uintptr_t retaddr)
43
{
44
    int ret = s390_cpu_handle_mmu_fault(cs, addr, access_type, mmu_idx);
45
    if (unlikely(ret != 0)) {
46
        cpu_loop_exit_restore(cs, retaddr);
47 48 49 50 51 52 53 54 55 56 57 58
    }
}

#endif

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

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

70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
/* Trigger a SPECIFICATION exception if an address or a length is not
   naturally aligned.  */
static inline void check_alignment(CPUS390XState *env, uint64_t v,
                                   int wordsize, uintptr_t ra)
{
    if (v % wordsize) {
        CPUState *cs = CPU(s390_env_get_cpu(env));
        cpu_restore_state(cs, ra);
        program_interrupt(env, PGM_SPECIFICATION, 6);
    }
}

/* Load a value from memory according to its size.  */
static inline uint64_t cpu_ldusize_data_ra(CPUS390XState *env, uint64_t addr,
                                           int wordsize, uintptr_t ra)
{
    switch (wordsize) {
    case 1:
        return cpu_ldub_data_ra(env, addr, ra);
    case 2:
        return cpu_lduw_data_ra(env, addr, ra);
    default:
        abort();
    }
}

96
static void fast_memset(CPUS390XState *env, uint64_t dest, uint8_t byte,
97
                        uint32_t l, uintptr_t ra)
98
{
99
    int mmu_idx = cpu_mmu_index(env, false);
100 101 102 103 104

    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.  */
105
            uint32_t l_adj = adj_len_to_page(l, dest);
106 107 108 109 110 111
            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.  */
112
            cpu_stb_data_ra(env, dest, byte, ra);
113 114 115
            dest++;
            l--;
        }
116 117 118
    }
}

119
static void fast_memmove(CPUS390XState *env, uint64_t dest, uint64_t src,
120
                         uint32_t l, uintptr_t ra)
121
{
122
    int mmu_idx = cpu_mmu_index(env, false);
123

124 125 126 127 128
    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.  */
129
            uint32_t l_adj = adj_len_to_page(l, src);
130 131 132 133 134 135 136 137 138
            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.  */
139
            cpu_stb_data_ra(env, dest, cpu_ldub_data_ra(env, src, ra), ra);
140 141 142 143
            src++;
            dest++;
            l--;
        }
144 145 146 147
    }
}

/* and on array */
148 149
static uint32_t do_helper_nc(CPUS390XState *env, uint32_t l, uint64_t dest,
                             uint64_t src, uintptr_t ra)
150
{
151 152
    uint32_t i;
    uint8_t c = 0;
153 154 155

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

157
    for (i = 0; i <= l; i++) {
158 159 160 161
        uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
        x &= cpu_ldub_data_ra(env, dest + i, ra);
        c |= x;
        cpu_stb_data_ra(env, dest + i, x, ra);
162
    }
163 164 165 166 167 168 169
    return c != 0;
}

uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
                    uint64_t src)
{
    return do_helper_nc(env, l, dest, src, GETPC());
170 171 172
}

/* xor on array */
173 174
static uint32_t do_helper_xc(CPUS390XState *env, uint32_t l, uint64_t dest,
                             uint64_t src, uintptr_t ra)
175
{
176 177
    uint32_t i;
    uint8_t c = 0;
178 179 180 181 182 183

    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) {
184
        fast_memset(env, dest, 0, l + 1, ra);
185 186 187 188
        return 0;
    }

    for (i = 0; i <= l; i++) {
189 190 191 192
        uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
        x ^= cpu_ldub_data_ra(env, dest + i, ra);
        c |= x;
        cpu_stb_data_ra(env, dest + i, x, ra);
193
    }
194 195 196 197 198 199 200
    return c != 0;
}

uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
                    uint64_t src)
{
    return do_helper_xc(env, l, dest, src, GETPC());
201 202 203
}

/* or on array */
204 205
static uint32_t do_helper_oc(CPUS390XState *env, uint32_t l, uint64_t dest,
                             uint64_t src, uintptr_t ra)
206
{
207 208
    uint32_t i;
    uint8_t c = 0;
209 210 211

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

213
    for (i = 0; i <= l; i++) {
214 215 216 217
        uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
        x |= cpu_ldub_data_ra(env, dest + i, ra);
        c |= x;
        cpu_stb_data_ra(env, dest + i, x, ra);
218
    }
219 220 221 222 223 224 225
    return c != 0;
}

uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
                    uint64_t src)
{
    return do_helper_oc(env, l, dest, src, GETPC());
226 227 228
}

/* memmove */
229 230
static uint32_t do_helper_mvc(CPUS390XState *env, uint32_t l, uint64_t dest,
                              uint64_t src, uintptr_t ra)
231
{
232
    uint32_t i;
233 234 235 236

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

237
    /* mvc and memmove do not behave the same when areas overlap! */
238 239
    /* mvc with source pointing to the byte after the destination is the
       same as memset with the first source byte */
240 241
    if (dest == src + 1) {
        fast_memset(env, dest, cpu_ldub_data_ra(env, src, ra), l + 1, ra);
242
    } else if (dest < src || src + l < dest) {
243
        fast_memmove(env, dest, src, l + 1, ra);
244 245 246 247 248 249
    } else {
        /* slow version with byte accesses which always work */
        for (i = 0; i <= l; i++) {
            uint8_t x = cpu_ldub_data_ra(env, src + i, ra);
            cpu_stb_data_ra(env, dest + i, x, ra);
        }
250 251
    }

252
    return env->cc_op;
253 254
}

255 256 257 258 259
void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
    do_helper_mvc(env, l, dest, src, GETPC());
}

260 261 262 263 264 265 266 267 268 269 270 271
/* move inverse  */
void HELPER(mvcin)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
    uintptr_t ra = GETPC();
    int i;

    for (i = 0; i <= l; i++) {
        uint8_t v = cpu_ldub_data_ra(env, src - i, ra);
        cpu_stb_data_ra(env, dest + i, v, ra);
    }
}

272 273 274 275 276 277 278 279 280 281 282 283 284
/* move numerics  */
void HELPER(mvn)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
    uintptr_t ra = GETPC();
    int i;

    for (i = 0; i <= l; i++) {
        uint8_t v = cpu_ldub_data_ra(env, dest + i, ra) & 0xf0;
        v |= cpu_ldub_data_ra(env, src + i, ra) & 0x0f;
        cpu_stb_data_ra(env, dest + i, v, ra);
    }
}

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
/* move with offset  */
void HELPER(mvo)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
    uintptr_t ra = GETPC();
    int len_dest = l >> 4;
    int len_src = l & 0xf;
    uint8_t byte_dest, byte_src;
    int i;

    src += len_src;
    dest += len_dest;

    /* Handle rightmost byte */
    byte_src = cpu_ldub_data_ra(env, src, ra);
    byte_dest = cpu_ldub_data_ra(env, dest, ra);
    byte_dest = (byte_dest & 0x0f) | (byte_src << 4);
    cpu_stb_data_ra(env, dest, byte_dest, ra);

    /* Process remaining bytes from right to left */
    for (i = 1; i <= len_dest; i++) {
        byte_dest = byte_src >> 4;
        if (len_src - i >= 0) {
            byte_src = cpu_ldub_data_ra(env, src - i, ra);
        } else {
            byte_src = 0;
        }
        byte_dest |= byte_src << 4;
        cpu_stb_data_ra(env, dest - i, byte_dest, ra);
    }
}

316 317 318 319 320 321 322 323 324 325 326 327 328
/* move zones  */
void HELPER(mvz)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
{
    uintptr_t ra = GETPC();
    int i;

    for (i = 0; i <= l; i++) {
        uint8_t b = cpu_ldub_data_ra(env, dest + i, ra) & 0x0f;
        b |= cpu_ldub_data_ra(env, src + i, ra) & 0xf0;
        cpu_stb_data_ra(env, dest + i, b, ra);
    }
}

329
/* compare unsigned byte arrays */
330 331
static uint32_t do_helper_clc(CPUS390XState *env, uint32_t l, uint64_t s1,
                              uint64_t s2, uintptr_t ra)
332
{
333 334
    uint32_t i;
    uint32_t cc = 0;
335 336 337

    HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
               __func__, l, s1, s2);
338

339
    for (i = 0; i <= l; i++) {
340 341
        uint8_t x = cpu_ldub_data_ra(env, s1 + i, ra);
        uint8_t y = cpu_ldub_data_ra(env, s2 + i, ra);
342 343 344
        HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
        if (x < y) {
            cc = 1;
345
            break;
346 347
        } else if (x > y) {
            cc = 2;
348
            break;
349 350
        }
    }
351

352 353 354 355
    HELPER_LOG("\n");
    return cc;
}

356 357 358 359 360
uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
{
    return do_helper_clc(env, l, s1, s2, GETPC());
}

361
/* compare logical under mask */
362 363
uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
                     uint64_t addr)
364
{
365 366
    uintptr_t ra = GETPC();
    uint32_t cc = 0;
367 368 369

    HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
               mask, addr);
370

371 372
    while (mask) {
        if (mask & 8) {
373 374
            uint8_t d = cpu_ldub_data_ra(env, addr, ra);
            uint8_t r = extract32(r1, 24, 8);
375 376 377 378 379 380 381 382 383 384 385 386 387 388
            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;
    }
389

390 391 392 393
    HELPER_LOG("\n");
    return cc;
}

394
static inline uint64_t wrap_address(CPUS390XState *env, uint64_t a)
395 396
{
    if (!(env->psw.mask & PSW_MASK_64)) {
397 398 399 400 401 402 403
        if (!(env->psw.mask & PSW_MASK_32)) {
            /* 24-Bit mode */
            a &= 0x00ffffff;
        } else {
            /* 31-Bit mode */
            a &= 0x7fffffff;
        }
404 405 406 407
    }
    return a;
}

408
static inline uint64_t get_address(CPUS390XState *env, int reg)
409
{
410
    return wrap_address(env, env->regs[reg]);
411 412
}

413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
static inline void set_address(CPUS390XState *env, int reg, uint64_t address)
{
    if (env->psw.mask & PSW_MASK_64) {
        /* 64-Bit mode */
        env->regs[reg] = address;
    } else {
        if (!(env->psw.mask & PSW_MASK_32)) {
            /* 24-Bit mode. According to the PoO it is implementation
            dependent if bits 32-39 remain unchanged or are set to
            zeros.  Choose the former so that the function can also be
            used for TRT.  */
            env->regs[reg] = deposit64(env->regs[reg], 0, 24, address);
        } else {
            /* 31-Bit mode. According to the PoO it is implementation
            dependent if bit 32 remains unchanged or is set to zero.
            Choose the latter so that the function can also be used for
            TRT.  */
            address &= 0x7fffffff;
            env->regs[reg] = deposit64(env->regs[reg], 0, 32, address);
        }
    }
}

436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
static inline uint64_t wrap_length(CPUS390XState *env, uint64_t length)
{
    if (!(env->psw.mask & PSW_MASK_64)) {
        /* 24-Bit and 31-Bit mode */
        length &= 0x7fffffff;
    }
    return length;
}

static inline uint64_t get_length(CPUS390XState *env, int reg)
{
    return wrap_length(env, env->regs[reg]);
}

static inline void set_length(CPUS390XState *env, int reg, uint64_t length)
{
    if (env->psw.mask & PSW_MASK_64) {
        /* 64-Bit mode */
        env->regs[reg] = length;
    } else {
        /* 24-Bit and 31-Bit mode */
        env->regs[reg] = deposit64(env->regs[reg], 0, 32, length);
    }
}

461
/* search string (c is byte to search, r2 is string, r1 end of string) */
R
Richard Henderson 已提交
462 463
uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
                      uint64_t str)
464
{
465
    uintptr_t ra = GETPC();
R
Richard Henderson 已提交
466 467
    uint32_t len;
    uint8_t v, c = r0;
468

469 470
    str = wrap_address(env, str);
    end = wrap_address(env, end);
471

R
Richard Henderson 已提交
472 473 474 475
    /* Assume for now that R2 is unmodified.  */
    env->retxl = str;

    /* Lest we fail to service interrupts in a timely manner, limit the
476
       amount of work we're willing to do.  For now, let's cap at 8k.  */
R
Richard Henderson 已提交
477 478 479 480 481 482
    for (len = 0; len < 0x2000; ++len) {
        if (str + len == end) {
            /* Character not found.  R1 & R2 are unmodified.  */
            env->cc_op = 2;
            return end;
        }
483
        v = cpu_ldub_data_ra(env, str + len, ra);
R
Richard Henderson 已提交
484 485 486 487
        if (v == c) {
            /* Character found.  Set R1 to the location; R2 is unmodified.  */
            env->cc_op = 1;
            return str + len;
488 489 490
        }
    }

R
Richard Henderson 已提交
491 492 493 494
    /* CPU-determined bytes processed.  Advance R2 to next byte to process.  */
    env->retxl = str + len;
    env->cc_op = 3;
    return end;
495 496 497
}

/* unsigned string compare (c is string terminator) */
498
uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
499
{
500
    uintptr_t ra = GETPC();
501
    uint32_t len;
502 503

    c = c & 0xff;
504 505
    s1 = wrap_address(env, s1);
    s2 = wrap_address(env, s2);
506 507

    /* Lest we fail to service interrupts in a timely manner, limit the
508
       amount of work we're willing to do.  For now, let's cap at 8k.  */
509
    for (len = 0; len < 0x2000; ++len) {
510 511
        uint8_t v1 = cpu_ldub_data_ra(env, s1 + len, ra);
        uint8_t v2 = cpu_ldub_data_ra(env, s2 + len, ra);
512 513 514 515 516 517 518 519 520 521 522 523 524 525
        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;
526 527 528
        }
    }

529 530 531 532
    /* CPU-determined bytes equal; advance the registers.  */
    env->cc_op = 3;
    env->retxl = s2 + len;
    return s1 + len;
533 534 535
}

/* move page */
536
uint32_t HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
537
{
538 539 540 541
    /* ??? missing r0 handling, which includes access keys, but more
       importantly optional suppression of the exception!  */
    fast_memmove(env, r1, r2, TARGET_PAGE_SIZE, GETPC());
    return 0; /* data moved */
542 543 544
}

/* string copy (c is string terminator) */
545
uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
546
{
547
    uintptr_t ra = GETPC();
548
    uint32_t len;
549 550

    c = c & 0xff;
551 552
    d = wrap_address(env, d);
    s = wrap_address(env, s);
553 554

    /* Lest we fail to service interrupts in a timely manner, limit the
555
       amount of work we're willing to do.  For now, let's cap at 8k.  */
556
    for (len = 0; len < 0x2000; ++len) {
557 558
        uint8_t v = cpu_ldub_data_ra(env, s + len, ra);
        cpu_stb_data_ra(env, d + len, v, ra);
559
        if (v == c) {
560 561 562 563
            /* Complete.  Set CC=1 and advance R1.  */
            env->cc_op = 1;
            env->retxl = s;
            return d + len;
564 565
        }
    }
566 567 568 569 570

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

/* load access registers r1 to r3 from memory at a2 */
574
void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
575
{
576
    uintptr_t ra = GETPC();
577 578 579
    int i;

    for (i = r1;; i = (i + 1) % 16) {
580
        env->aregs[i] = cpu_ldl_data_ra(env, a2, ra);
581 582 583 584 585 586 587 588 589
        a2 += 4;

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

/* store access registers r1 to r3 in memory at a2 */
590
void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
591
{
592
    uintptr_t ra = GETPC();
593 594 595
    int i;

    for (i = r1;; i = (i + 1) % 16) {
596
        cpu_stl_data_ra(env, a2, env->aregs[i], ra);
597 598 599 600 601 602 603 604
        a2 += 4;

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

605 606 607 608
/* move long helper */
static inline uint32_t do_mvcl(CPUS390XState *env,
                               uint64_t *dest, uint64_t *destlen,
                               uint64_t *src, uint64_t *srclen,
609
                               uint16_t pad, int wordsize, uintptr_t ra)
610
{
611
    uint64_t len = MIN(*srclen, *destlen);
612 613
    uint32_t cc;

614
    if (*destlen == *srclen) {
615
        cc = 0;
616
    } else if (*destlen < *srclen) {
617 618 619 620 621
        cc = 1;
    } else {
        cc = 2;
    }

622 623 624 625 626 627
    /* Copy the src array */
    fast_memmove(env, *dest, *src, len, ra);
    *src += len;
    *srclen -= len;
    *dest += len;
    *destlen -= len;
628

629
    /* Pad the remaining area */
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
    if (wordsize == 1) {
        fast_memset(env, *dest, pad, *destlen, ra);
        *dest += *destlen;
        *destlen = 0;
    } else {
        /* If remaining length is odd, pad with odd byte first.  */
        if (*destlen & 1) {
            cpu_stb_data_ra(env, *dest, pad & 0xff, ra);
            *dest += 1;
            *destlen -= 1;
        }
        /* The remaining length is even, pad using words.  */
        for (; *destlen; *dest += 2, *destlen -= 2) {
            cpu_stw_data_ra(env, *dest, pad, ra);
        }
    }
646

647 648 649 650 651 652 653 654 655 656 657 658 659 660
    return cc;
}

/* move long */
uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
    uintptr_t ra = GETPC();
    uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
    uint64_t dest = get_address(env, r1);
    uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
    uint64_t src = get_address(env, r2);
    uint8_t pad = env->regs[r2 + 1] >> 24;
    uint32_t cc;

661
    cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 1, ra);
662

663 664
    env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, destlen);
    env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, srclen);
665 666
    set_address(env, r1, dest);
    set_address(env, r2, src);
667 668 669 670

    return cc;
}

671
/* move long extended */
672 673
uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
                       uint32_t r3)
674
{
675
    uintptr_t ra = GETPC();
676
    uint64_t destlen = get_length(env, r1 + 1);
677
    uint64_t dest = get_address(env, r1);
678
    uint64_t srclen = get_length(env, r3 + 1);
679
    uint64_t src = get_address(env, r3);
680
    uint8_t pad = a2;
681 682
    uint32_t cc;

683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705
    cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 1, ra);

    set_length(env, r1 + 1, destlen);
    set_length(env, r3 + 1, srclen);
    set_address(env, r1, dest);
    set_address(env, r3, src);

    return cc;
}

/* move long unicode */
uint32_t HELPER(mvclu)(CPUS390XState *env, uint32_t r1, uint64_t a2,
                       uint32_t r3)
{
    uintptr_t ra = GETPC();
    uint64_t destlen = get_length(env, r1 + 1);
    uint64_t dest = get_address(env, r1);
    uint64_t srclen = get_length(env, r3 + 1);
    uint64_t src = get_address(env, r3);
    uint16_t pad = a2;
    uint32_t cc;

    cc = do_mvcl(env, &dest, &destlen, &src, &srclen, pad, 2, ra);
706

707 708
    set_length(env, r1 + 1, destlen);
    set_length(env, r3 + 1, srclen);
709 710
    set_address(env, r1, dest);
    set_address(env, r3, src);
711 712 713 714

    return cc;
}

715 716 717 718
/* compare logical long helper */
static inline uint32_t do_clcl(CPUS390XState *env,
                               uint64_t *src1, uint64_t *src1len,
                               uint64_t *src3, uint64_t *src3len,
719 720
                               uint16_t pad, uint64_t limit,
                               int wordsize, uintptr_t ra)
721 722
{
    uint64_t len = MAX(*src1len, *src3len);
723 724
    uint32_t cc = 0;

725 726
    check_alignment(env, *src1len | *src3len, wordsize, ra);

727
    if (!len) {
728 729 730
        return cc;
    }

731
    /* Lest we fail to service interrupts in a timely manner, limit the
732 733 734
       amount of work we're willing to do.  */
    if (len > limit) {
        len = limit;
735
        cc = 3;
736 737
    }

738 739 740
    for (; len; len -= wordsize) {
        uint16_t v1 = pad;
        uint16_t v3 = pad;
741

742
        if (*src1len) {
743
            v1 = cpu_ldusize_data_ra(env, *src1, wordsize, ra);
744
        }
745
        if (*src3len) {
746
            v3 = cpu_ldusize_data_ra(env, *src3, wordsize, ra);
747 748 749 750
        }

        if (v1 != v3) {
            cc = (v1 < v3) ? 1 : 2;
751 752
            break;
        }
753

754
        if (*src1len) {
755 756
            *src1 += wordsize;
            *src1len -= wordsize;
757
        }
758
        if (*src3len) {
759 760
            *src3 += wordsize;
            *src3len -= wordsize;
761
        }
762 763
    }

764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
    return cc;
}


/* compare logical long */
uint32_t HELPER(clcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
{
    uintptr_t ra = GETPC();
    uint64_t src1len = extract64(env->regs[r1 + 1], 0, 24);
    uint64_t src1 = get_address(env, r1);
    uint64_t src3len = extract64(env->regs[r2 + 1], 0, 24);
    uint64_t src3 = get_address(env, r2);
    uint8_t pad = env->regs[r2 + 1] >> 24;
    uint32_t cc;

779
    cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, -1, 1, ra);
780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800

    env->regs[r1 + 1] = deposit64(env->regs[r1 + 1], 0, 24, src1len);
    env->regs[r2 + 1] = deposit64(env->regs[r2 + 1], 0, 24, src3len);
    set_address(env, r1, src1);
    set_address(env, r2, src3);

    return cc;
}

/* compare logical long extended memcompare insn with padding */
uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
                       uint32_t r3)
{
    uintptr_t ra = GETPC();
    uint64_t src1len = get_length(env, r1 + 1);
    uint64_t src1 = get_address(env, r1);
    uint64_t src3len = get_length(env, r3 + 1);
    uint64_t src3 = get_address(env, r3);
    uint8_t pad = a2;
    uint32_t cc;

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
    cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, 0x2000, 1, ra);

    set_length(env, r1 + 1, src1len);
    set_length(env, r3 + 1, src3len);
    set_address(env, r1, src1);
    set_address(env, r3, src3);

    return cc;
}

/* compare logical long unicode memcompare insn with padding */
uint32_t HELPER(clclu)(CPUS390XState *env, uint32_t r1, uint64_t a2,
                       uint32_t r3)
{
    uintptr_t ra = GETPC();
    uint64_t src1len = get_length(env, r1 + 1);
    uint64_t src1 = get_address(env, r1);
    uint64_t src3len = get_length(env, r3 + 1);
    uint64_t src3 = get_address(env, r3);
    uint16_t pad = a2;
    uint32_t cc = 0;

    cc = do_clcl(env, &src1, &src1len, &src3, &src3len, pad, 0x1000, 2, ra);
824

825 826 827 828
    set_length(env, r1 + 1, src1len);
    set_length(env, r3 + 1, src3len);
    set_address(env, r1, src1);
    set_address(env, r3, src3);
829 830 831 832 833

    return cc;
}

/* checksum */
R
Richard Henderson 已提交
834 835
uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
                      uint64_t src, uint64_t src_len)
836
{
837
    uintptr_t ra = GETPC();
R
Richard Henderson 已提交
838 839
    uint64_t max_len, len;
    uint64_t cksm = (uint32_t)r1;
840

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

R
Richard Henderson 已提交
845 846
    /* Process full words as available.  */
    for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
847
        cksm += (uint32_t)cpu_ldl_data_ra(env, src, ra);
848 849
    }

R
Richard Henderson 已提交
850
    switch (max_len - len) {
851
    case 1:
852
        cksm += cpu_ldub_data_ra(env, src, ra) << 24;
R
Richard Henderson 已提交
853
        len += 1;
854 855
        break;
    case 2:
856
        cksm += cpu_lduw_data_ra(env, src, ra) << 16;
R
Richard Henderson 已提交
857
        len += 2;
858 859
        break;
    case 3:
860 861
        cksm += cpu_lduw_data_ra(env, src, ra) << 16;
        cksm += cpu_ldub_data_ra(env, src + 2, ra) << 8;
R
Richard Henderson 已提交
862
        len += 3;
863 864 865
        break;
    }

R
Richard Henderson 已提交
866 867 868 869 870 871 872 873
    /* 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);
874

R
Richard Henderson 已提交
875 876 877
    /* Return both cksm and processed length.  */
    env->retxl = cksm;
    return len;
878 879
}

A
Aurelien Jarno 已提交
880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916
void HELPER(pack)(CPUS390XState *env, uint32_t len, uint64_t dest, uint64_t src)
{
    uintptr_t ra = GETPC();
    int len_dest = len >> 4;
    int len_src = len & 0xf;
    uint8_t b;

    dest += len_dest;
    src += len_src;

    /* last byte is special, it only flips the nibbles */
    b = cpu_ldub_data_ra(env, src, ra);
    cpu_stb_data_ra(env, dest, (b << 4) | (b >> 4), ra);
    src--;
    len_src--;

    /* now pack every value */
    while (len_dest >= 0) {
        b = 0;

        if (len_src > 0) {
            b = cpu_ldub_data_ra(env, src, ra) & 0x0f;
            src--;
            len_src--;
        }
        if (len_src > 0) {
            b |= cpu_ldub_data_ra(env, src, ra) << 4;
            src--;
            len_src--;
        }

        len_dest--;
        dest--;
        cpu_stb_data_ra(env, dest, b, ra);
    }
}

917 918
static inline void do_pkau(CPUS390XState *env, uint64_t dest, uint64_t src,
                           uint32_t srclen, int ssize, uintptr_t ra)
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
{
    int i;
    /* The destination operand is always 16 bytes long.  */
    const int destlen = 16;

    /* The operands are processed from right to left.  */
    src += srclen - 1;
    dest += destlen - 1;

    for (i = 0; i < destlen; i++) {
        uint8_t b = 0;

        /* Start with a positive sign */
        if (i == 0) {
            b = 0xc;
934
        } else if (srclen > ssize) {
935
            b = cpu_ldub_data_ra(env, src, ra) & 0x0f;
936 937
            src -= ssize;
            srclen -= ssize;
938 939
        }

940
        if (srclen > ssize) {
941
            b |= cpu_ldub_data_ra(env, src, ra) << 4;
942 943
            src -= ssize;
            srclen -= ssize;
944 945 946 947 948 949 950
        }

        cpu_stb_data_ra(env, dest, b, ra);
        dest--;
    }
}

951 952 953 954 955 956 957 958 959 960 961 962 963

void HELPER(pka)(CPUS390XState *env, uint64_t dest, uint64_t src,
                 uint32_t srclen)
{
    do_pkau(env, dest, src, srclen, 1, GETPC());
}

void HELPER(pku)(CPUS390XState *env, uint64_t dest, uint64_t src,
                 uint32_t srclen)
{
    do_pkau(env, dest, src, srclen, 2, GETPC());
}

964 965
void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
                  uint64_t src)
966
{
967
    uintptr_t ra = GETPC();
968 969 970 971 972 973 974 975 976
    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 */
977 978
    b = cpu_ldub_data_ra(env, src, ra);
    cpu_stb_data_ra(env, dest, (b << 4) | (b >> 4), ra);
979 980 981 982 983 984 985 986 987
    src--;
    len_src--;

    /* now pad every nibble with 0xf0 */

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

        if (len_src > 0) {
988
            cur_byte = cpu_ldub_data_ra(env, src, ra);
989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006
        }

        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;

1007
        cpu_stb_data_ra(env, dest, cur_byte, ra);
1008 1009 1010
    }
}

1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
uint32_t HELPER(unpka)(CPUS390XState *env, uint64_t dest, uint32_t destlen,
                       uint64_t src)
{
    uintptr_t ra = GETPC();
    int i;
    uint32_t cc;
    uint8_t b;
    /* The source operand is always 16 bytes long.  */
    const int srclen = 16;

    /* The operands are processed from right to left.  */
    src += srclen - 1;
    dest += destlen - 1;

    /* Check for the sign.  */
    b = cpu_ldub_data_ra(env, src, ra);
    src--;
    switch (b & 0xf) {
    case 0xa:
    case 0xc:
    case 0xe ... 0xf:
        cc = 0;  /* plus */
        break;
    case 0xb:
    case 0xd:
        cc = 1;  /* minus */
        break;
    default:
    case 0x0 ... 0x9:
        cc = 3;  /* invalid */
        break;
    }

    /* Now pad every nibble with 0x30, advancing one nibble at a time. */
    for (i = 0; i < destlen; i++) {
        if (i == 31) {
            /* If length is 32 bytes, the leftmost byte is 0. */
            b = 0;
        } else if (i % 2) {
            b = cpu_ldub_data_ra(env, src, ra);
            src--;
        } else {
            b >>= 4;
        }
        cpu_stb_data_ra(env, dest, 0x30 + (b & 0xf), ra);
        dest--;
    }

    return cc;
}

1062 1063
static uint32_t do_helper_tr(CPUS390XState *env, uint32_t len, uint64_t array,
                             uint64_t trans, uintptr_t ra)
1064
{
1065
    uint32_t i;
1066 1067

    for (i = 0; i <= len; i++) {
1068 1069 1070
        uint8_t byte = cpu_ldub_data_ra(env, array + i, ra);
        uint8_t new_byte = cpu_ldub_data_ra(env, trans + byte, ra);
        cpu_stb_data_ra(env, array + i, new_byte, ra);
1071
    }
1072 1073

    return env->cc_op;
1074 1075
}

1076 1077 1078
void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
                uint64_t trans)
{
1079
    do_helper_tr(env, len, array, trans, GETPC());
1080 1081
}

1082 1083 1084
uint64_t HELPER(tre)(CPUS390XState *env, uint64_t array,
                     uint64_t len, uint64_t trans)
{
1085
    uintptr_t ra = GETPC();
1086 1087 1088
    uint8_t end = env->regs[0] & 0xff;
    uint64_t l = len;
    uint64_t i;
1089
    uint32_t cc = 0;
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099

    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;
1100
        cc = 3;
1101 1102 1103 1104 1105
    }

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

1106
        byte = cpu_ldub_data_ra(env, array + i, ra);
1107 1108

        if (byte == end) {
1109
            cc = 1;
1110 1111 1112
            break;
        }

1113 1114
        new_byte = cpu_ldub_data_ra(env, trans + byte, ra);
        cpu_stb_data_ra(env, array + i, new_byte, ra);
1115 1116
    }

1117
    env->cc_op = cc;
1118 1119 1120 1121
    env->retxl = len - i;
    return array + i;
}

1122 1123
static uint32_t do_helper_trt(CPUS390XState *env, uint32_t len, uint64_t array,
                              uint64_t trans, uintptr_t ra)
1124
{
1125
    uint32_t i;
1126 1127

    for (i = 0; i <= len; i++) {
1128 1129
        uint8_t byte = cpu_ldub_data_ra(env, array + i, ra);
        uint8_t sbyte = cpu_ldub_data_ra(env, trans + byte, ra);
1130 1131

        if (sbyte != 0) {
1132
            set_address(env, 1, array + i);
1133 1134
            env->regs[2] = deposit64(env->regs[2], 0, 8, sbyte);
            return (i == len) ? 2 : 1;
1135 1136 1137
        }
    }

1138 1139 1140 1141 1142 1143 1144
    return 0;
}

uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array,
                     uint64_t trans)
{
    return do_helper_trt(env, len, array, trans, GETPC());
1145 1146
}

1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185
void HELPER(cdsg)(CPUS390XState *env, uint64_t addr,
                  uint32_t r1, uint32_t r3)
{
    uintptr_t ra = GETPC();
    Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]);
    Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]);
    Int128 oldv;
    bool fail;

    if (parallel_cpus) {
#ifndef CONFIG_ATOMIC128
        cpu_loop_exit_atomic(ENV_GET_CPU(env), ra);
#else
        int mem_idx = cpu_mmu_index(env, false);
        TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx);
        oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra);
        fail = !int128_eq(oldv, cmpv);
#endif
    } else {
        uint64_t oldh, oldl;

        oldh = cpu_ldq_data_ra(env, addr + 0, ra);
        oldl = cpu_ldq_data_ra(env, addr + 8, ra);

        oldv = int128_make128(oldl, oldh);
        fail = !int128_eq(oldv, cmpv);
        if (fail) {
            newv = oldv;
        }

        cpu_stq_data_ra(env, addr + 0, int128_gethi(newv), ra);
        cpu_stq_data_ra(env, addr + 8, int128_getlo(newv), ra);
    }

    env->cc_op = fail;
    env->regs[r1] = int128_gethi(oldv);
    env->regs[r1 + 1] = int128_getlo(oldv);
}

1186
#if !defined(CONFIG_USER_ONLY)
1187
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
1188
{
1189
    uintptr_t ra = GETPC();
1190
    S390CPU *cpu = s390_env_get_cpu(env);
1191
    bool PERchanged = false;
1192
    uint64_t src = a2;
1193
    uint32_t i;
1194 1195

    for (i = r1;; i = (i + 1) % 16) {
1196
        uint64_t val = cpu_ldq_data_ra(env, src, ra);
1197 1198 1199 1200
        if (env->cregs[i] != val && i >= 9 && i <= 11) {
            PERchanged = true;
        }
        env->cregs[i] = val;
1201
        HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
1202
                   i, src, val);
1203 1204 1205 1206 1207 1208 1209
        src += sizeof(uint64_t);

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

1210 1211 1212 1213
    if (PERchanged && env->psw.mask & PSW_MASK_PER) {
        s390_cpu_recompute_watchpoints(CPU(cpu));
    }

1214
    tlb_flush(CPU(cpu));
1215 1216
}

1217
void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
1218
{
1219
    uintptr_t ra = GETPC();
1220
    S390CPU *cpu = s390_env_get_cpu(env);
1221
    bool PERchanged = false;
1222
    uint64_t src = a2;
1223
    uint32_t i;
1224 1225

    for (i = r1;; i = (i + 1) % 16) {
1226
        uint32_t val = cpu_ldl_data_ra(env, src, ra);
1227 1228 1229
        if ((uint32_t)env->cregs[i] != val && i >= 9 && i <= 11) {
            PERchanged = true;
        }
1230 1231
        env->cregs[i] = deposit64(env->cregs[i], 0, 32, val);
        HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%x\n", i, src, val);
1232 1233 1234 1235 1236 1237 1238
        src += sizeof(uint32_t);

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

1239 1240 1241 1242
    if (PERchanged && env->psw.mask & PSW_MASK_PER) {
        s390_cpu_recompute_watchpoints(CPU(cpu));
    }

1243
    tlb_flush(CPU(cpu));
1244 1245
}

1246
void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
1247
{
1248
    uintptr_t ra = GETPC();
1249
    uint64_t dest = a2;
1250
    uint32_t i;
1251 1252

    for (i = r1;; i = (i + 1) % 16) {
1253
        cpu_stq_data_ra(env, dest, env->cregs[i], ra);
1254 1255 1256 1257 1258 1259 1260 1261
        dest += sizeof(uint64_t);

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

1262
void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
1263
{
1264
    uintptr_t ra = GETPC();
1265
    uint64_t dest = a2;
1266
    uint32_t i;
1267 1268

    for (i = r1;; i = (i + 1) % 16) {
1269
        cpu_stl_data_ra(env, dest, env->cregs[i], ra);
1270 1271 1272 1273 1274 1275 1276 1277
        dest += sizeof(uint32_t);

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

1278 1279
uint32_t HELPER(testblock)(CPUS390XState *env, uint64_t real_addr)
{
1280
    uintptr_t ra = GETPC();
1281 1282 1283 1284
    CPUState *cs = CPU(s390_env_get_cpu(env));
    uint64_t abs_addr;
    int i;

1285
    real_addr = wrap_address(env, real_addr);
1286 1287 1288
    abs_addr = mmu_real2abs(env, real_addr) & TARGET_PAGE_MASK;
    if (!address_space_access_valid(&address_space_memory, abs_addr,
                                    TARGET_PAGE_SIZE, true)) {
1289
        cpu_restore_state(cs, ra);
1290 1291 1292 1293 1294 1295
        program_interrupt(env, PGM_ADDRESSING, 4);
        return 1;
    }

    /* Check low-address protection */
    if ((env->cregs[0] & CR0_LOWPROT) && real_addr < 0x2000) {
1296
        cpu_restore_state(cs, ra);
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307
        program_interrupt(env, PGM_PROTECTION, 4);
        return 1;
    }

    for (i = 0; i < TARGET_PAGE_SIZE; i += 8) {
        stq_phys(cs->as, abs_addr + i, 0);
    }

    return 0;
}

1308 1309 1310 1311 1312 1313 1314
uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
{
    /* XXX implement */
    return 0;
}

/* insert storage key extended */
1315
uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
1316
{
1317 1318
    static S390SKeysState *ss;
    static S390SKeysClass *skeyclass;
1319
    uint64_t addr = wrap_address(env, r2);
1320
    uint8_t key;
1321 1322 1323 1324 1325

    if (addr > ram_size) {
        return 0;
    }

1326 1327 1328 1329 1330 1331 1332 1333 1334
    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;
1335 1336 1337
}

/* set storage key extended */
R
Richard Henderson 已提交
1338
void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
1339
{
1340 1341
    static S390SKeysState *ss;
    static S390SKeysClass *skeyclass;
1342
    uint64_t addr = wrap_address(env, r2);
1343
    uint8_t key;
1344 1345 1346 1347 1348

    if (addr > ram_size) {
        return;
    }

1349 1350 1351 1352 1353 1354 1355
    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);
1356 1357 1358
}

/* reset reference bit extended */
R
Richard Henderson 已提交
1359
uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
1360
{
1361 1362 1363
    static S390SKeysState *ss;
    static S390SKeysClass *skeyclass;
    uint8_t re, key;
1364 1365 1366 1367 1368

    if (r2 > ram_size) {
        return 0;
    }

1369 1370 1371 1372 1373 1374 1375 1376 1377
    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;
    }

1378
    re = key & (SK_R | SK_C);
1379 1380 1381 1382 1383
    key &= ~SK_R;

    if (skeyclass->set_skeys(ss, r2 / TARGET_PAGE_SIZE, 1, &key)) {
        return 0;
    }
1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396

    /*
     * 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;
}

1397
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1398
{
1399
    uintptr_t ra = GETPC();
1400
    int cc = 0, i;
1401

1402 1403 1404 1405
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
               __func__, l, a1, a2);

    if (l > 256) {
1406 1407 1408 1409 1410 1411 1412
        /* max 256 */
        l = 256;
        cc = 3;
    }

    /* XXX replace w/ memcpy */
    for (i = 0; i < l; i++) {
1413 1414
        uint8_t x = cpu_ldub_primary_ra(env, a2 + i, ra);
        cpu_stb_secondary_ra(env, a1 + i, x, ra);
1415 1416 1417 1418 1419
    }

    return cc;
}

1420
uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1421
{
1422
    uintptr_t ra = GETPC();
1423 1424
    int cc = 0, i;

1425 1426 1427
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
               __func__, l, a1, a2);

1428 1429 1430 1431 1432
    if (l > 256) {
        /* max 256 */
        l = 256;
        cc = 3;
    }
1433

1434 1435
    /* XXX replace w/ memcpy */
    for (i = 0; i < l; i++) {
1436 1437
        uint8_t x = cpu_ldub_secondary_ra(env, a2 + i, ra);
        cpu_stb_primary_ra(env, a1 + i, x, ra);
1438
    }
1439

1440
    return cc;
1441 1442 1443
}

/* invalidate pte */
1444 1445
void HELPER(ipte)(CPUS390XState *env, uint64_t pto, uint64_t vaddr,
                  uint32_t m4)
1446
{
1447
    CPUState *cs = CPU(s390_env_get_cpu(env));
1448
    uint64_t page = vaddr & TARGET_PAGE_MASK;
1449
    uint64_t pte_addr, pte;
1450

1451 1452
    /* Compute the page table entry address */
    pte_addr = (pto & _SEGMENT_ENTRY_ORIGIN);
1453
    pte_addr += (vaddr & VADDR_PX) >> 9;
1454 1455 1456 1457 1458

    /* Mark the page table entry as invalid */
    pte = ldq_phys(cs->as, pte_addr);
    pte |= _PAGE_INVALID;
    stq_phys(cs->as, pte_addr, pte);
1459 1460 1461

    /* XXX we exploit the fact that Linux passes the exact virtual
       address here - it's not obliged to! */
1462 1463 1464 1465 1466 1467 1468
    /* XXX: the LC bit should be considered as 0 if the local-TLB-clearing
       facility is not installed.  */
    if (m4 & 1) {
        tlb_flush_page(cs, page);
    } else {
        tlb_flush_page_all_cpus_synced(cs, page);
    }
1469 1470

    /* XXX 31-bit hack */
1471 1472
    if (m4 & 1) {
        tlb_flush_page(cs, page ^ 0x80000000);
1473
    } else {
1474
        tlb_flush_page_all_cpus_synced(cs, page ^ 0x80000000);
1475 1476 1477 1478
    }
}

/* flush local tlb */
1479
void HELPER(ptlb)(CPUS390XState *env)
1480
{
1481 1482
    S390CPU *cpu = s390_env_get_cpu(env);

1483
    tlb_flush(CPU(cpu));
1484 1485
}

1486 1487 1488 1489 1490 1491 1492 1493
/* flush global tlb */
void HELPER(purge)(CPUS390XState *env)
{
    S390CPU *cpu = s390_env_get_cpu(env);

    tlb_flush_all_cpus_synced(CPU(cpu));
}

1494 1495 1496 1497 1498
/* load using real address */
uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr)
{
    CPUState *cs = CPU(s390_env_get_cpu(env));

1499
    return (uint32_t)ldl_phys(cs->as, wrap_address(env, addr));
1500 1501 1502 1503 1504 1505
}

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

1506
    return ldq_phys(cs->as, wrap_address(env, addr));
1507 1508
}

1509
/* store using real address */
R
Richard Henderson 已提交
1510
void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1511
{
1512 1513
    CPUState *cs = CPU(s390_env_get_cpu(env));

1514
    stl_phys(cs->as, wrap_address(env, addr), (uint32_t)v1);
1515 1516 1517 1518 1519 1520 1521 1522

    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);
    }
1523 1524
}

1525 1526 1527 1528
void HELPER(sturg)(CPUS390XState *env, uint64_t addr, uint64_t v1)
{
    CPUState *cs = CPU(s390_env_get_cpu(env));

1529
    stq_phys(cs->as, wrap_address(env, addr), v1);
1530 1531 1532 1533 1534 1535 1536 1537

    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);
    }
1538 1539
}

1540
/* load real address */
R
Richard Henderson 已提交
1541
uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1542
{
1543
    CPUState *cs = CPU(s390_env_get_cpu(env));
1544 1545 1546
    uint32_t cc = 0;
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
    uint64_t ret;
1547
    int old_exc, flags;
1548 1549 1550

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

1555
    old_exc = cs->exception_index;
1556
    if (mmu_translate(env, addr, 0, asc, &ret, &flags, true)) {
1557 1558
        cc = 3;
    }
1559
    if (cs->exception_index == EXCP_PGM) {
1560 1561 1562 1563
        ret = env->int_pgm_code | 0x80000000;
    } else {
        ret |= addr & ~TARGET_PAGE_MASK;
    }
1564
    cs->exception_index = old_exc;
1565

R
Richard Henderson 已提交
1566 1567
    env->cc_op = cc;
    return ret;
1568 1569
}
#endif
1570

1571 1572 1573 1574 1575 1576
/* 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.

   Perform this by recording the modified instruction in env->ex_value.
   This will be noticed by cpu_get_tb_cpu_state and thus tb translation.
1577
*/
1578
void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr)
1579
{
1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600
    uint64_t insn = cpu_lduw_code(env, addr);
    uint8_t opc = insn >> 8;

    /* Or in the contents of R1[56:63].  */
    insn |= r1 & 0xff;

    /* Load the rest of the instruction.  */
    insn <<= 48;
    switch (get_ilen(opc)) {
    case 2:
        break;
    case 4:
        insn |= (uint64_t)cpu_lduw_code(env, addr + 2) << 32;
        break;
    case 6:
        insn |= (uint64_t)(uint32_t)cpu_ldl_code(env, addr + 2) << 16;
        break;
    default:
        g_assert_not_reached();
    }

1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621
    /* The very most common cases can be sped up by avoiding a new TB.  */
    if ((opc & 0xf0) == 0xd0) {
        typedef uint32_t (*dx_helper)(CPUS390XState *, uint32_t, uint64_t,
                                      uint64_t, uintptr_t);
        static const dx_helper dx[16] = {
            [0x2] = do_helper_mvc,
            [0x4] = do_helper_nc,
            [0x5] = do_helper_clc,
            [0x6] = do_helper_oc,
            [0x7] = do_helper_xc,
            [0xc] = do_helper_tr,
            [0xd] = do_helper_trt,
        };
        dx_helper helper = dx[opc & 0xf];

        if (helper) {
            uint32_t l = extract64(insn, 48, 8);
            uint32_t b1 = extract64(insn, 44, 4);
            uint32_t d1 = extract64(insn, 32, 12);
            uint32_t b2 = extract64(insn, 28, 4);
            uint32_t d2 = extract64(insn, 16, 12);
1622 1623
            uint64_t a1 = wrap_address(env, env->regs[b1] + d1);
            uint64_t a2 = wrap_address(env, env->regs[b2] + d2);
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635

            env->cc_op = helper(env, l, a1, a2, 0);
            env->psw.addr += ilen;
            return;
        }
    } else if (opc == 0x0a) {
        env->int_svc_code = extract64(insn, 48, 8);
        env->int_svc_ilen = ilen;
        helper_exception(env, EXCP_SVC);
        g_assert_not_reached();
    }

1636 1637 1638 1639 1640
    /* Record the insn we want to execute as well as the ilen to use
       during the execution of the target insn.  This will also ensure
       that ex_value is non-zero, which flags that we are in a state
       that requires such execution.  */
    env->ex_value = insn | ilen;
1641
}