Statistics
| Branch: | Revision:

root / target-s390x / mem_helper.c @ a8170e5e

History | View | Annotate | Download (29.8 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 "softmmu_exec.h"
28

    
29
#define MMUSUFFIX _mmu
30

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

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

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

    
40
#define SHIFT 3
41
#include "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
    TranslationBlock *tb;
51
    int ret;
52

    
53
    ret = cpu_s390x_handle_mmu_fault(env, addr, is_write, mmu_idx);
54
    if (unlikely(ret != 0)) {
55
        if (likely(retaddr)) {
56
            /* now we have a real cpu fault */
57
            tb = tb_find_pc(retaddr);
58
            if (likely(tb)) {
59
                /* the PC is inside the translated code. It means that we have
60
                   a virtual CPU fault */
61
                cpu_restore_state(tb, env, retaddr);
62
            }
63
        }
64
        cpu_loop_exit(env);
65
    }
66
}
67

    
68
#endif
69

    
70
/* #define DEBUG_HELPER */
71
#ifdef DEBUG_HELPER
72
#define HELPER_LOG(x...) qemu_log(x)
73
#else
74
#define HELPER_LOG(x...)
75
#endif
76

    
77
#ifndef CONFIG_USER_ONLY
78
static void mvc_fast_memset(CPUS390XState *env, uint32_t l, uint64_t dest,
79
                            uint8_t byte)
80
{
81
    hwaddr dest_phys;
82
    hwaddr len = l;
83
    void *dest_p;
84
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
85
    int flags;
86

    
87
    if (mmu_translate(env, dest, 1, asc, &dest_phys, &flags)) {
88
        cpu_stb_data(env, dest, byte);
89
        cpu_abort(env, "should never reach here");
90
    }
91
    dest_phys |= dest & ~TARGET_PAGE_MASK;
92

    
93
    dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
94

    
95
    memset(dest_p, byte, len);
96

    
97
    cpu_physical_memory_unmap(dest_p, 1, len, len);
98
}
99

    
100
static void mvc_fast_memmove(CPUS390XState *env, uint32_t l, uint64_t dest,
101
                             uint64_t src)
102
{
103
    hwaddr dest_phys;
104
    hwaddr src_phys;
105
    hwaddr len = l;
106
    void *dest_p;
107
    void *src_p;
108
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
109
    int flags;
110

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

    
117
    if (mmu_translate(env, src, 0, asc, &src_phys, &flags)) {
118
        cpu_ldub_data(env, src);
119
        cpu_abort(env, "should never reach here");
120
    }
121
    src_phys |= src & ~TARGET_PAGE_MASK;
122

    
123
    dest_p = cpu_physical_memory_map(dest_phys, &len, 1);
124
    src_p = cpu_physical_memory_map(src_phys, &len, 0);
125

    
126
    memmove(dest_p, src_p, len);
127

    
128
    cpu_physical_memory_unmap(dest_p, 1, len, len);
129
    cpu_physical_memory_unmap(src_p, 0, len, len);
130
}
131
#endif
132

    
133
/* and on array */
134
uint32_t HELPER(nc)(CPUS390XState *env, uint32_t l, uint64_t dest,
135
                    uint64_t src)
136
{
137
    int i;
138
    unsigned char x;
139
    uint32_t cc = 0;
140

    
141
    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
142
               __func__, l, dest, src);
143
    for (i = 0; i <= l; i++) {
144
        x = cpu_ldub_data(env, dest + i) & cpu_ldub_data(env, src + i);
145
        if (x) {
146
            cc = 1;
147
        }
148
        cpu_stb_data(env, dest + i, x);
149
    }
150
    return cc;
151
}
152

    
153
/* xor on array */
154
uint32_t HELPER(xc)(CPUS390XState *env, uint32_t l, uint64_t dest,
155
                    uint64_t src)
156
{
157
    int i;
158
    unsigned char x;
159
    uint32_t cc = 0;
160

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

    
164
#ifndef CONFIG_USER_ONLY
165
    /* xor with itself is the same as memset(0) */
166
    if ((l > 32) && (src == dest) &&
167
        (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK)) {
168
        mvc_fast_memset(env, l + 1, dest, 0);
169
        return 0;
170
    }
171
#else
172
    if (src == dest) {
173
        memset(g2h(dest), 0, l + 1);
174
        return 0;
175
    }
176
#endif
177

    
178
    for (i = 0; i <= l; i++) {
179
        x = cpu_ldub_data(env, dest + i) ^ cpu_ldub_data(env, src + i);
180
        if (x) {
181
            cc = 1;
182
        }
183
        cpu_stb_data(env, dest + i, x);
184
    }
185
    return cc;
186
}
187

    
188
/* or on array */
189
uint32_t HELPER(oc)(CPUS390XState *env, uint32_t l, uint64_t dest,
190
                    uint64_t src)
