Statistics
| Branch: | Revision:

root / target-s390x / mem_helper.c @ 374724f9

History | View | Annotate | Download (27.9 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 get_address(CPUS390XState *env, int x2, int b2, int d2)
308
{
309
    uint64_t r = d2;
310

    
311
    if (x2) {
312
        r += env->regs[x2];
313
    }
314

    
315
    if (b2) {
316
        r += env->regs[b2];
317
    }
318

    
319
    /* 31-Bit mode */
320
    if (!(env->psw.mask & PSW_MASK_64)) {
321
        r &= 0x7fffffff;
322
    }
323

    
324
    return r;
325
}
326

    
327
static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
328
{
329
    uint64_t r = env->regs[reg];
330

    
331
    /* 31-Bit mode */
332
    if (!(env->psw.mask & PSW_MASK_64)) {
333
        r &= 0x7fffffff;
334
    }
335

    
336
    return r;
337
}
338

    
339
/* search string (c is byte to search, r2 is string, r1 end of string) */
340
uint32_t HELPER(srst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
341
{
342
    uint64_t i;
343
    uint32_t cc = 2;
344
    uint64_t str = get_address_31fix(env, r2);
345
    uint64_t end = get_address_31fix(env, r1);
346

    
347
    HELPER_LOG("%s: c %d *r1 0x%" PRIx64 " *r2 0x%" PRIx64 "\n", __func__,
348
               c, env->regs[r1], env->regs[r2]);
349

    
350
    for (i = str; i != end; i++) {
351
        if (cpu_ldub_data(env, i) == c) {
352
            env->regs[r1] = i;
353
            cc = 1;
354
            break;
355
        }
356
    }
357

    
358
    return cc;
359
}
360

    
361
/* unsigned string compare (c is string terminator) */
362
uint32_t HELPER(clst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
363
{
364
    uint64_t s1 = get_address_31fix(env, r1);
365
    uint64_t s2 = get_address_31fix(env, r2);
366
    uint8_t v1, v2;
367
    uint32_t cc;
368

    
369
    c = c & 0xff;
370
#ifdef CONFIG_USER_ONLY
371
    if (!c) {
372
        HELPER_LOG("%s: comparing '%s' and '%s'\n",
373
                   __func__, (char *)g2h(s1), (char *)g2h(s2));
374
    }
375
#endif
376
    for (;;) {
377
        v1 = cpu_ldub_data(env, s1);
378
        v2 = cpu_ldub_data(env, s2);
379
        if ((v1 == c || v2 == c) || (v1 != v2)) {
380
            break;
381
        }
382
        s1++;
383
        s2++;
384
    }
385

    
386
    if (v1 == v2) {
387
        cc = 0;
388
    } else {
389
        cc = (v1 < v2) ? 1 : 2;
390
        /* FIXME: 31-bit mode! */
391
        env->regs[r1] = s1;
392
        env->regs[r2] = s2;
393
    }
394
    return cc;
395
}
396

    
397
/* move page */
398
void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
399
{
400
    /* XXX missing r0 handling */
401
#ifdef CONFIG_USER_ONLY
402
    int i;
403

    
404
    for (i = 0; i < TARGET_PAGE_SIZE; i++) {
405
        cpu_stb_data(env, r1 + i, cpu_ldub_data(env, r2 + i));
406
    }
407
#else
408
    mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
409
#endif
410
}
411

    
412
/* string copy (c is string terminator) */
413
void HELPER(mvst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
414
{
415
    uint64_t dest = get_address_31fix(env, r1);
416
    uint64_t src = get_address_31fix(env, r2);
417
    uint8_t v;
418

    
419
    c = c & 0xff;
420
#ifdef CONFIG_USER_ONLY
421
    if (!c) {
422
        HELPER_LOG("%s: copy '%s' to 0x%lx\n", __func__, (char *)g2h(src),
423
                   dest);
424
    }
425
#endif
426
    for (;;) {
427
        v = cpu_ldub_data(env, src);
428
        cpu_stb_data(env, dest, v);
429
        if (v == c) {
430
            break;
431
        }
432
        src++;
433
        dest++;
434
    }
435
    env->regs[r1] = dest; /* FIXME: 31-bit mode! */
436
}
437

    
438
/* compare and swap 64-bit */
439
uint64_t HELPER(csg)(CPUS390XState *env, uint64_t r1, uint64_t a2, uint64_t r3)
440
{
441
    /* FIXME: locking? */
442
    uint64_t v2 = cpu_ldq_data(env, a2);
443
    if (r1 == v2) {
444
        cpu_stq_data(env, a2, r3);
445
        env->cc_op = 0;
446
        return r1;
447
    } else {
448
        env->cc_op = 1;
449
        return v2;
450
    }
451
}
452

    
453
/* compare double and swap 64-bit */
454
uint32_t HELPER(cdsg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
455
{
456
    /* FIXME: locking? */
457
    uint32_t cc;
458
    uint64_t v2_hi = cpu_ldq_data(env, a2);
459
    uint64_t v2_lo = cpu_ldq_data(env, a2 + 8);
460
    uint64_t v1_hi = env->regs[r1];
461
    uint64_t v1_lo = env->regs[r1 + 1];
462

    
463
    if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) {
464
        cc = 0;
465
        cpu_stq_data(env, a2, env->regs[r3]);
466
        cpu_stq_data(env, a2 + 8, env->regs[r3 + 1]);
467
    } else {
468
        cc = 1;
469
        env->regs[r1] = v2_hi;
470
        env->regs[r1 + 1] = v2_lo;
471
    }
472

    
473
    return cc;
474
}
475

    
476
/* compare and swap 32-bit */
477
uint64_t HELPER(cs)(CPUS390XState *env, uint64_t r1, uint64_t a2, uint64_t r3)
478
{
479
    /* FIXME: locking? */
480
    uint32_t v2 = cpu_ldl_data(env, a2);
481
    if ((uint32_t)r1 == v2) {
482
        cpu_stl_data(env, a2, (uint32_t)r3);
483
        env->cc_op = 0;
484
        return r1;
485
    } else {
486
        env->cc_op = 1;
487
        return v2;
488
    }
489
}
490

    
491
static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
492
                           uint32_t mask)
493
{
494
    int pos = 24; /* top of the lower half of r1 */
495
    uint64_t rmask = 0xff000000ULL;
496
    uint8_t val = 0;
497
    int ccd = 0;
498
    uint32_t cc = 0;
499

    
500
    while (mask) {
501
        if (mask & 8) {
502
            env->regs[r1] &= ~rmask;
503
            val = cpu_ldub_data(env, address);
504
            if ((val & 0x80) && !ccd) {
505
                cc = 1;
506
            }
507
            ccd = 1;
508
            if (val && cc == 0) {
509
                cc = 2;
510
            }
511
            env->regs[r1] |= (uint64_t)val << pos;
512
            address++;
513
        }
514
        mask = (mask << 1) & 0xf;
515
        pos -= 8;
516
        rmask >>= 8;
517
    }
518

    
519
    return cc;
520
}
521

    
522
/* execute instruction
523
   this instruction executes an insn modified with the contents of r1
524
   it does not change the executed instruction in memory
525
   it does not change the program counter
526
   in other words: tricky...
527
   currently implemented by interpreting the cases it is most commonly used in
528
*/
529
uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
530
                    uint64_t addr, uint64_t ret)
531
{
532
    uint16_t insn = cpu_lduw_code(env, addr);
533

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

    
539
        l = v1 & 0xff;
540
        insn2 = cpu_ldl_code(env, addr + 2);
541
        b1 = (insn2 >> 28) & 0xf;
542
        b2 = (insn2 >> 12) & 0xf;
543
        d1 = (insn2 >> 16) & 0xfff;
544
        d2 = insn2 & 0xfff;
545
        switch (insn & 0xf00) {
546
        case 0x200:
547
            helper_mvc(env, l, get_address(env, 0, b1, d1),
548
                       get_address(env, 0, b2, d2));
549
            break;
550
        case 0x500:
551
            cc = helper_clc(env, l, get_address(env, 0, b1, d1),
552
                            get_address(env, 0, b2, d2));
553
            break;
554
        case 0x700:
555
            cc = helper_xc(env, l, get_address(env, 0, b1, d1),
556
                           get_address(env, 0, b2, d2));
557
            break;
558
        case 0xc00:
559
            helper_tr(env, l, get_address(env, 0, b1, d1),
560
                      get_address(env, 0, b2, d2));
561
            break;
562
        default:
563
            goto abort;
564
            break;
565
        }
566
    } else if ((insn & 0xff00) == 0x0a00) {
567
        /* supervisor call */
568
        HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
569
        env->psw.addr = ret - 4;
570
        env->int_svc_code = (insn | v1) & 0xff;
571
        env->int_svc_ilen = 4;
572
        helper_exception(env, EXCP_SVC);
573
    } else if ((insn & 0xff00) == 0xbf00) {
574
        uint32_t insn2, r1, r3, b2, d2;
575

    
576
        insn2 = cpu_ldl_code(env, addr + 2);
577
        r1 = (insn2 >> 20) & 0xf;
578
        r3 = (insn2 >> 16) & 0xf;
579
        b2 = (insn2 >> 12) & 0xf;
580
        d2 = insn2 & 0xfff;
581
        cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
582
    } else {
583
    abort:
584
        cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
585
                  insn);
586
    }
587
    return cc;
588
}
589

    
590
/* load access registers r1 to r3 from memory at a2 */
591
void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
592
{
593
    int i;
594

    
595
    for (i = r1;; i = (i + 1) % 16) {
596
        env->aregs[i] = cpu_ldl_data(env, a2);
597
        a2 += 4;
598

    
599
        if (i == r3) {
600
            break;
601
        }
602
    }
603
}
604

    
605
/* store access registers r1 to r3 in memory at a2 */
606
void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
607
{
608
    int i;
609

    
610
    for (i = r1;; i = (i + 1) % 16) {
611
        cpu_stl_data(env, a2, env->aregs[i]);
612
        a2 += 4;
613

    
614
        if (i == r3) {
615
            break;
616
        }
617
    }
618
}
619

    
620
/* move long */
621
uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
622
{
623
    uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
624
    uint64_t dest = get_address_31fix(env, r1);
625
    uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
626
    uint64_t src = get_address_31fix(env, r2);
627
    uint8_t pad = src >> 24;
628
    uint8_t v;
629
    uint32_t cc;
630

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

    
639
    if (srclen > destlen) {
640
        srclen = destlen;
641
    }
642

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

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

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

    
658
    return cc;
659
}
660

    
661
/* move long extended another memcopy insn with more bells and whistles */
662
uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
663
                       uint32_t r3)
