Statistics
| Branch: | Revision:

root / target-s390x / mem_helper.c @ 795ca114

History | View | Annotate | Download (27.5 kB)

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

    
21
#include "cpu.h"
22
#include "helper.h"
23

    
24
/*****************************************************************************/
25
/* Softmmu support */
26
#if !defined(CONFIG_USER_ONLY)
27
#include "exec/softmmu_exec.h"
28

    
29
#define MMUSUFFIX _mmu
30

    
31
#define SHIFT 0
32
#include "exec/softmmu_template.h"
33

    
34
#define SHIFT 1
35
#include "exec/softmmu_template.h"
36

    
37
#define SHIFT 2
38
#include "exec/softmmu_template.h"
39

    
40
#define SHIFT 3
41
#include "exec/softmmu_template.h"
42

    
43
/* try to fill the TLB and return an exception if error. If retaddr is
44
   NULL, it means that the function was called in C code (i.e. not
45
   from generated code or from helper.c) */
46
/* XXX: fix it to restore all registers */
47
void tlb_fill(CPUS390XState *env, target_ulong addr, int is_write, int mmu_idx,
48
              uintptr_t retaddr)
49
{
50
    int ret;
51

    
52
    ret = cpu_s390x_handle_mmu_fault(env, addr, is_write, mmu_idx);
53
    if (unlikely(ret != 0)) {
54
        if (likely(retaddr)) {
55
            /* now we have a real cpu fault */
56
            cpu_restore_state(env, retaddr);
57
        }
58
        cpu_loop_exit(env);
59
    }
60
}
61

    
62
#endif
63

    
64
/* #define DEBUG_HELPER */
65
#ifdef DEBUG_HELPER
66
#define HELPER_LOG(x...) qemu_log(x)
67
#else
68
#define HELPER_LOG(x...)
69
#endif
70

    
71
#ifndef CONFIG_USER_ONLY
72
static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest,
73
                            uint8_t byte)
74
{
75
    hwaddr dest_phys;
76
    hwaddr len = l;
77
    void *dest_p;
78
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
79
    int flags;
80

    
81
    if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
82
        cpu_stb_data(env, dest, byte);
83
        cpu_abort(env, "should never reach here");
84
    }
85
    dest_phys |= dest & ~TARGET_PAGE_MASK;
86

    
87
    dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
88

    
89
    memset(dest_p, byte, len);
90

    
91
    cpu_physical_memory_unmap(dest_p, 1, len, len);
92
}
93

    
94
static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
95
                             uint64_t src)
96
{
97
    hwaddr dest_phys;
98
    hwaddr src_phys;
99
    hwaddr len = l;
100
    void *dest_p;
101
    void *src_p;
102
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
103
    int flags;
104

    
105
    if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
106
        cpu_stb_data(env, dest, 0);
107
        cpu_abort(env, "should never reach here");
108
    }
109
    dest_phys |= dest & ~TARGET_PAGE_MASK;
110

    
111
    if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) {
112
        cpu_ldub_data(env, src);
113
        cpu_abort(env, "should never reach here");
114
    }
115
    src_phys |= src & ~TARGET_PAGE_MASK;
116

    
117
    dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
118
    src_p = cpu_physical_memory_map(src_phys, &len, 0);
119

    
120
    memmove(dest_p, src_p, len);
121

    
122
    cpu_physical_memory_unmap(dest_p, 1, len, len);
123
    cpu_physical_memory_unmap(src_p, 0, len, len);
124
}
125
#endif
126

    
127
/* and on array */
128
uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
129
                    uint64_t src)
130
{
131
    int i;
132
    unsigned char x;
133
    uint32_t cc = 0;
134

    
135
    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
136
               __func__, l, dest, src);
137
    for (i = 0; i <= l; i++) {
138
        x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
139
        if (x) {
140
            cc = 1;
141
        }
142
        cpu_stb_data(env, dest + i, x);
143
    }
144
    return cc;
145
}
146

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

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

    
158
#ifndef CONFIG_USER_ONLY
159
    /* xor with itself is the same as memset(0) */
