~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to target/s390x/mmu_helper.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * S390x MMU related functions
 
3
 *
 
4
 * Copyright (c) 2011 Alexander Graf
 
5
 * Copyright (c) 2015 Thomas Huth, IBM Corporation
 
6
 *
 
7
 * This program is free software; you can redistribute it and/or modify
 
8
 * it under the terms of the GNU General Public License as published by
 
9
 * the Free Software Foundation; either version 2 of the License, or
 
10
 * (at your option) any later version.
 
11
 *
 
12
 * This program 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
 
15
 * GNU General Public License for more details.
 
16
 */
 
17
 
 
18
#include "qemu/osdep.h"
 
19
#include "qemu/error-report.h"
 
20
#include "exec/address-spaces.h"
 
21
#include "cpu.h"
 
22
#include "sysemu/kvm.h"
 
23
#include "trace.h"
 
24
#include "hw/s390x/storage-keys.h"
 
25
 
 
26
/* #define DEBUG_S390 */
 
27
/* #define DEBUG_S390_PTE */
 
28
/* #define DEBUG_S390_STDOUT */
 
29
 
 
30
#ifdef DEBUG_S390
 
31
#ifdef DEBUG_S390_STDOUT
 
32
#define DPRINTF(fmt, ...) \
 
33
    do { fprintf(stderr, fmt, ## __VA_ARGS__); \
 
34
         if (qemu_log_separate()) qemu_log(fmt, ##__VA_ARGS__); } while (0)
 
35
#else
 
36
#define DPRINTF(fmt, ...) \
 
37
    do { qemu_log(fmt, ## __VA_ARGS__); } while (0)
 
38
#endif
 
39
#else
 
40
#define DPRINTF(fmt, ...) \
 
41
    do { } while (0)
 
42
#endif
 
43
 
 
44
#ifdef DEBUG_S390_PTE
 
45
#define PTE_DPRINTF DPRINTF
 
46
#else
 
47
#define PTE_DPRINTF(fmt, ...) \
 
48
    do { } while (0)
 
49
#endif
 
50
 
 
51
/* Fetch/store bits in the translation exception code: */
 
52
#define FS_READ  0x800
 
53
#define FS_WRITE 0x400
 
54
 
 
55
static void trigger_access_exception(CPUS390XState *env, uint32_t type,
 
56
                                     uint32_t ilen, uint64_t tec)
 
57
{
 
58
    S390CPU *cpu = s390_env_get_cpu(env);
 
59
 
 
60
    if (kvm_enabled()) {
 
61
        kvm_s390_access_exception(cpu, type, tec);
 
62
    } else {
 
63
        CPUState *cs = CPU(cpu);
 
64
        stq_phys(cs->as, env->psa + offsetof(LowCore, trans_exc_code), tec);
 
65
        trigger_pgm_exception(env, type, ilen);
 
66
    }
 
67
}
 
68
 
 
69
static void trigger_prot_fault(CPUS390XState *env, target_ulong vaddr,
 
70
                               uint64_t asc, int rw, bool exc)
 
71
{
 
72
    uint64_t tec;
 
73
 
 
74
    tec = vaddr | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) | 4 | asc >> 46;
 
75
 
 
76
    DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec);
 
77
 
 
78
    if (!exc) {
 
79
        return;
 
80
    }
 
81
 
 
82
    trigger_access_exception(env, PGM_PROTECTION, ILEN_LATER_INC, tec);
 
83
}
 
84
 
 
85
static void trigger_page_fault(CPUS390XState *env, target_ulong vaddr,
 
86
                               uint32_t type, uint64_t asc, int rw, bool exc)
 
87
{
 
88
    int ilen = ILEN_LATER;
 
89
    uint64_t tec;
 
90
 
 
91
    tec = vaddr | (rw == MMU_DATA_STORE ? FS_WRITE : FS_READ) | asc >> 46;
 
92
 
 
93
    DPRINTF("%s: trans_exc_code=%016" PRIx64 "\n", __func__, tec);
 
94
 
 
95
    if (!exc) {
 
96
        return;
 
97
    }
 
98
 
 
99
    /* Code accesses have an undefined ilc.  */
 
100
    if (rw == MMU_INST_FETCH) {
 
101
        ilen = 2;
 
102
    }
 
103
 
 
104
    trigger_access_exception(env, type, ilen, tec);
 
105
}
 
106
 
 
107
/**
 
108
 * Translate real address to absolute (= physical)
 
109
 * address by taking care of the prefix mapping.
 
110
 */
 
111
static target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr)
 
112
{
 
113
    if (raddr < 0x2000) {
 
114
        return raddr + env->psa;    /* Map the lowcore. */
 
115
    } else if (raddr >= env->psa && raddr < env->psa + 0x2000) {
 
116
        return raddr - env->psa;    /* Map the 0 page. */
 
117
    }
 
118
    return raddr;
 
119
}
 
120
 
 
121
/* Decode page table entry (normal 4KB page) */
 
122
static int mmu_translate_pte(CPUS390XState *env, target_ulong vaddr,
 
123
                             uint64_t asc, uint64_t pt_entry,
 
124
                             target_ulong *raddr, int *flags, int rw, bool exc)
 
125
{
 
126
    if (pt_entry & _PAGE_INVALID) {
 
127
        DPRINTF("%s: PTE=0x%" PRIx64 " invalid\n", __func__, pt_entry);
 
128
        trigger_page_fault(env, vaddr, PGM_PAGE_TRANS, asc, rw, exc);
 
129
        return -1;
 
130
    }
 
131
    if (pt_entry & _PAGE_RES0) {
 
132
        trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
 
133
        return -1;
 
134
    }
 
135
    if (pt_entry & _PAGE_RO) {
 
136
        *flags &= ~PAGE_WRITE;
 
137
    }
 
138
 
 
139
    *raddr = pt_entry & _ASCE_ORIGIN;
 
140
 
 
141
    PTE_DPRINTF("%s: PTE=0x%" PRIx64 "\n", __func__, pt_entry);
 
142
 
 
143
    return 0;
 
144
}
 
145
 
 
146
#define VADDR_PX    0xff000         /* Page index bits */
 
147
 
 
148
/* Decode segment table entry */
 
149
static int mmu_translate_segment(CPUS390XState *env, target_ulong vaddr,
 
150
                                 uint64_t asc, uint64_t st_entry,
 
151
                                 target_ulong *raddr, int *flags, int rw,
 
152
                                 bool exc)
 
153
{
 
154
    CPUState *cs = CPU(s390_env_get_cpu(env));
 
155
    uint64_t origin, offs, pt_entry;
 
156
 
 
157
    if (st_entry & _SEGMENT_ENTRY_RO) {
 
158
        *flags &= ~PAGE_WRITE;
 
159
    }
 
160
 
 
161
    if ((st_entry & _SEGMENT_ENTRY_FC) && (env->cregs[0] & CR0_EDAT)) {
 
162
        /* Decode EDAT1 segment frame absolute address (1MB page) */
 
163
        *raddr = (st_entry & 0xfffffffffff00000ULL) | (vaddr & 0xfffff);
 
164
        PTE_DPRINTF("%s: SEG=0x%" PRIx64 "\n", __func__, st_entry);
 
165
        return 0;
 
166
    }
 
167
 
 
168
    /* Look up 4KB page entry */
 
169
    origin = st_entry & _SEGMENT_ENTRY_ORIGIN;
 
170
    offs  = (vaddr & VADDR_PX) >> 9;
 
171
    pt_entry = ldq_phys(cs->as, origin + offs);
 
172
    PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
 
173
                __func__, origin, offs, pt_entry);
 
174
    return mmu_translate_pte(env, vaddr, asc, pt_entry, raddr, flags, rw, exc);
 
175
}
 
176
 
 
177
/* Decode region table entries */
 
178
static int mmu_translate_region(CPUS390XState *env, target_ulong vaddr,
 
179
                                uint64_t asc, uint64_t entry, int level,
 
180
                                target_ulong *raddr, int *flags, int rw,
 
181
                                bool exc)
 
182
{
 
183
    CPUState *cs = CPU(s390_env_get_cpu(env));
 
184
    uint64_t origin, offs, new_entry;
 
185
    const int pchks[4] = {
 
186
        PGM_SEGMENT_TRANS, PGM_REG_THIRD_TRANS,
 
187
        PGM_REG_SEC_TRANS, PGM_REG_FIRST_TRANS
 
188
    };
 
189
 
 
190
    PTE_DPRINTF("%s: 0x%" PRIx64 "\n", __func__, entry);
 
191
 
 
192
    origin = entry & _REGION_ENTRY_ORIGIN;
 
193
    offs = (vaddr >> (17 + 11 * level / 4)) & 0x3ff8;
 
194
 
 
195
    new_entry = ldq_phys(cs->as, origin + offs);
 
196
    PTE_DPRINTF("%s: 0x%" PRIx64 " + 0x%" PRIx64 " => 0x%016" PRIx64 "\n",
 
197
                __func__, origin, offs, new_entry);
 
198
 
 
199
    if ((new_entry & _REGION_ENTRY_INV) != 0) {
 
200
        DPRINTF("%s: invalid region\n", __func__);
 
201
        trigger_page_fault(env, vaddr, pchks[level / 4], asc, rw, exc);
 
202
        return -1;
 
203
    }
 
204
 
 
205
    if ((new_entry & _REGION_ENTRY_TYPE_MASK) != level) {
 
206
        trigger_page_fault(env, vaddr, PGM_TRANS_SPEC, asc, rw, exc);
 
207
        return -1;
 
208
    }
 
209
 
 
210
    if (level == _ASCE_TYPE_SEGMENT) {
 
211
        return mmu_translate_segment(env, vaddr, asc, new_entry, raddr, flags,
 
212
                                     rw, exc);
 
213
    }
 
214
 
 
215
    /* Check region table offset and length */
 
216
    offs = (vaddr >> (28 + 11 * (level - 4) / 4)) & 3;
 
217
    if (offs < ((new_entry & _REGION_ENTRY_TF) >> 6)
 
218
        || offs > (new_entry & _REGION_ENTRY_LENGTH)) {
 
219
        DPRINTF("%s: invalid offset or len (%lx)\n", __func__, new_entry);
 
220
        trigger_page_fault(env, vaddr, pchks[level / 4 - 1], asc, rw, exc);
 
221
        return -1;
 
222
    }
 
223
 
 
224
    if ((env->cregs[0] & CR0_EDAT) && (new_entry & _REGION_ENTRY_RO)) {
 
225
        *flags &= ~PAGE_WRITE;
 
226
    }
 
227
 
 
228
    /* yet another region */
 
229
    return mmu_translate_region(env, vaddr, asc, new_entry, level - 4,
 
230
                                raddr, flags, rw, exc);
 
231
}
 
232
 
 
233
static int mmu_translate_asce(CPUS390XState *env, target_ulong vaddr,
 
234
                              uint64_t asc, uint64_t asce, target_ulong *raddr,
 
235
                              int *flags, int rw, bool exc)
 
236
{
 
237
    int level;
 
238
    int r;
 
239
 
 
240
    if (asce & _ASCE_REAL_SPACE) {
 
241
        /* direct mapping */
 
242
        *raddr = vaddr;
 
243
        return 0;
 
244
    }
 
245
 
 
246
    level = asce & _ASCE_TYPE_MASK;
 
247
    switch (level) {
 
248
    case _ASCE_TYPE_REGION1:
 
249
        if ((vaddr >> 62) > (asce & _ASCE_TABLE_LENGTH)) {
 
250
            trigger_page_fault(env, vaddr, PGM_REG_FIRST_TRANS, asc, rw, exc);
 
251
            return -1;
 
252
        }
 
253
        break;
 
254
    case _ASCE_TYPE_REGION2:
 
255
        if (vaddr & 0xffe0000000000000ULL) {
 
256
            DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
 
257
                    " 0xffe0000000000000ULL\n", __func__, vaddr);
 
258
            trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
 
259
            return -1;
 
260
        }
 
261
        if ((vaddr >> 51 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
 
262
            trigger_page_fault(env, vaddr, PGM_REG_SEC_TRANS, asc, rw, exc);
 
263
            return -1;
 
264
        }
 
265
        break;
 
266
    case _ASCE_TYPE_REGION3:
 
267
        if (vaddr & 0xfffffc0000000000ULL) {
 
268
            DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
 
269
                    " 0xfffffc0000000000ULL\n", __func__, vaddr);
 
270
            trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
 
271
            return -1;
 
272
        }
 
273
        if ((vaddr >> 40 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
 
274
            trigger_page_fault(env, vaddr, PGM_REG_THIRD_TRANS, asc, rw, exc);
 
275
            return -1;
 
276
        }
 
277
        break;
 
278
    case _ASCE_TYPE_SEGMENT:
 
279
        if (vaddr & 0xffffffff80000000ULL) {
 
280
            DPRINTF("%s: vaddr doesn't fit 0x%16" PRIx64
 
281
                    " 0xffffffff80000000ULL\n", __func__, vaddr);
 
282
            trigger_page_fault(env, vaddr, PGM_ASCE_TYPE, asc, rw, exc);
 
283
            return -1;
 
284
        }
 
285
        if ((vaddr >> 29 & 3) > (asce & _ASCE_TABLE_LENGTH)) {
 
286
            trigger_page_fault(env, vaddr, PGM_SEGMENT_TRANS, asc, rw, exc);
 
287
            return -1;
 
288
        }
 
289
        break;
 
290
    }
 
291
 
 
292
    r = mmu_translate_region(env, vaddr, asc, asce, level, raddr, flags, rw,
 
293
                             exc);
 
294
    if (rw == MMU_DATA_STORE && !(*flags & PAGE_WRITE)) {
 
295
        trigger_prot_fault(env, vaddr, asc, rw, exc);
 
296
        return -1;
 
297
    }
 
298
 
 
299
    return r;
 
300
}
 
301
 
 
302
/**
 
303
 * Translate a virtual (logical) address into a physical (absolute) address.
 
304
 * @param vaddr  the virtual address
 
305
 * @param rw     0 = read, 1 = write, 2 = code fetch
 
306
 * @param asc    address space control (one of the PSW_ASC_* modes)
 
307
 * @param raddr  the translated address is stored to this pointer
 
308
 * @param flags  the PAGE_READ/WRITE/EXEC flags are stored to this pointer
 
309
 * @param exc    true = inject a program check if a fault occurred
 
310
 * @return       0 if the translation was successful, -1 if a fault occurred
 
311
 */
 
312
int mmu_translate(CPUS390XState *env, target_ulong vaddr, int rw, uint64_t asc,
 
313
                  target_ulong *raddr, int *flags, bool exc)
 
314
{
 
315
    static S390SKeysState *ss;
 
316
    static S390SKeysClass *skeyclass;
 
317
    int r = -1;
 
318
    uint8_t key;
 
319
 
 
320
    if (unlikely(!ss)) {
 
321
        ss = s390_get_skeys_device();
 
322
        skeyclass = S390_SKEYS_GET_CLASS(ss);
 
323
    }
 
324
 
 
325
    *flags = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
 
326
    vaddr &= TARGET_PAGE_MASK;
 
327
 
 
328
    if (!(env->psw.mask & PSW_MASK_DAT)) {
 
329
        *raddr = vaddr;
 
330
        r = 0;
 
331
        goto out;
 
332
    }
 
333
 
 
334
    switch (asc) {
 
335
    case PSW_ASC_PRIMARY:
 
336
        PTE_DPRINTF("%s: asc=primary\n", __func__);
 
337
        r = mmu_translate_asce(env, vaddr, asc, env->cregs[1], raddr, flags,
 
338
                               rw, exc);
 
339
        break;
 
340
    case PSW_ASC_HOME:
 
341
        PTE_DPRINTF("%s: asc=home\n", __func__);
 
342
        r = mmu_translate_asce(env, vaddr, asc, env->cregs[13], raddr, flags,
 
343
                               rw, exc);
 
344
        break;
 
345
    case PSW_ASC_SECONDARY:
 
346
        PTE_DPRINTF("%s: asc=secondary\n", __func__);
 
347
        /*
 
348
         * Instruction: Primary
 
349
         * Data: Secondary
 
350
         */
 
351
        if (rw == MMU_INST_FETCH) {
 
352
            r = mmu_translate_asce(env, vaddr, PSW_ASC_PRIMARY, env->cregs[1],
 
353
                                   raddr, flags, rw, exc);
 
354
            *flags &= ~(PAGE_READ | PAGE_WRITE);
 
355
        } else {
 
356
            r = mmu_translate_asce(env, vaddr, PSW_ASC_SECONDARY, env->cregs[7],
 
357
                                   raddr, flags, rw, exc);
 
358
            *flags &= ~(PAGE_EXEC);
 
359
        }
 
360
        break;
 
361
    case PSW_ASC_ACCREG:
 
362
    default:
 
363
        hw_error("guest switched to unknown asc mode\n");
 
364
        break;
 
365
    }
 
366
 
 
367
 out:
 
368
    /* Convert real address -> absolute address */
 
369
    *raddr = mmu_real2abs(env, *raddr);
 
370
 
 
371
    if (r == 0 && *raddr < ram_size) {
 
372
        if (skeyclass->get_skeys(ss, *raddr / TARGET_PAGE_SIZE, 1, &key)) {
 
373
            trace_get_skeys_nonzero(r);
 
374
            return 0;
 
375
        }
 
376
 
 
377
        if (*flags & PAGE_READ) {
 
378
            key |= SK_R;
 
379
        }
 
380
 
 
381
        if (*flags & PAGE_WRITE) {
 
382
            key |= SK_C;
 
383
        }
 
384
 
 
385
        if (skeyclass->set_skeys(ss, *raddr / TARGET_PAGE_SIZE, 1, &key)) {
 
386
            trace_set_skeys_nonzero(r);
 
387
            return 0;
 
388
        }
 
389
    }
 
390
 
 
391
    return r;
 
392
}
 
393
 
 
394
/**
 
395
 * lowprot_enabled: Check whether low-address protection is enabled
 
396
 */
 
397
static bool lowprot_enabled(const CPUS390XState *env)
 
398
{
 
399
    if (!(env->cregs[0] & CR0_LOWPROT)) {
 
400
        return false;
 
401
    }
 
402
    if (!(env->psw.mask & PSW_MASK_DAT)) {
 
403
        return true;
 
404
    }
 
405
 
 
406
    /* Check the private-space control bit */
 
407
    switch (env->psw.mask & PSW_MASK_ASC) {
 
408
    case PSW_ASC_PRIMARY:
 
409
        return !(env->cregs[1] & _ASCE_PRIVATE_SPACE);
 
410
    case PSW_ASC_SECONDARY:
 
411
        return !(env->cregs[7] & _ASCE_PRIVATE_SPACE);
 
412
    case PSW_ASC_HOME:
 
413
        return !(env->cregs[13] & _ASCE_PRIVATE_SPACE);
 
414
    default:
 
415
        /* We don't support access register mode */
 
416
        error_report("unsupported addressing mode");
 
417
        exit(1);
 
418
    }
 
419
}
 
420
 
 
421
/**
 
422
 * translate_pages: Translate a set of consecutive logical page addresses
 
423
 * to absolute addresses
 
424
 */
 
425
static int translate_pages(S390CPU *cpu, vaddr addr, int nr_pages,
 
426
                           target_ulong *pages, bool is_write)
 
427
{
 
428
    bool lowprot = is_write && lowprot_enabled(&cpu->env);
 
429
    uint64_t asc = cpu->env.psw.mask & PSW_MASK_ASC;
 
430
    CPUS390XState *env = &cpu->env;
 
431
    int ret, i, pflags;
 
432
 
 
433
    for (i = 0; i < nr_pages; i++) {
 
434
        /* Low-address protection? */
 
435
        if (lowprot && (addr < 512 || (addr >= 4096 && addr < 4096 + 512))) {
 
436
            trigger_access_exception(env, PGM_PROTECTION, ILEN_LATER_INC, 0);
 
437
            return -EACCES;
 
438
        }
 
439
        ret = mmu_translate(env, addr, is_write, asc, &pages[i], &pflags, true);
 
440
        if (ret) {
 
441
            return ret;
 
442
        }
 
443
        if (!address_space_access_valid(&address_space_memory, pages[i],
 
444
                                        TARGET_PAGE_SIZE, is_write)) {
 
445
            program_interrupt(env, PGM_ADDRESSING, 0);
 
446
            return -EFAULT;
 
447
        }
 
448
        addr += TARGET_PAGE_SIZE;
 
449
    }
 
450
 
 
451
    return 0;
 
452
}
 
453
 
 
454
/**
 
455
 * s390_cpu_virt_mem_rw:
 
456
 * @laddr:     the logical start address
 
457
 * @ar:        the access register number
 
458
 * @hostbuf:   buffer in host memory. NULL = do only checks w/o copying
 
459
 * @len:       length that should be transferred
 
460
 * @is_write:  true = write, false = read
 
461
 * Returns:    0 on success, non-zero if an exception occurred
 
462
 *
 
463
 * Copy from/to guest memory using logical addresses. Note that we inject a
 
464
 * program interrupt in case there is an error while accessing the memory.
 
465
 */
 
466
int s390_cpu_virt_mem_rw(S390CPU *cpu, vaddr laddr, uint8_t ar, void *hostbuf,
 
467
                         int len, bool is_write)
 
468
{
 
469
    int currlen, nr_pages, i;
 
470
    target_ulong *pages;
 
471
    int ret;
 
472
 
 
473
    if (kvm_enabled()) {
 
474
        ret = kvm_s390_mem_op(cpu, laddr, ar, hostbuf, len, is_write);
 
475
        if (ret >= 0) {
 
476
            return ret;
 
477
        }
 
478
    }
 
479
 
 
480
    nr_pages = (((laddr & ~TARGET_PAGE_MASK) + len - 1) >> TARGET_PAGE_BITS)
 
481
               + 1;
 
482
    pages = g_malloc(nr_pages * sizeof(*pages));
 
483
 
 
484
    ret = translate_pages(cpu, laddr, nr_pages, pages, is_write);
 
485
    if (ret == 0 && hostbuf != NULL) {
 
486
        /* Copy data by stepping through the area page by page */
 
487
        for (i = 0; i < nr_pages; i++) {
 
488
            currlen = MIN(len, TARGET_PAGE_SIZE - (laddr % TARGET_PAGE_SIZE));
 
489
            cpu_physical_memory_rw(pages[i] | (laddr & ~TARGET_PAGE_MASK),
 
490
                                   hostbuf, currlen, is_write);
 
491
            laddr += currlen;
 
492
            hostbuf += currlen;
 
493
            len -= currlen;
 
494
        }
 
495
    }
 
496
 
 
497
    g_free(pages);
 
498
    return ret;
 
499
}