664
{
665
    uint64_t destlen = env->regs[r1 + 1];
666
    uint64_t dest = env->regs[r1];
667
    uint64_t srclen = env->regs[r3 + 1];
668
    uint64_t src = env->regs[r3];
669
    uint8_t pad = a2 & 0xff;
670
    uint8_t v;
671
    uint32_t cc;
672

    
673
    if (!(env->psw.mask & PSW_MASK_64)) {
674
        destlen = (uint32_t)destlen;
675
        srclen = (uint32_t)srclen;
676
        dest &= 0x7fffffff;
677
        src &= 0x7fffffff;
678
    }
679

    
680
    if (destlen == srclen) {
681
        cc = 0;
682
    } else if (destlen < srclen) {
683
        cc = 1;
684
    } else {
685
        cc = 2;
686
    }
687

    
688
    if (srclen > destlen) {
689
        srclen = destlen;
690
    }
691

    
692
    for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
693
        v = cpu_ldub_data(env, src);
694
        cpu_stb_data(env, dest, v);
695
    }
696

    
697
    for (; destlen; dest++, destlen--) {
698
        cpu_stb_data(env, dest, pad);
699
    }
700

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

    
708
    return cc;
709
}
710

    
711
/* compare logical long extended memcompare insn with padding */
712
uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
713
                       uint32_t r3)