160
    if ((l > 32) && (src == dest) &&
161
        (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) {
162
        mvc_fast_memset(env, l + 1, dest, 0);
163
        return 0;
164
    }
165
#else
166
    if (src == dest) {
167
        memset(g2h(dest), 0, l + 1);
168
        return 0;
169
    }
170
#endif
171

    
172
    for (i = 0; i <= l; i++) {
173
        x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
174
        if (x) {
175
            cc = 1;
176
        }
177
        cpu_stb_data(env, dest + i, x);
178
    }
179
    return cc;
180
}
181

    
182
/* or on array */
183
uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
184
                    uint64_t src)
185
{
186
    int i;
187
    unsigned char x;
188
    uint32_t cc = 0;
189

    
190
    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
191
               __func__, l, dest, src);
192
    for (i = 0; i <= l; i++) {
193
        x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
194
        if (x) {
195
            cc = 1;
196
        }
197
        cpu_stb_data(env, dest + i, x);
198
    }
199
    return cc;
200
}
201

    
202
/* memmove */
203
void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
204
{
205
    int i = 0;
206
    int x = 0;
207
    uint32_t l_64 = (l + 1) / 8;
208

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

    
212
#ifndef CONFIG_USER_ONLY
213
    if ((l > 32) &&
214
        (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) &&
215
        (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) {
216
        if (dest == (src + 1)) {
217
            mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src));
218
            return;
219
        } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
220
            mvc_fast_memmove(env, l + 1, dest, src);
221
            return;
222
        }
223
    }
224
#else
225
    if (dest == (src + 1)) {
226
        memset(g2h(dest), cpu_ldub_data(env, src), l + 1);
227
        return;
228
    } else {
229
        memmove(g2h(dest), g2h(src), l + 1);
230
        return;
231
    }
232
#endif
233

    
234
    /* handle the parts that fit into 8-byte loads/stores */
235
    if (dest != (src + 1)) {
236
        for (i = 0; i < l_64; i++) {
237
            cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
238
            x += 8;
239
        }
240
    }
241

    
242
    /* slow version crossing pages with byte accesses */
243
    for (i = x; i <= l; i++) {
244
        cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
245
    }
246
}
247

    
248
/* compare unsigned byte arrays */
249
uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
250
{
251
    int i;
252
    unsigned char x, y;
253
    uint32_t cc;
254

    
255
    HELPER_LOG("%s l %d s1 %" PRIx64 " s2 %" PRIx64 "\n",
256
               __func__, l, s1, s2);
257
    for (i = 0; i <= l; i++) {
258
        x = cpu_ldub_data(env, s1 + i);
259
        y = cpu_ldub_data(env, s2 + i);
260
        HELPER_LOG("%02x (%c)/%02x (%c) ", x, x, y, y);
261
        if (x < y) {
262
            cc = 1;
263
            goto done;
264
        } else if (x > y) {
265
            cc = 2;
266
            goto done;
267
        }
268
    }
269
    cc = 0;
270
 done:
271
    HELPER_LOG("\n");
272
    return cc;
273
}
274

    
275
/* compare logical under mask */
276
uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
277
                     uint64_t addr)
278
{
279
    uint8_t r, d;
280
    uint32_t cc;
281

    
282
    HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%" PRIx64 "\n", __func__, r1,
283
               mask, addr);
284
    cc = 0;
285
    while (mask) {
286
        if (mask & 8) {
287
            d = cpu_ldub_data(env, addr);
288
            r = (r1 & 0xff000000UL) >> 24;
289
            HELPER_LOG("mask 0x%x %02x/%02x (0x%" PRIx64 ") ", mask, r, d,
290
                       addr);
291
            if (r < d) {
292
                cc = 1;
293
                break;
294
            } else if (r > d) {
295
                cc = 2;
296
                break;
297
            }
298
            addr++;
299
        }
300
        mask = (mask << 1) & 0xf;
301
        r1 <<= 8;
302
    }
303
    HELPER_LOG("\n");
304
    return cc;
305
}
306

    
307
static inline uint64_t fix_address(CPUS390XState *env, uint64_t a)
308
{
309
    /* 31-Bit mode */
310
    if (!(env->psw.mask & PSW_MASK_64)) {
311
        a &= 0x7fffffff;
312
    }
313
    return a;
314
}
315

    
316
static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
317
{
318
    uint64_t r = d2;
319
    if (x2) {
320
        r += env->regs[x2];
321
    }
322
    if (b2) {
323
        r += env->regs[b2];
324
    }
325
    return fix_address(env, r);
326
}
327

    
328
static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
329
{
330
    return fix_address(env, env->regs[reg]);
331
}
332

    
333
/* search string (c is byte to search, r2 is string, r1 end of string) */
334
uint64_t HELPER(srst)(CPUS390XState *env, uint64_t r0, uint64_t end,
335
                      uint64_t str)