191
{
192
    int i;
193
    unsigned char x;
194
    uint32_t cc = 0;
195

    
196
    HELPER_LOG("%s l %d dest %" PRIx64 " src %" PRIx64 "\n",
197
               __func__, l, dest, src);
198
    for (i = 0; i <= l; i++) {
199
        x = cpu_ldub_data(env, dest + i) | cpu_ldub_data(env, src + i);
200
        if (x) {
201
            cc = 1;
202
        }
203
        cpu_stb_data(env, dest + i, x);
204
    }
205
    return cc;
206
}
207

    
208
/* memmove */
209
void HELPER(mvc)(CPUS390XState *env, uint32_t l, uint64_t dest, uint64_t src)
210
{
211
    int i = 0;
212
    int x = 0;
213
    uint32_t l_64 = (l + 1) / 8;
214

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

    
218
#ifndef CONFIG_USER_ONLY
219
    if ((l > 32) &&
220
        (src & TARGET_PAGE_MASK) == ((src + l) & TARGET_PAGE_MASK) &&
221
        (dest & TARGET_PAGE_MASK) == ((dest + l) & TARGET_PAGE_MASK)) {
222
        if (dest == (src + 1)) {
223
            mvc_fast_memset(env, l + 1, dest, cpu_ldub_data(env, src));
224
            return;
225
        } else if ((src & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) {
226
            mvc_fast_memmove(env, l + 1, dest, src);
227
            return;
228
        }
229
    }
230
#else
231
    if (dest == (src + 1)) {
232
        memset(g2h(dest), cpu_ldub_data(env, src), l + 1);
233
        return;
234
    } else {
235
        memmove(g2h(dest), g2h(src), l + 1);
236
        return;
237
    }
238
#endif
239

    
240
    /* handle the parts that fit into 8-byte loads/stores */
241
    if (dest != (src + 1)) {
242
        for (i = 0; i < l_64; i++) {
243
            cpu_stq_data(env, dest + x, cpu_ldq_data(env, src + x));
244
            x += 8;
245
        }
246
    }
247

    
248
    /* slow version crossing pages with byte accesses */
249
    for (i = x; i <= l; i++) {
250
        cpu_stb_data(env, dest + i, cpu_ldub_data(env, src + i));
251
    }
252
}
253

    
254
/* compare unsigned byte arrays */
255
uint32_t HELPER(clc)(CPUS390XState *env, uint32_t l, uint64_t s1, uint64_t s2)
256
{
257
    int i;
258
    unsigned char x, y;
259
    uint32_t cc;
260

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

    
281
/* compare logical under mask */
282
uint32_t HELPER(clm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
283
                     uint64_t addr)
284
{
285
    uint8_t r, d;
286
    uint32_t cc;
287

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

    
313
/* store character under mask */
314
void HELPER(stcm)(CPUS390XState *env, uint32_t r1, uint32_t mask,
315
                  uint64_t addr)
316
{
317
    uint8_t r;
318

    
319
    HELPER_LOG("%s: r1 0x%x mask 0x%x addr 0x%lx\n", __func__, r1, mask,
320
               addr);
321
    while (mask) {
322
        if (mask & 8) {
323
            r = (r1 & 0xff000000UL) >> 24;
324
            cpu_stb_data(env, addr, r);
325
            HELPER_LOG("mask 0x%x %02x (0x%lx) ", mask, r, addr);
326
            addr++;
327
        }
328
        mask = (mask << 1) & 0xf;
329
        r1 <<= 8;
330
    }
331
    HELPER_LOG("\n");
332
}
333

    
334
static inline uint64_t get_address(CPUS390XState *env, int x2, int b2, int d2)
335
{
336
    uint64_t r = d2;
337

    
338
    if (x2) {
339
        r += env->regs[x2];
340
    }
341

    
342
    if (b2) {
343
        r += env->regs[b2];
344
    }
345

    
346
    /* 31-Bit mode */
347
    if (!(env->psw.mask & PSW_MASK_64)) {
348
        r &= 0x7fffffff;
349
    }
350

    
351
    return r;
352
}
353

    
354
static inline uint64_t get_address_31fix(CPUS390XState *env, int reg)
355
{
356
    uint64_t r = env->regs[reg];
357

    
358
    /* 31-Bit mode */
359
    if (!(env->psw.mask & PSW_MASK_64)) {
360
        r &= 0x7fffffff;
361
    }
362

    
363
    return r;
364
}
365

    
366
/* search string (c is byte to search, r2 is string, r1 end of string) */
367
uint32_t HELPER(srst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
368
{
369
    uint64_t i;
370
    uint32_t cc = 2;
371
    uint64_t str = get_address_31fix(env, r2);
372
    uint64_t end = get_address_31fix(env, r1);
373

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

    
377
    for (i = str; i != end; i++) {
378
        if (cpu_ldub_data(env, i) == c) {
379
            env->regs[r1] = i;
380
            cc = 1;
381
            break;
382
        }
383
    }
384

    
385
    return cc;
386
}
387

    
388
/* unsigned string compare (c is string terminator) */
389
uint32_t HELPER(clst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
390
{
391
    uint64_t s1 = get_address_31fix(env, r1);
392
    uint64_t s2 = get_address_31fix(env, r2);
393
    uint8_t v1, v2;
394
    uint32_t cc;
395

    
396
    c = c & 0xff;
397
#ifdef CONFIG_USER_ONLY
398
    if (!c) {
399
        HELPER_LOG("%s: comparing '%s' and '%s'\n",
400
                   __func__, (char *)g2h(s1), (char *)g2h(s2));
401
    }
402
#endif
403
    for (;;) {
404
        v1 = cpu_ldub_data(env, s1);
405
        v2 = cpu_ldub_data(env, s2);
406
        if ((v1 == c || v2 == c) || (v1 != v2)) {
407
            break;
408
        }
409
        s1++;
410
        s2++;
411
    }
412

    
413
    if (v1 == v2) {
414
        cc = 0;
415
    } else {
416
        cc = (v1 < v2) ? 1 : 2;
417
        /* FIXME: 31-bit mode! */
418
        env->regs[r1] = s1;
419
        env->regs[r2] = s2;
420
    }
421
    return cc;
422
}
423

    
424
/* move page */
425
void HELPER(mvpg)(CPUS390XState *env, uint64_t r0, uint64_t r1, uint64_t r2)
426
{
427
    /* XXX missing r0 handling */
428
#ifdef CONFIG_USER_ONLY
429
    int i;
430

    
431
    for (i = 0; i < TARGET_PAGE_SIZE; i++) {
432
        cpu_stb_data(env, r1 + i, cpu_ldub_data(env, r2 + i));
433
    }
434
#else
435
    mvc_fast_memmove(env, TARGET_PAGE_SIZE, r1, r2);
436
#endif
437
}
438

    
439
/* string copy (c is string terminator) */
440
void HELPER(mvst)(CPUS390XState *env, uint32_t c, uint32_t r1, uint32_t r2)
441
{
442
    uint64_t dest = get_address_31fix(env, r1);
443
    uint64_t src = get_address_31fix(env, r2);
444
    uint8_t v;
445

    
446
    c = c & 0xff;
447
#ifdef CONFIG_USER_ONLY
448
    if (!c) {
449
        HELPER_LOG("%s: copy '%s' to 0x%lx\n", __func__, (char *)g2h(src),
450
                   dest);
451
    }
452
#endif
453
    for (;;) {
454
        v = cpu_ldub_data(env, src);
455
        cpu_stb_data(env, dest, v);
456
        if (v == c) {
457
            break;
458
        }
459
        src++;
460
        dest++;
461
    }
462
    env->regs[r1] = dest; /* FIXME: 31-bit mode! */
463
}
464

    
465
/* compare and swap 64-bit */
466
uint32_t HELPER(csg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
467
{
468
    /* FIXME: locking? */
469
    uint32_t cc;
470
    uint64_t v2 = cpu_ldq_data(env, a2);
471

    
472
    if (env->regs[r1] == v2) {
473
        cc = 0;
474
        cpu_stq_data(env, a2, env->regs[r3]);
475
    } else {
476
        cc = 1;
477
        env->regs[r1] = v2;
478
    }
479
    return cc;
480
}
481

    
482
/* compare double and swap 64-bit */
483
uint32_t HELPER(cdsg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
484
{
485
    /* FIXME: locking? */
486
    uint32_t cc;
487
    uint64_t v2_hi = cpu_ldq_data(env, a2);
488
    uint64_t v2_lo = cpu_ldq_data(env, a2 + 8);
489
    uint64_t v1_hi = env->regs[r1];
490
    uint64_t v1_lo = env->regs[r1 + 1];
491

    
492
    if ((v1_hi == v2_hi) && (v1_lo == v2_lo)) {
493
        cc = 0;
494
        cpu_stq_data(env, a2, env->regs[r3]);
495
        cpu_stq_data(env, a2 + 8, env->regs[r3 + 1]);
496
    } else {
497
        cc = 1;
498
        env->regs[r1] = v2_hi;
499
        env->regs[r1 + 1] = v2_lo;
500
    }
501

    
502
    return cc;
503
}
504

    
505
/* compare and swap 32-bit */
506
uint32_t HELPER(cs)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
507
{
508
    /* FIXME: locking? */
509
    uint32_t cc;
510
    uint32_t v2 = cpu_ldl_data(env, a2);
511

    
512
    HELPER_LOG("%s: r1 %d a2 0x%lx r3 %d\n", __func__, r1, a2, r3);
513
    if (((uint32_t)env->regs[r1]) == v2) {
514
        cc = 0;
515
        cpu_stl_data(env, a2, (uint32_t)env->regs[r3]);
516
    } else {
517
        cc = 1;
518
        env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | v2;
519
    }
520
    return cc;
521
}
522

    
523
static uint32_t helper_icm(CPUS390XState *env, uint32_t r1, uint64_t address,
524
                           uint32_t mask)
525
{
526
    int pos = 24; /* top of the lower half of r1 */
527
    uint64_t rmask = 0xff000000ULL;
528
    uint8_t val = 0;
529
    int ccd = 0;
530
    uint32_t cc = 0;
531

    
532
    while (mask) {
533
        if (mask & 8) {
534
            env->regs[r1] &= ~rmask;
535
            val = cpu_ldub_data(env, address);
536
            if ((val & 0x80) && !ccd) {
537
                cc = 1;
538
            }
539
            ccd = 1;
540
            if (val && cc == 0) {
541
                cc = 2;
542
            }
543
            env->regs[r1] |= (uint64_t)val << pos;
544
            address++;
545
        }
546
        mask = (mask << 1) & 0xf;
547
        pos -= 8;
548
        rmask >>= 8;
549
    }
550

    
551
    return cc;
552
}
553

    
554
/* execute instruction
555
   this instruction executes an insn modified with the contents of r1
556
   it does not change the executed instruction in memory
557
   it does not change the program counter
558
   in other words: tricky...
559
   currently implemented by interpreting the cases it is most commonly used in
560
*/
561
uint32_t HELPER(ex)(CPUS390XState *env, uint32_t cc, uint64_t v1,
562
                    uint64_t addr, uint64_t ret)
563
{
564
    uint16_t insn = cpu_lduw_code(env, addr);
565

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

    
571
        l = v1 & 0xff;
572
        insn2 = cpu_ldl_code(env, addr + 2);
573
        b1 = (insn2 >> 28) & 0xf;
574
        b2 = (insn2 >> 12) & 0xf;
575
        d1 = (insn2 >> 16) & 0xfff;
576
        d2 = insn2 & 0xfff;
577
        switch (insn & 0xf00) {
578
        case 0x200:
579
            helper_mvc(env, l, get_address(env, 0, b1, d1),
580
                       get_address(env, 0, b2, d2));
581
            break;
582
        case 0x500:
583
            cc = helper_clc(env, l, get_address(env, 0, b1, d1),
584
                            get_address(env, 0, b2, d2));
585
            break;
586
        case 0x700:
587
            cc = helper_xc(env, l, get_address(env, 0, b1, d1),
588
                           get_address(env, 0, b2, d2));
589
            break;
590
        case 0xc00:
591
            helper_tr(env, l, get_address(env, 0, b1, d1),
592
                      get_address(env, 0, b2, d2));
593
            break;
594
        default:
595
            goto abort;
596
            break;
597
        }
598
    } else if ((insn & 0xff00) == 0x0a00) {
599
        /* supervisor call */
600
        HELPER_LOG("%s: svc %ld via execute\n", __func__, (insn | v1) & 0xff);
601
        env->psw.addr = ret - 4;
602
        env->int_svc_code = (insn | v1) & 0xff;
603
        env->int_svc_ilc = 4;
604
        helper_exception(env, EXCP_SVC);
605
    } else if ((insn & 0xff00) == 0xbf00) {
606
        uint32_t insn2, r1, r3, b2, d2;
607

    
608
        insn2 = cpu_ldl_code(env, addr + 2);
609
        r1 = (insn2 >> 20) & 0xf;
610
        r3 = (insn2 >> 16) & 0xf;
611
        b2 = (insn2 >> 12) & 0xf;
612
        d2 = insn2 & 0xfff;
613
        cc = helper_icm(env, r1, get_address(env, 0, b2, d2), r3);
614
    } else {
615
    abort:
616
        cpu_abort(env, "EXECUTE on instruction prefix 0x%x not implemented\n",
617
                  insn);
618
    }
619
    return cc;
620
}
621

    
622
/* store character under mask high operates on the upper half of r1 */
623
void HELPER(stcmh)(CPUS390XState *env, uint32_t r1, uint64_t address,
624
                   uint32_t mask)
625
{
626
    int pos = 56; /* top of the upper half of r1 */
627

    
628
    while (mask) {
629
        if (mask & 8) {
630
            cpu_stb_data(env, address, (env->regs[r1] >> pos) & 0xff);
631
            address++;
632
        }
633
        mask = (mask << 1) & 0xf;
634
        pos -= 8;
635
    }
636
}
637

    
638
/* insert character under mask high; same as icm, but operates on the
639
   upper half of r1 */
640
uint32_t HELPER(icmh)(CPUS390XState *env, uint32_t r1, uint64_t address,
641
                      uint32_t mask)
642
{
643
    int pos = 56; /* top of the upper half of r1 */
644
    uint64_t rmask = 0xff00000000000000ULL;
645
    uint8_t val = 0;
646
    int ccd = 0;
647
    uint32_t cc = 0;
648

    
649
    while (mask) {
650
        if (mask & 8) {
651
            env->regs[r1] &= ~rmask;
652
            val = cpu_ldub_data(env, address);
653
            if ((val & 0x80) && !ccd) {
654
                cc = 1;
655
            }
656
            ccd = 1;
657
            if (val && cc == 0) {
658
                cc = 2;
659
            }
660
            env->regs[r1] |= (uint64_t)val << pos;
661
            address++;
662
        }
663
        mask = (mask << 1) & 0xf;
664
        pos -= 8;
665
        rmask >>= 8;
666
    }
667

    
668
    return cc;
669
}
670

    
671
/* load access registers r1 to r3 from memory at a2 */
672
void HELPER(lam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
673
{
674
    int i;
675

    
676
    for (i = r1;; i = (i + 1) % 16) {
677
        env->aregs[i] = cpu_ldl_data(env, a2);
678
        a2 += 4;
679

    
680
        if (i == r3) {
681
            break;
682
        }
683
    }
684
}
685

    
686
/* store access registers r1 to r3 in memory at a2 */
687
void HELPER(stam)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
688
{
689
    int i;
690

    
691
    for (i = r1;; i = (i + 1) % 16) {
692
        cpu_stl_data(env, a2, env->aregs[i]);
693
        a2 += 4;
694

    
695
        if (i == r3) {
696
            break;
697
        }
698
    }
699
}
700

    
701
/* move long */
702
uint32_t HELPER(mvcl)(CPUS390XState *env, uint32_t r1, uint32_t r2)
703
{
704
    uint64_t destlen = env->regs[r1 + 1] & 0xffffff;
705
    uint64_t dest = get_address_31fix(env, r1);
706
    uint64_t srclen = env->regs[r2 + 1] & 0xffffff;
707
    uint64_t src = get_address_31fix(env, r2);
708
    uint8_t pad = src >> 24;
709
    uint8_t v;
710
    uint32_t cc;
711

    
712
    if (destlen == srclen) {
713
        cc = 0;
714
    } else if (destlen < srclen) {
715
        cc = 1;
716
    } else {
717
        cc = 2;
718
    }
719

    
720
    if (srclen > destlen) {
721
        srclen = destlen;
722
    }
723

    
724
    for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
725
        v = cpu_ldub_data(env, src);
726
        cpu_stb_data(env, dest, v);
727
    }
728

    
729
    for (; destlen; dest++, destlen--) {
730
        cpu_stb_data(env, dest, pad);
731
    }
732

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

    
739
    return cc;
740
}
741

    
742
/* move long extended another memcopy insn with more bells and whistles */
743
uint32_t HELPER(mvcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
744
                       uint32_t r3)
745
{
746
    uint64_t destlen = env->regs[r1 + 1];
747
    uint64_t dest = env->regs[r1];
748
    uint64_t srclen = env->regs[r3 + 1];
749
    uint64_t src = env->regs[r3];
750
    uint8_t pad = a2 & 0xff;
751
    uint8_t v;
752
    uint32_t cc;
753

    
754
    if (!(env->psw.mask & PSW_MASK_64)) {
755
        destlen = (uint32_t)destlen;
756
        srclen = (uint32_t)srclen;
757
        dest &= 0x7fffffff;
758
        src &= 0x7fffffff;
759
    }
760

    
761
    if (destlen == srclen) {
762
        cc = 0;
763
    } else if (destlen < srclen) {
764
        cc = 1;
765
    } else {
766
        cc = 2;
767
    }
768

    
769
    if (srclen > destlen) {
770
        srclen = destlen;
771
    }
772

    
773
    for (; destlen && srclen; src++, dest++, destlen--, srclen--) {
774
        v = cpu_ldub_data(env, src);
775
        cpu_stb_data(env, dest, v);
776
    }
777

    
778
    for (; destlen; dest++, destlen--) {
779
        cpu_stb_data(env, dest, pad);
780
    }
781

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

    
789
    return cc;
790
}
791

    
792
/* compare logical long extended memcompare insn with padding */
793
uint32_t HELPER(clcle)(CPUS390XState *env, uint32_t r1, uint64_t a2,
794
                       uint32_t r3)
795
{
796
    uint64_t destlen = env->regs[r1 + 1];
797
    uint64_t dest = get_address_31fix(env, r1);
798
    uint64_t srclen = env->regs[r3 + 1];
799
    uint64_t src = get_address_31fix(env, r3);
800
    uint8_t pad = a2 & 0xff;
801
    uint8_t v1 = 0, v2 = 0;
802
    uint32_t cc = 0;
803

    
804
    if (!(destlen || srclen)) {
805
        return cc;
806
    }
807

    
808
    if (srclen > destlen) {
809
        srclen = destlen;
810
    }
811

    
812
    for (; destlen || srclen; src++, dest++, destlen--, srclen--) {
813
        v1 = srclen ? cpu_ldub_data(env, src) : pad;
814
        v2 = destlen ? cpu_ldub_data(env, dest) : pad;
815
        if (v1 != v2) {
816
            cc = (v1 < v2) ? 1 : 2;
817
            break;
818
        }
819
    }
820

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

    
827
    return cc;
828
}
829

    
830
/* checksum */
831
void HELPER(cksm)(CPUS390XState *env, uint32_t r1, uint32_t r2)
832
{
833
    uint64_t src = get_address_31fix(env, r2);
834
    uint64_t src_len = env->regs[(r2 + 1) & 15];
835
    uint64_t cksm = (uint32_t)env->regs[r1];
836

    
837
    while (src_len >= 4) {
838
        cksm += cpu_ldl_data(env, src);
839

    
840
        /* move to next word */
841
        src_len -= 4;
842
        src += 4;
843
    }
844

    
845
    switch (src_len) {
846
    case 0:
847
        break;
848
    case 1:
849
        cksm += cpu_ldub_data(env, src) << 24;
850
        break;
851
    case 2:
852
        cksm += cpu_lduw_data(env, src) << 16;
853
        break;
854
    case 3:
855
        cksm += cpu_lduw_data(env, src) << 16;
856
        cksm += cpu_ldub_data(env, src + 2) << 8;
857
        break;
858
    }
859

    
860
    /* indicate we've processed everything */
861
    env->regs[r2] = src + src_len;
862
    env->regs[(r2 + 1) & 15] = 0;
863

    
864
    /* store result */
865
    env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
866
        ((uint32_t)cksm + (cksm >> 32));
867
}
868

    
869
void HELPER(unpk)(CPUS390XState *env, uint32_t len, uint64_t dest,
870
                  uint64_t src)
871
{
872
    int len_dest = len >> 4;
873
    int len_src = len & 0xf;
874
    uint8_t b;
875
    int second_nibble = 0;
876

    
877
    dest += len_dest;
878
    src += len_src;
879

    
880
    /* last byte is special, it only flips the nibbles */
881
    b = cpu_ldub_data(env, src);
882
    cpu_stb_data(env, dest, (b << 4) | (b >> 4));
883
    src--;
884
    len_src--;
885

    
886
    /* now pad every nibble with 0xf0 */
887

    
888
    while (len_dest > 0) {
889
        uint8_t cur_byte = 0;
890

    
891
        if (len_src > 0) {
892
            cur_byte = cpu_ldub_data(env, src);
893
        }
894

    
895
        len_dest--;
896
        dest--;
897

    
898
        /* only advance one nibble at a time */
899
        if (second_nibble) {
900
            cur_byte >>= 4;
901
            len_src--;
902
            src--;
903
        }
904
        second_nibble = !second_nibble;
905

    
906
        /* digit */
907
        cur_byte = (cur_byte & 0xf);
908
        /* zone bits */
909
        cur_byte |= 0xf0;
910

    
911
        cpu_stb_data(env, dest, cur_byte);
912
    }
913
}
914

    
915
void HELPER(tr)(CPUS390XState *env, uint32_t len, uint64_t array,
916
                uint64_t trans)
917
{
918
    int i;
919

    
920
    for (i = 0; i <= len; i++) {
921
        uint8_t byte = cpu_ldub_data(env, array + i);
922
        uint8_t new_byte = cpu_ldub_data(env, trans + byte);
923

    
924
        cpu_stb_data(env, array + i, new_byte);
925
    }
926
}
927

    
928
#if !defined(CONFIG_USER_ONLY)
929
void HELPER(lctlg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
930
{
931
    int i;
932
    uint64_t src = a2;
933

    
934
    for (i = r1;; i = (i + 1) % 16) {
935
        env->cregs[i] = cpu_ldq_data(env, src);
936
        HELPER_LOG("load ctl %d from 0x%" PRIx64 " == 0x%" PRIx64 "\n",
937
                   i, src, env->cregs[i]);
938
        src += sizeof(uint64_t);
939

    
940
        if (i == r3) {
941
            break;
942
        }
943
    }
944

    
945
    tlb_flush(env, 1);
946
}
947

    
948
void HELPER(lctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
949
{
950
    int i;
951
    uint64_t src = a2;
952

    
953
    for (i = r1;; i = (i + 1) % 16) {
954
        env->cregs[i] = (env->cregs[i] & 0xFFFFFFFF00000000ULL) |
955
            cpu_ldl_data(env, src);
956
        src += sizeof(uint32_t);
957

    
958
        if (i == r3) {
959
            break;
960
        }
961
    }
962

    
963
    tlb_flush(env, 1);
964
}
965

    
966
void HELPER(stctg)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
967
{
968
    int i;
969
    uint64_t dest = a2;
970

    
971
    for (i = r1;; i = (i + 1) % 16) {
972
        cpu_stq_data(env, dest, env->cregs[i]);
973
        dest += sizeof(uint64_t);
974

    
975
        if (i == r3) {
976
            break;
977
        }
978
    }
979
}
980

    
981
void HELPER(stctl)(CPUS390XState *env, uint32_t r1, uint64_t a2, uint32_t r3)
982
{
983
    int i;
984
    uint64_t dest = a2;
985

    
986
    for (i = r1;; i = (i + 1) % 16) {
987
        cpu_stl_data(env, dest, env->cregs[i]);
988
        dest += sizeof(uint32_t);
989

    
990
        if (i == r3) {
991
            break;
992
        }
993
    }
994
}
995

    
996
uint32_t HELPER(tprot)(uint64_t a1, uint64_t a2)
997
{
998
    /* XXX implement */
999

    
1000
    return 0;
1001
}
1002

    
1003
/* insert storage key extended */
1004
uint64_t HELPER(iske)(CPUS390XState *env, uint64_t r2)
1005
{
1006
    uint64_t addr = get_address(env, 0, 0, r2);
1007

    
1008
    if (addr > ram_size) {
1009
        return 0;
1010
    }
1011

    
1012
    return env->storage_keys[addr / TARGET_PAGE_SIZE];
1013
}
1014

    
1015
/* set storage key extended */
1016
void HELPER(sske)(CPUS390XState *env, uint32_t r1, uint64_t r2)
1017
{
1018
    uint64_t addr = get_address(env, 0, 0, r2);
1019

    
1020
    if (addr > ram_size) {
1021
        return;
1022
    }
1023

    
1024
    env->storage_keys[addr / TARGET_PAGE_SIZE] = r1;
1025
}
1026

    
1027
/* reset reference bit extended */
1028
uint32_t HELPER(rrbe)(CPUS390XState *env, uint32_t r1, uint64_t r2)
1029
{
1030
    uint8_t re;
1031
    uint8_t key;
1032

    
1033
    if (r2 > ram_size) {
1034
        return 0;
1035
    }
1036

    
1037
    key = env->storage_keys[r2 / TARGET_PAGE_SIZE];
1038
    re = key & (SK_R | SK_C);
1039
    env->storage_keys[r2 / TARGET_PAGE_SIZE] = (key & ~SK_R);
1040

    
1041
    /*
1042
     * cc
1043
     *
1044
     * 0  Reference bit zero; change bit zero
1045
     * 1  Reference bit zero; change bit one
1046
     * 2  Reference bit one; change bit zero
1047
     * 3  Reference bit one; change bit one
1048
     */
1049

    
1050
    return re >> 1;
1051
}
1052

    
1053
/* compare and swap and purge */
1054
uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint32_t r2)
1055
{
1056
    uint32_t cc;
1057
    uint32_t o1 = env->regs[r1];
1058
    uint64_t a2 = get_address_31fix(env, r2) & ~3ULL;
1059
    uint32_t o2 = cpu_ldl_data(env, a2);
1060

    
1061
    if (o1 == o2) {
1062
        cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
1063
        if (env->regs[r2] & 0x3) {
1064
            /* flush TLB / ALB */
1065
            tlb_flush(env, 1);
1066
        }
1067
        cc = 0;
1068
    } else {
1069
        env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
1070
        cc = 1;
1071
    }
1072

    
1073
    return cc;
1074
}
1075

    
1076
static uint32_t mvc_asc(CPUS390XState *env, int64_t l, uint64_t a1,
1077
                        uint64_t mode1, uint64_t a2, uint64_t mode2)
1078
{
1079
    target_ulong src, dest;
1080
    int flags, cc = 0, i;
1081

    
1082
    if (!l) {
1083
        return 0;
1084
    } else if (l > 256) {
1085
        /* max 256 */
1086
        l = 256;
1087
        cc = 3;
1088
    }
1089

    
1090
    if (mmu_translate(env, a1 & TARGET_PAGE_MASK, 1, mode1, &dest, &flags)) {
1091
        cpu_loop_exit(env);
1092
    }
1093
    dest |= a1 & ~TARGET_PAGE_MASK;
1094

    
1095
    if (mmu_translate(env, a2 & TARGET_PAGE_MASK, 0, mode2, &src, &flags)) {
1096
        cpu_loop_exit(env);
1097
    }
1098
    src |= a2 & ~TARGET_PAGE_MASK;
1099

    
1100
    /* XXX replace w/ memcpy */
1101
    for (i = 0; i < l; i++) {
1102
        /* XXX be more clever */
1103
        if ((((dest + i) & TARGET_PAGE_MASK) != (dest & TARGET_PAGE_MASK)) ||
1104
            (((src + i) & TARGET_PAGE_MASK) != (src & TARGET_PAGE_MASK))) {
1105
            mvc_asc(env, l - i, a1 + i, mode1, a2 + i, mode2);
1106
            break;
1107
        }
1108
        stb_phys(dest + i, ldub_phys(src + i));
1109
    }
1110

    
1111
    return cc;
1112
}
1113

    
1114
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1115
{
1116
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1117
               __func__, l, a1, a2);
1118

    
1119
    return mvc_asc(env, l, a1, PSW_ASC_SECONDARY, a2, PSW_ASC_PRIMARY);
1120
}
1121

    
1122
uint32_t HELPER(mvcp)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
1123
{
1124
    HELPER_LOG("%s: %16" PRIx64 " %16" PRIx64 " %16" PRIx64 "\n",
1125
               __func__, l, a1, a2);
1126

    
1127
    return mvc_asc(env, l, a1, PSW_ASC_PRIMARY, a2, PSW_ASC_SECONDARY);
1128
}
1129

    
1130
/* invalidate pte */
1131
void HELPER(ipte)(CPUS390XState *env, uint64_t pte_addr, uint64_t vaddr)
1132
{
1133
    uint64_t page = vaddr & TARGET_PAGE_MASK;
1134
    uint64_t pte = 0;
1135

    
1136
    /* XXX broadcast to other CPUs */
1137

    
1138
    /* XXX Linux is nice enough to give us the exact pte address.
1139
       According to spec we'd have to find it out ourselves */
1140
    /* XXX Linux is fine with overwriting the pte, the spec requires
1141
       us to only set the invalid bit */
1142
    stq_phys(pte_addr, pte | _PAGE_INVALID);
1143

    
1144
    /* XXX we exploit the fact that Linux passes the exact virtual
1145
       address here - it's not obliged to! */
1146
    tlb_flush_page(env, page);
1147

    
1148
    /* XXX 31-bit hack */
1149
    if (page & 0x80000000) {
1150
        tlb_flush_page(env, page & ~0x80000000);
1151
    } else {
1152
        tlb_flush_page(env, page | 0x80000000);
1153
    }
1154
}
1155

    
1156
/* flush local tlb */
1157
void HELPER(ptlb)(CPUS390XState *env)
1158
{
1159
    tlb_flush(env, 1);
1160
}
1161

    
1162
/* store using real address */
1163
void HELPER(stura)(CPUS390XState *env, uint64_t addr, uint32_t v1)
1164
{
1165
    stw_phys(get_address(env, 0, 0, addr), v1);
1166
}
1167

    
1168
/* load real address */
1169
uint32_t HELPER(lra)(CPUS390XState *env, uint64_t addr, uint32_t r1)
1170
{
1171
    uint32_t cc = 0;
1172
    int old_exc = env->exception_index;
1173
    uint64_t asc = env->psw.mask & PSW_MASK_ASC;
1174
    uint64_t ret;
1175
    int flags;
1176

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

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

    
1193
    if (!(env->psw.mask & PSW_MASK_64)) {
1194
        env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) |
1195
            (ret & 0xffffffffULL);
1196
    } else {
1197
        env->regs[r1] = ret;
1198
    }
1199

    
1200
    return cc;
1201
}
1202

    
1203
#endif