714
{
715
    uint64_t destlen = env->regs[r1 + 1];
716
    uint64_t dest = get_address_31fix(env, r1);
717
    uint64_t srclen = env->regs[r3 + 1];
718
    uint64_t src = get_address_31fix(env, r3);
719
    uint8_t pad = a2 & 0xff;
720
    uint8_t v1 = 0, v2 = 0;
721
    uint32_t cc = 0;
722

    
723
    if (!(destlen || srclen)) {
724
        return cc;
725
    }
726

    
727
    if (srclen > destlen) {
728
        srclen = destlen;
729
    }
730

    
731
    for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
732
        v1 = srclen ? cpu_ldub_data(env, src) : pad;
733
        v2 = destlen ? cpu_ldub_data(env, dest) : pad;
734
        if (v1 != v2) {
735
            cc = (v1 < v2) ? 1 : 2;
736
            break;
737
        }
738
    }
739

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

    
746
    return cc;
747
}
748

    
749
/* checksum */
750
uint64_t HELPER(cksm)(CPUS390XState *env, uint64_t r1,
751
                      uint64_t src, uint64_t src_len)
752
{
753
    uint64_t max_len, len;
754
    uint64_t cksm = (uint32_t)r1;
755

    
756
    /* Lest we fail to service interrupts in a timely manner, limit the
757
       amount of work we're willing to do.  For now, lets cap at 8k.  */
758
    max_len = (src_len > 0x2000 ? 0x2000 : src_len);
759

    
760
    /* Process full words as available.  */
761
    for (len = 0; len + 4 <= max_len; len += 4, src += 4) {
762
        cksm += (uint32_t)cpu_ldl_data(env, src);
763
    }
764

    
765
    switch (max_len - len) {
766
    case 1:
767
        cksm += cpu_ldub_data(env, src) << 24;
768
        len += 1;
769
        break;
770
    case 2:
771
        cksm += cpu_lduw_data(env, src) << 16;
772
        len += 2;
773
        break;
774
    case 3:
775
        cksm += cpu_lduw_data(env, src) << 16;
776
        cksm += cpu_ldub_data(env, src + 2) << 8;
777
        len += 3;
778
        break;
779
    }
780

    
781
    /* Fold the carry from the checksum.  Note that we can see carry-out
782
       during folding more than once (but probably not more than twice).  */
783
    while (cksm > 0xffffffffull) {
784
        cksm = (uint32_t)cksm + (cksm >> 32);
785
    }
786

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

    
790
    /* Return both cksm and processed length.  */
791
    env->retxl = cksm;
792
    return len;
793
}
794

    
795
void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
796
                  uint64_t src)