336
{
337
    uint32_t len;
338
    uint8_t v, c = r0;
339

    
340
    str = fix_address(env, str);
341
    end = fix_address(env, end);
342

    
343
    /* Assume for now that R2 is unmodified.  */
344
    env->retxl = str;
345

    
346
    /* Lest we fail to service interrupts in a timely manner, limit the
347
       amount of work we're willing to do.  For now, let's cap at 8k.  */
348
    for (len = 0; len < 0x2000; ++len) {
349
        if (str + len == end) {
350
            /* Character not found.  R1 & R2 are unmodified.  */
351
            env->cc_op = 2;
352
            return end;
353
        }
354
        v = cpu_ldub_data(env, str + len);
355
        if (v == c) {
356
            /* Character found.  Set R1 to the location; R2 is unmodified.  */
357
            env->cc_op = 1;
358
            return str + len;
359
        }
360
    }
361

    
362
    /* CPU-determined bytes processed.  Advance R2 to next byte to process.  */
363
    env->retxl = str + len;
364
    env->cc_op = 3;
365
    return end;
366
}
367

    
368
/* unsigned string compare (c is string terminator) */
369
uint64_t HELPER(clst)(CPUS390XState *env, uint64_t c, uint64_t s1, uint64_t s2)
370
{
371
    uint32_t len;
372

    
373
    c = c & 0xff;
374
    s1 = fix_address(env, s1);
375
    s2 = fix_address(env, s2);
376

    
377
    /* Lest we fail to service interrupts in a timely manner, limit the
378
       amount of work we're willing to do.  For now, let's cap at 8k.  */
379
    for (len = 0; len < 0x2000; ++len) {
380
        uint8_t v1 = cpu_ldub_data(env, s1 + len);
381
        uint8_t v2 = cpu_ldub_data(env, s2 + len);
382
        if (v1 == v2) {
383
            if (v1 == c) {
384
                /* Equal.  CC=0, and don't advance the registers.  */
385
                env->cc_op = 0;
386
                env->retxl = s2;
387
                return s1;
388
            }
389
        } else {
390
            /* Unequal.  CC={1,2}, and advance the registers.  Note that
391
               the terminator need not be zero, but the string that contains
392
               the terminator is by definition "low".  */
393
            env->cc_op = (v1 == c ? 1 : v2 == c ? 2 : v1 < v2 ? 1 : 2);
394
            env->retxl = s2 + len;
395
            return s1 + len;
396
        }
397
    }
398

    
399
    /* CPU-determined bytes equal; advance the registers.  */
400
    env->cc_op = 3;
401
    env->retxl = s2 + len;
402
    return s1 + len;
403
}
404

    
405
/* move page */
406
void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
407
{
408
    /* XXX missing r0 handling */
409
    env->cc_op = 0;
410
#ifdef CONFIG_USER_ONLY
411
    memmove(g2h(r1), g2h(r2), TARGET_PAGE_SIZE);
412
#else
413
    mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
414
#endif
415
}
416

    
417
/* string copy (c is string terminator) */
418
uint64_t HELPER(mvst)(CPUS390XState *env, uint64_t c, uint64_t d, uint64_t s)
419
{
420
    uint32_t len;
421

    
422
    c = c & 0xff;
423
    d = fix_address(env, d);
424
    s = fix_address(env, s);
425

    
426
    /* Lest we fail to service interrupts in a timely manner, limit the
427
       amount of work we're willing to do.  For now, let's cap at 8k.  */
428
    for (len = 0; len < 0x2000; ++len) {
429
        uint8_t v = cpu_ldub_data(env, s + len);
430
        cpu_stb_data(env, d + len, v);
431
        if (v == c) {
432
            /* Complete.  Set CC=1 and advance R1.  */
433
            env->cc_op = 1;
434
            env->retxl = s;
435
            return d + len;
436
        }
437
    }
438

    
439
    /* Incomplete.  Set CC=3 and signal to advance R1 and R2.  */
440
    env->cc_op = 3;
441
    env->retxl = s + len;
442
    return d + len;
443
}
444

    
445
static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
446
                           uint32_t mask)