797
{
798
    int len_dest = len >> 4;
799
    int len_src = len & 0xf;
800
    uint8_t b;
801
    int second_nibble = 0;
802

    
803
    dest += len_dest;
804
    src += len_src;
805

    
806
    /* last byte is special, it only flips the nibbles */
807
    b = cpu_ldub_data(env, src);
808
    cpu_stb_data(env, dest, (b << 4) | (b >> 4));
809
    src--;
810
    len_src--;
811

    
812
    /* now pad every nibble with 0xf0 */
813

    
814
    while (len_dest > 0) {
815
        uint8_t cur_byte = 0;
816

    
817
        if (len_src > 0) {
818
            cur_byte = cpu_ldub_data(env, src);
819
        }
820

    
821
        len_dest--;
822
        dest--;
823

    
824
        /* only advance one nibble at a time */
825
        if (second_nibble) {
826
            cur_byte >>= 4;
827
            len_src--;
828
            src--;
829
        }
830
        second_nibble = !second_nibble;
831

    
832
        /* digit */
833
        cur_byte = (cur_byte & 0xf);
834
        /* zone bits */
835
        cur_byte |= 0xf0;
836

    
837
        cpu_stb_data(env, dest, cur_byte);
838
    }
839
}
840

    
841
void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
842
                uint64_t trans)