447
{
448
    int pos = 24; /* top of the lower half of r1 */
449
    uint64_t rmask = 0xff000000ULL;
450
    uint8_t val = 0;
451
    int ccd = 0;
452
    uint32_t cc = 0;
453

    
454
    while (mask) {
455
        if (mask & 8) {
456
            env->regs[r1] &= ~rmask;
457
            val = cpu_ldub_data(env, address);
458
            if ((val & 0x80) && !ccd) {
459
                cc = 1;
460
            }
461
            ccd = 1;
462
            if (val && cc == 0) {
463
                cc = 2;
464
            }
465
            env->regs[r1] |= (uint64_t)val << pos;
466
            address++;
467
        }
468
        mask = (mask << 1) & 0xf;
469
        pos -= 8;
470
        rmask >>= 8;
471
    }
472

    
473
    return cc;
474
}
475

    
476
/* execute instruction
477
   this instruction executes an insn modified with the contents of r1
478
   it does not change the executed instruction in memory
479
   it does not change the program counter
480
   in other words: tricky...
481
   currently implemented by interpreting the cases it is most commonly used in
482
*/
483
uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
484
                    uint64_t addr, uint64_t ret)
485
{
486
    uint16_t insn = cpu_lduw_code(env, addr);
487

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

    
493
        l = v1 & 0xff;
494
        insn2 = cpu_ldl_code(env, addr + 2);
495
        b1 = (insn2 >> 28) & 0xf;
496
        b2 = (insn2 >> 12) & 0xf;
497
        d1 = (insn2 >> 16) & 0xfff;
498
        d2 = insn2 & 0xfff;
499
        switch (insn & 0xf00) {
500
        case 0x200:
501
            helper_mvc(env, l, get_address(env, 0, b1, d1),
502
                       get_address(env, 0, b2, d2));
503
            break;
504
        case 0x500:
505
            cc = helper_clc(env, l, get_address(env, 0, b1, d1),
506
                            get_address(env, 0, b2, d2));
507
            break;
508
        case 0x700:
509
            cc = helper_xc(env, l, get_address(env, 0, b1, d1),
510
                           get_address(env, 0, b2, d2));
511
            break;
512
        case 0xc00:
513
            helper_tr(env, l, get_address(env, 0, b1, d1),
514
                      get_address(env, 0, b2, d2));
515
            break;
516
        default:
517
            goto abort;
518
        }
519
    } else if ((insn & 0xff00) == 0x0a00) {
520
        /* supervisor call */
521
        HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
522
        env->psw.addr = ret - 4;
523
        env->int_svc_code = (insn | v1) & 0xff;
524
        env->int_svc_ilen = 4;
525
        helper_exception(env, EXCP_SVC);
526
    } else if ((insn & 0xff00) == 0xbf00) {
527
        uint32_t insn2, r1, r3, b2, d2;
528

    
529
        insn2 = cpu_ldl_code(env, addr + 2);
530
        r1 = (insn2 >> 20) & 0xf;
531
        r3 = (insn2 >> 16) & 0xf;
532
        b2 = (insn2 >> 12) & 0xf;
533
        d2 = insn2 & 0xfff;
534
        cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
535
    } else {
536
    abort:
537
        cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
538
                  insn);
539
    }
540
    return cc;
541
}
542

    
543
/* load access registers r1 to r3 from memory at a2 */
544
void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
545
{
546
    int i;
547

    
548
    for (i = r1;; i = (i + 1) % 16) {
549
        env->aregs[i] = cpu_ldl_data(env, a2);
550
        a2 += 4;
551

    
552
        if (i == r3) {
553
            break;
554
        }
555
    }
556
}
557

    
558
/* store access registers r1 to r3 in memory at a2 */
559
void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
560
{
561
    int i;
562

    
563
    for (i = r1;; i = (i + 1) % 16) {
564
        cpu_stl_data(env, a2, env->aregs[i]);
565
        a2 += 4;
566

    
567
        if (i == r3) {
568
            break;
569
        }
570
    }
571
}
572

    
573
/* move long */
574
uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
575
{
576
    uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
577
    uint64_t dest = get_address_31fix(env, r1);
578
    uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
579
    uint64_t src = get_address_31fix(env, r2);
580
    uint8_t pad = src >> 24;
581
    uint8_t v;
582
    uint32_t cc;
583

    
584
    if (destlen == srclen) {
585
        cc = 0;
586
    } else if (destlen < srclen) {
587
        cc = 1;
588
    } else {
589
        cc = 2;
590
    }
591

    
592
    if (srclen > destlen) {
593
        srclen = destlen;
594
    }
595

    
596
    for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
597
        v = cpu_ldub_data(env, src);
598
        cpu_stb_data(env, dest, v);
599
    }
600

    
601
    for (; destlen; dest++, destlen--) {
602
        cpu_stb_data(env, dest, pad);
603
    }
604

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

    
611
    return cc;
612
}
613

    
614
/* move long extended another memcopy insn with more bells and whistles */
615
uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
616
                       uint32_t r3)
617
{
618
    uint64_t destlen = env->regs[r1 + 1];
619
    uint64_t dest = env->regs[r1];
620
    uint64_t srclen = env->regs[r3 + 1];
621
    uint64_t src = env->regs[r3];
622
    uint8_t pad = a2 & 0xff;
623
    uint8_t v;
624
    uint32_t cc;
625

    
626
    if (!(env->psw.mask & PSW_MASK_64)) {
627
        destlen = (uint32_t)destlen;
628
        srclen = (uint32_t)srclen;
629
        dest &= 0x7fffffff;
630
        src &= 0x7fffffff;
631
    }
632

    
633
    if (destlen == srclen) {
634
        cc = 0;
635
    } else if (destlen < srclen) {
636
        cc = 1;
637
    } else {
638
        cc = 2;
639
    }
640

    
641
    if (srclen > destlen) {
642
        srclen = destlen;
643
    }
644

    
645
    for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
646
        v = cpu_ldub_data(env, src);
647
        cpu_stb_data(env, dest, v);
648
    }
649

    
650
    for (; destlen; dest++, destlen--) {
651
        cpu_stb_data(env, dest, pad);
652
    }
653

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

    
661
    return cc;
662
}
663

    
664
/* compare logical long extended memcompare insn with padding */
665
uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
666
                       uint32_t r3)
667
{
668
    uint64_t destlen = env->regs[r1 + 1];
669
    uint64_t dest = get_address_31fix(env, r1);
670
    uint64_t srclen = env->regs[r3 + 1];
671
    uint64_t src = get_address_31fix(env, r3);
672
    uint8_t pad = a2 & 0xff;
673
    uint8_t v1 = 0, v2 = 0;
674
    uint32_t cc = 0;
675

    
676
    if (!(destlen || srclen)) {
677
        return cc;
678
    }
679

    
680
    if (srclen > destlen) {
681
        srclen = destlen;
682
    }
683

    
684
    for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
685
        v1 = srclen ? cpu_ldub_data(env, src) : pad;
686
        v2 = destlen ? cpu_ldub_data(env, dest) : pad;
687
        if (v1 != v2) {
688
            cc = (v1 < v2) ? 1 : 2;
689
            break;
690
        }
691
    }
692

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

    
699
    return cc;
700
}
701

    
702
/* checksum */
703
uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
704
                      uint64_t src, uint64_t src_len)
705
{
706
    uint64_t max_len, len;
707
    uint64_t cksm = (uint32_t)r1;
708

    
709
    /* Lest we fail to service interrupts in a timely manner, limit the
710
       amount of work we're willing to do.  For now, let's cap at 8k.  */
711
    max_len = (src_len > 0x2000 ? 0x2000 : src_len);
712

    
713
    /* Process full words as available.  */
714
    for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
715
        cksm += (uint32_t)cpu_ldl_data(env, src);
716
    }
717

    
718
    switch (max_len - len) {
719
    case 1:
720
        cksm += cpu_ldub_data(env, src) << 24;
721
        len += 1;
722
        break;
723
    case 2:
724
        cksm += cpu_lduw_data(env, src) << 16;
725
        len += 2;
726
        break;
727
    case 3:
728
        cksm += cpu_lduw_data(env, src) << 16;
729
        cksm += cpu_ldub_data(env, src + 2) << 8;
730
        len += 3;
731
        break;
732
    }
733

    
734
    /* Fold the carry from the checksum.  Note that we can see carry-out
735
       during folding more than once (but probably not more than twice).  */
736
    while (cksm > 0xffffffffull) {
737
        cksm = (uint32_t)cksm + (cksm >> 32);
738
    }
739

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

    
743
    /* Return both cksm and processed length.  */