843
{
844
    int i;
845

    
846
    for (i = 0; i <= len; i++) {
847
        uint8_t byte = cpu_ldub_data(env, array + i);
848
        uint8_t new_byte = cpu_ldub_data(env, trans + byte);
849

    
850
        cpu_stb_data(env, array + i, new_byte);
851
    }
852
}
853

    
854
#if !defined(CONFIG_USER_ONLY)
855
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
856
{
857
    int i;
858
    uint64_t src = a2;
859

    
860
    for (i = r1;; i = (i + 1) % 16) {
861
        env->cregs[i] = cpu_ldq_data(env, src);
862
        HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
863
                   i, src, env->cregs[i]);
864
        src += sizeof(uint64_t);
865

    
866
        if (i == r3) {
867
            break;
868
        }
869
    }
870

    
871
    tlb_flush(env, 1);
872
}
873

    
874
void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
875
{
876
    int i;
877
    uint64_t src = a2;
878

    
879
    for (i = r1;; i = (i + 1) % 16) {
880
        env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
881
            cpu_ldl_data(env, src);
882
        src += sizeof(uint32_t);
883

    
884
        if (i == r3) {
885
            break;
886
        }
887
    }
888

    
889
    tlb_flush(env, 1);
890
}
891

    
892
void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
893
{
894
    int i;
895
    uint64_t dest = a2;
896

    
897
    for (i = r1;; i = (i + 1) % 16) {
898
        cpu_stq_data(env, dest, env->cregs[i]);
899
        dest += sizeof(uint64_t);
900

    
901
        if (i == r3) {
902
            break;
903
        }
904
    }
905
}
906

    
907
void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
908
{
909
    int i;
910
    uint64_t dest = a2;
911

    
912
    for (i = r1;; i = (i + 1) % 16) {
913
        cpu_stl_data(env, dest, env->cregs[i]);
914
        dest += sizeof(uint32_t);
915

    
916
        if (i == r3) {
917
            break;
918
        }
919
    }
920
}
921

    
922
uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
923
{
924
    /* XXX implement */
925

    
926
    return 0;
927
}
928

    
929
/* insert storage key extended */
930
uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
931
{
932
    uint64_t addr = get_address(env, 0, 0, r2);
933

    
934
    if (addr > ram_size) {
935
        return 0;
936
    }
937

    
938
    return env->storage_keys[addr / TARGET_PAGE_SIZE];
939
}
940

    
941
/* set storage key extended */
942
void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2)
943
{
944
    uint64_t addr = get_address(env, 0, 0, r2);
945

    
946
    if (addr > ram_size) {
947
        return;
948
    }
949

    
950
    env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
951
}
952

    
953
/* reset reference bit extended */
954
uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2)
955
{
956
    uint8_t re;
957
    uint8_t key;
958

    
959
    if (r2 > ram_size) {
960
        return 0;
961
    }
962

    
963
    key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
964
    re = key & (SK_R | SK_C);
965
    env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
966

    
967
    /*
968
     * cc
969
     *
970
     * 0  Reference bit zero; change bit zero
971
     * 1  Reference bit zero; change bit one
972
     * 2  Reference bit one; change bit zero
973
     * 3  Reference bit one; change bit one
974
     */
975

    
976
    return re >> 1;
977
}
978

    
979
/* compare and swap and purge */
980
uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint32_t r2)
981
{
982
    uint32_t cc;
983
    uint32_t o1 = env->regs[r1];
984
    uint64_t a2 = get_address_31fix(env, r2) & ~3ULL;
985
    uint32_t o2 = cpu_ldl_data(env, a2);
986

    
987
    if (o1 == o2) {
988
        cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
989
        if (env->regs[r2] & 0x3) {
990
            /* flush TLB / ALB */
991
            tlb_flush(env, 1);
992
        }
993
        cc = 0;
994
    } else {
995
        env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
996
        cc = 1;
997
    }
998

    
999
    return cc;
1000
}
1001

    
1002
static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
1003
                        uint64_t mode1, uint64_t a2, uint64_t mode2)