744
    env->retxl = cksm;
745
    return len;
746
}
747

    
748
void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
749
                  uint64_t src)
750
{
751
    int len_dest = len >> 4;
752
    int len_src = len & 0xf;
753
    uint8_t b;
754
    int second_nibble = 0;
755

    
756
    dest += len_dest;
757
    src += len_src;
758

    
759
    /* last byte is special, it only flips the nibbles */
760
    b = cpu_ldub_data(env, src);
761
    cpu_stb_data(env, dest, (b << 4) | (b >> 4));
762
    src--;
763
    len_src--;
764

    
765
    /* now pad every nibble with 0xf0 */
766

    
767
    while (len_dest > 0) {
768
        uint8_t cur_byte = 0;
769

    
770
        if (len_src > 0) {
771
            cur_byte = cpu_ldub_data(env, src);
772
        }
773

    
774
        len_dest--;
775
        dest--;
776

    
777
        /* only advance one nibble at a time */
778
        if (second_nibble) {
779
            cur_byte >>= 4;
780
            len_src--;
781
            src--;
782
        }
783
        second_nibble = !second_nibble;
784

    
785
        /* digit */
786
        cur_byte = (cur_byte & 0xf);
787
        /* zone bits */
788
        cur_byte |= 0xf0;
789

    
790
        cpu_stb_data(env, dest, cur_byte);
791
    }
792
}
793

    
794
void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
795
                uint64_t trans)
796
{
797
    int i;
798

    
799
    for (i = 0; i <= len; i++) {
800
        uint8_t byte = cpu_ldub_data(env, array + i);
801
        uint8_t new_byte = cpu_ldub_data(env, trans + byte);
802

    
803
        cpu_stb_data(env, array + i, new_byte);
804
    }
805
}
806

    
807
#if !defined(CONFIG_USER_ONLY)
808
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
809
{
810
    int i;
811
    uint64_t src = a2;
812

    
813
    for (i = r1;; i = (i + 1) % 16) {
814
        env->cregs[i] = cpu_ldq_data(env, src);
815
        HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
816
                   i, src, env->cregs[i]);
817
        src += sizeof(uint64_t);
818

    
819
        if (i == r3) {
820
            break;
821
        }
822
    }
823

    
824
    tlb_flush(env, 1);
825
}
826

    
827
void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
828
{
829
    int i;
830
    uint64_t src = a2;
831

    
832
    for (i = r1;; i = (i + 1) % 16) {
833
        env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
834
            cpu_ldl_data(env, src);
835
        src += sizeof(uint32_t);
836

    
837
        if (i == r3) {
838
            break;
839
        }
840
    }
841

    
842
    tlb_flush(env, 1);
843
}
844

    
845
void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
846
{
847
    int i;
848
    uint64_t dest = a2;
849

    
850
    for (i = r1;; i = (i + 1) % 16) {
851
        cpu_stq_data(env, dest, env->cregs[i]);
852
        dest += sizeof(uint64_t);
853

    
854
        if (i == r3) {
855
            break;
856
        }
857
    }
858
}
859

    
860
void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
861
{
862
    int i;
863
    uint64_t dest = a2;
864

    
865
    for (i = r1;; i = (i + 1) % 16) {
866
        cpu_stl_data(env, dest, env->cregs[i]);
867
        dest += sizeof(uint32_t);
868

    
869
        if (i == r3) {
870
            break;
871
        }
872
    }
873
}
874

    
875
uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
876
{
877
    /* XXX implement */
878

    
879
    return 0;
880
}
881

    
882
/* insert storage key extended */
883
uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
884
{
885
    uint64_t addr = get_address(env, 0, 0, r2);
886

    
887
    if (addr > ram_size) {
888
        return 0;
889
    }
890

    
891
    return env->storage_keys[addr / TARGET_PAGE_SIZE];
892
}
893

    
894
/* set storage key extended */
895
void HELPER(sske)(CPUS390XState *env, uint64_t r1, uint64_t r2)
896
{
897
    uint64_t addr = get_address(env, 0, 0, r2);
898

    
899
    if (addr > ram_size) {
900
        return;
901
    }
902

    
903
    env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
904
}
905

    
906
/* reset reference bit extended */
907
uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
908
{
909
    uint8_t re;
910
    uint8_t key;
911

    
912
    if (r2 > ram_size) {
913
        return 0;
914
    }
915

    
916
    key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
917
    re = key & (SK_R | SK_C);
918
    env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
919

    
920
    /*
921
     * cc
922
     *
923
     * 0  Reference bit zero; change bit zero
924
     * 1  Reference bit zero; change bit one
925
     * 2  Reference bit one; change bit zero
926
     * 3  Reference bit one; change bit one
927
     */
928

    
929
    return re >> 1;
930
}
931

    
932
/* compare and swap and purge */
933
uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
934
{
935
    uint32_t cc;
936
    uint32_t o1 = env->regs[r1];
937
    uint64_t a2 = r2 & ~3ULL;
938
    uint32_t o2 = cpu_ldl_data(env, a2);
939

    
940
    if (o1 == o2) {
941
        cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
942
        if (r2 & 0x3) {
943
            /* flush TLB / ALB */
944
            tlb_flush(env, 1);
945
        }
946
        cc = 0;
947
    } else {
948
        env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
949
        cc = 1;
950
    }
951

    
952
    return cc;
953
}
954

    
955
static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
956
                        uint64_t mode1, uint64_t a2, uint64_t mode2)