1004
{
1005
    target_ulong src, dest;
1006
    int flags, cc = 0, i;
1007

    
1008
    if (!l) {
1009
        return 0;
1010
    } else if (l > 256) {
1011
        /* max 256 */
1012
        l = 256;
1013
        cc = 3;
1014
    }
1015

    
1016
    if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
1017
        cpu_loop_exit(env);
1018
    }
1019
    dest |= a1 & ~TARGET_PAGE_MASK;
1020

    
1021
    if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
1022
        cpu_loop_exit(env);
1023
    }
1024
    src |= a2 & ~TARGET_PAGE_MASK;
1025

    
1026
    /* XXX replace w/ memcpy */
1027
    for (i = 0; i < l; i++) {
1028
        /* XXX be more clever */
1029
        if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
1030
            (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
1031
            mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
1032
            break;
1033
        }
1034
        stb_phys(dest + i, ldub_phys(src + i));
1035
    }
1036

    
1037
    return cc;
1038
}
1039

    
1040
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1041
{
1042
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1043
               __func__, l, a1, a2);
1044

    
1045
    return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
1046
}
1047

    
1048
uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1049
{
1050
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1051
               __func__, l, a1, a2);
1052

    
1053
    return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
1054
}
1055

    
1056
/* invalidate pte */
1057
void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1058
{
1059
    uint64_t page = vaddr & TARGET_PAGE_MASK;
1060
    uint64_t pte = 0;
1061

    
1062
    /* XXX broadcast to other CPUs */
1063

    
1064
    /* XXX Linux is nice enough to give us the exact pte address.
1065
       According to spec we'd have to find it out ourselves */
1066
    /* XXX Linux is fine with overwriting the pte, the spec requires
1067
       us to only set the invalid bit */
1068
    stq_phys(pte_addr, pte | _PAGE_INVALID);
1069

    
1070
    /* XXX we exploit the fact that Linux passes the exact virtual
1071
       address here - it's not obliged to! */
1072
    tlb_flush_page(env, page);
1073

    
1074
    /* XXX 31-bit hack */
1075
    if (page & 0x80000000) {
1076
        tlb_flush_page(env, page & ~0x80000000);
1077
    } else {
1078
        tlb_flush_page(env, page | 0x80000000);
1079
    }
1080
}
1081

    
1082
/* flush local tlb */
1083
void HELPER(ptlb)(CPUS390XState *env)
1084
{
1085
    tlb_flush(env, 1);
1086
}
1087

    
1088
/* store using real address */
1089
void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint32_t v1)
1090
{
1091
    stw_phys(get_address(env, 0, 0, addr), v1);
1092
}
1093

    
1094
/* load real address */
1095
uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr)
1096
{
1097
    uint32_t cc = 0;
1098
    int old_exc = env->exception_index;
1099
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1100
    uint64_t ret;
1101
    int flags;
1102

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

    
1108
    env->exception_index = old_exc;
1109
    if (mmu_translate(env, addr, 0, asc, &ret, &flags)) {
1110
        cc = 3;
1111
    }
1112
    if (env->exception_index == EXCP_PGM) {
1113
        ret = env->int_pgm_code | 0x80000000;
1114
    } else {
1115
        ret |= addr & ~TARGET_PAGE_MASK;
1116
    }
1117
    env->exception_index = old_exc;
1118

    
1119
    env->cc_op = cc;
1120
    return ret;
1121
}
1122
#endif