957
{
958
    target_ulong src, dest;
959
    int flags, cc = 0, i;
960

    
961
    if (!l) {
962
        return 0;
963
    } else if (l > 256) {
964
        /* max 256 */
965
        l = 256;
966
        cc = 3;
967
    }
968

    
969
    if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
970
        cpu_loop_exit(env);
971
    }
972
    dest |= a1 & ~TARGET_PAGE_MASK;
973

    
974
    if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
975
        cpu_loop_exit(env);
976
    }
977
    src |= a2 & ~TARGET_PAGE_MASK;
978

    
979
    /* XXX replace w/ memcpy */
980
    for (i = 0; i < l; i++) {
981
        /* XXX be more clever */
982
        if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
983
            (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
984
            mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
985
            break;
986
        }
987
        stb_phys(dest + i, ldub_phys(src + i));
988
    }
989

    
990
    return cc;
991
}
992

    
993
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
994
{
995
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
996
               __func__, l, a1, a2);
997

    
998
    return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
999
}
1000

    
1001
uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1002
{
1003
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1004
               __func__, l, a1, a2);
1005

    
1006
    return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
1007
}
1008

    
1009
/* invalidate pte */
1010
void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1011
{
1012
    uint64_t page = vaddr & TARGET_PAGE_MASK;
1013
    uint64_t pte = 0;
1014

    
1015
    /* XXX broadcast to other CPUs */
1016

    
1017
    /* XXX Linux is nice enough to give us the exact pte address.
1018
       According to spec we'd have to find it out ourselves */
1019
    /* XXX Linux is fine with overwriting the pte, the spec requires
1020
       us to only set the invalid bit */
1021
    stq_phys(pte_addr, pte | _PAGE_INVALID);
1022

    
1023
    /* XXX we exploit the fact that Linux passes the exact virtual
1024
       address here - it's not obliged to! */
1025
    tlb_flush_page(env, page);
1026

    
1027
    /* XXX 31-bit hack */
1028
    if (page & 0x80000000) {
1029
        tlb_flush_page(env, page & ~0x80000000);
1030
    } else {
1031
        tlb_flush_page(env, page | 0x80000000);
1032
    }
1033
}
1034

    
1035
/* flush local tlb */
1036
void HELPER(ptlb)(CPUS390XState *env)
1037
{
1038
    tlb_flush(env, 1);
1039
}
1040

    
1041
/* store using real address */
1042
void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint64_t v1)
1043
{
1044
    stw_phys(get_address(env, 0, 0, addr), (uint32_t)v1);
1045
}
1046

    
1047
/* load real address */
1048
uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1049
{
1050
    uint32_t cc = 0;
1051
    int old_exc = env->exception_index;
1052
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1053
    uint64_t ret;
1054
    int flags;
1055

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

    
1061
    env->exception_index = old_exc;
1062
    if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1063
        cc = 3;
1064
    }
1065
    if (env->exception_index == EXCP_PGM) {
1066
        ret = env->int_pgm_code | 0x80000000;
1067
    } else {
1068
        ret |= addr & ~TARGET_PAGE_MASK;
1069
    }
1070
    env->exception_index = old_exc;
1071

    
1072
    env->cc_op = cc;
1073
    return ret;
1074
}
1075
#endif