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

« back to all changes in this revision

Viewing changes to target/alpha/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
 *  Alpha emulation cpu helpers for qemu.
 
3
 *
 
4
 *  Copyright (c) 2007 Jocelyn Mayer
 
5
 *
 
6
 * This library is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU Lesser General Public
 
8
 * License as published by the Free Software Foundation; either
 
9
 * version 2 of the License, or (at your option) any later version.
 
10
 *
 
11
 * This library is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * Lesser General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU Lesser General Public
 
17
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include "qemu/osdep.h"
 
21
 
 
22
#include "cpu.h"
 
23
#include "exec/exec-all.h"
 
24
#include "fpu/softfloat.h"
 
25
#include "exec/helper-proto.h"
 
26
 
 
27
 
 
28
#define CONVERT_BIT(X, SRC, DST) \
 
29
    (SRC > DST ? (X) / (SRC / DST) & (DST) : ((X) & SRC) * (DST / SRC))
 
30
 
 
31
uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env)
 
32
{
 
33
    return (uint64_t)env->fpcr << 32;
 
34
}
 
35
 
 
36
void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val)
 
37
{
 
38
    uint32_t fpcr = val >> 32;
 
39
    uint32_t t = 0;
 
40
 
 
41
    t |= CONVERT_BIT(fpcr, FPCR_INED, FPCR_INE);
 
42
    t |= CONVERT_BIT(fpcr, FPCR_UNFD, FPCR_UNF);
 
43
    t |= CONVERT_BIT(fpcr, FPCR_OVFD, FPCR_OVF);
 
44
    t |= CONVERT_BIT(fpcr, FPCR_DZED, FPCR_DZE);
 
45
    t |= CONVERT_BIT(fpcr, FPCR_INVD, FPCR_INV);
 
46
 
 
47
    env->fpcr = fpcr;
 
48
    env->fpcr_exc_enable = ~t & FPCR_STATUS_MASK;
 
49
 
 
50
    switch (fpcr & FPCR_DYN_MASK) {
 
51
    case FPCR_DYN_NORMAL:
 
52
    default:
 
53
        t = float_round_nearest_even;
 
54
        break;
 
55
    case FPCR_DYN_CHOPPED:
 
56
        t = float_round_to_zero;
 
57
        break;
 
58
    case FPCR_DYN_MINUS:
 
59
        t = float_round_down;
 
60
        break;
 
61
    case FPCR_DYN_PLUS:
 
62
        t = float_round_up;
 
63
        break;
 
64
    }
 
65
    env->fpcr_dyn_round = t;
 
66
 
 
67
    env->fpcr_flush_to_zero = (fpcr & FPCR_UNFD) && (fpcr & FPCR_UNDZ);
 
68
    env->fp_status.flush_inputs_to_zero = (fpcr & FPCR_DNZ) != 0;
 
69
}
 
70
 
 
71
uint64_t helper_load_fpcr(CPUAlphaState *env)
 
72
{
 
73
    return cpu_alpha_load_fpcr(env);
 
74
}
 
75
 
 
76
void helper_store_fpcr(CPUAlphaState *env, uint64_t val)
 
77
{
 
78
    cpu_alpha_store_fpcr(env, val);
 
79
}
 
80
 
 
81
static uint64_t *cpu_alpha_addr_gr(CPUAlphaState *env, unsigned reg)
 
82
{
 
83
#ifndef CONFIG_USER_ONLY
 
84
    if (env->pal_mode) {
 
85
        if (reg >= 8 && reg <= 14) {
 
86
            return &env->shadow[reg - 8];
 
87
        } else if (reg == 25) {
 
88
            return &env->shadow[7];
 
89
        }
 
90
    }
 
91
#endif
 
92
    return &env->ir[reg];
 
93
}
 
94
 
 
95
uint64_t cpu_alpha_load_gr(CPUAlphaState *env, unsigned reg)
 
96
{
 
97
    return *cpu_alpha_addr_gr(env, reg);
 
98
}
 
99
 
 
100
void cpu_alpha_store_gr(CPUAlphaState *env, unsigned reg, uint64_t val)
 
101
{
 
102
    *cpu_alpha_addr_gr(env, reg) = val;
 
103
}
 
104
 
 
105
#if defined(CONFIG_USER_ONLY)
 
106
int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr address,
 
107
                               int rw, int mmu_idx)
 
108
{
 
109
    AlphaCPU *cpu = ALPHA_CPU(cs);
 
110
 
 
111
    cs->exception_index = EXCP_MMFAULT;
 
112
    cpu->env.trap_arg0 = address;
 
113
    return 1;
 
114
}
 
115
#else
 
116
/* Returns the OSF/1 entMM failure indication, or -1 on success.  */
 
117
static int get_physical_address(CPUAlphaState *env, target_ulong addr,
 
118
                                int prot_need, int mmu_idx,
 
119
                                target_ulong *pphys, int *pprot)
 
120
{
 
121
    CPUState *cs = CPU(alpha_env_get_cpu(env));
 
122
    target_long saddr = addr;
 
123
    target_ulong phys = 0;
 
124
    target_ulong L1pte, L2pte, L3pte;
 
125
    target_ulong pt, index;
 
126
    int prot = 0;
 
127
    int ret = MM_K_ACV;
 
128
 
 
129
    /* Handle physical accesses.  */
 
130
    if (mmu_idx == MMU_PHYS_IDX) {
 
131
        phys = addr;
 
132
        prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
 
133
        ret = -1;
 
134
        goto exit;
 
135
    }
 
136
 
 
137
    /* Ensure that the virtual address is properly sign-extended from
 
138
       the last implemented virtual address bit.  */
 
139
    if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) {
 
140
        goto exit;
 
141
    }
 
142
 
 
143
    /* Translate the superpage.  */
 
144
    /* ??? When we do more than emulate Unix PALcode, we'll need to
 
145
       determine which KSEG is actually active.  */
 
146
    if (saddr < 0 && ((saddr >> 41) & 3) == 2) {
 
147
        /* User-space cannot access KSEG addresses.  */
 
148
        if (mmu_idx != MMU_KERNEL_IDX) {
 
149
            goto exit;
 
150
        }
 
151
 
 
152
        /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
 
153
           We would not do this if the 48-bit KSEG is enabled.  */
 
154
        phys = saddr & ((1ull << 40) - 1);
 
155
        phys |= (saddr & (1ull << 40)) << 3;
 
156
 
 
157
        prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
 
158
        ret = -1;
 
159
        goto exit;
 
160
    }
 
161
 
 
162
    /* Interpret the page table exactly like PALcode does.  */
 
163
 
 
164
    pt = env->ptbr;
 
165
 
 
166
    /* L1 page table read.  */
 
167
    index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff;
 
168
    L1pte = ldq_phys(cs->as, pt + index*8);
 
169
 
 
170
    if (unlikely((L1pte & PTE_VALID) == 0)) {
 
171
        ret = MM_K_TNV;
 
172
        goto exit;
 
173
    }
 
174
    if (unlikely((L1pte & PTE_KRE) == 0)) {
 
175
        goto exit;
 
176
    }
 
177
    pt = L1pte >> 32 << TARGET_PAGE_BITS;
 
178
 
 
179
    /* L2 page table read.  */
 
180
    index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff;
 
181
    L2pte = ldq_phys(cs->as, pt + index*8);
 
182
 
 
183
    if (unlikely((L2pte & PTE_VALID) == 0)) {
 
184
        ret = MM_K_TNV;
 
185
        goto exit;
 
186
    }
 
187
    if (unlikely((L2pte & PTE_KRE) == 0)) {
 
188
        goto exit;
 
189
    }
 
190
    pt = L2pte >> 32 << TARGET_PAGE_BITS;
 
191
 
 
192
    /* L3 page table read.  */
 
193
    index = (addr >> TARGET_PAGE_BITS) & 0x3ff;
 
194
    L3pte = ldq_phys(cs->as, pt + index*8);
 
195
 
 
196
    phys = L3pte >> 32 << TARGET_PAGE_BITS;
 
197
    if (unlikely((L3pte & PTE_VALID) == 0)) {
 
198
        ret = MM_K_TNV;
 
199
        goto exit;
 
200
    }
 
201
 
 
202
#if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
 
203
# error page bits out of date
 
204
#endif
 
205
 
 
206
    /* Check access violations.  */
 
207
    if (L3pte & (PTE_KRE << mmu_idx)) {
 
208
        prot |= PAGE_READ | PAGE_EXEC;
 
209
    }
 
210
    if (L3pte & (PTE_KWE << mmu_idx)) {
 
211
        prot |= PAGE_WRITE;
 
212
    }
 
213
    if (unlikely((prot & prot_need) == 0 && prot_need)) {
 
214
        goto exit;
 
215
    }
 
216
 
 
217
    /* Check fault-on-operation violations.  */
 
218
    prot &= ~(L3pte >> 1);
 
219
    ret = -1;
 
220
    if (unlikely((prot & prot_need) == 0)) {
 
221
        ret = (prot_need & PAGE_EXEC ? MM_K_FOE :
 
222
               prot_need & PAGE_WRITE ? MM_K_FOW :
 
223
               prot_need & PAGE_READ ? MM_K_FOR : -1);
 
224
    }
 
225
 
 
226
 exit:
 
227
    *pphys = phys;
 
228
    *pprot = prot;
 
229
    return ret;
 
230
}
 
231
 
 
232
hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 
233
{
 
234
    AlphaCPU *cpu = ALPHA_CPU(cs);
 
235
    target_ulong phys;
 
236
    int prot, fail;
 
237
 
 
238
    fail = get_physical_address(&cpu->env, addr, 0, 0, &phys, &prot);
 
239
    return (fail >= 0 ? -1 : phys);
 
240
}
 
241
 
 
242
int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int rw,
 
243
                               int mmu_idx)
 
244
{
 
245
    AlphaCPU *cpu = ALPHA_CPU(cs);
 
246
    CPUAlphaState *env = &cpu->env;
 
247
    target_ulong phys;
 
248
    int prot, fail;
 
249
 
 
250
    fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot);
 
251
    if (unlikely(fail >= 0)) {
 
252
        cs->exception_index = EXCP_MMFAULT;
 
253
        env->trap_arg0 = addr;
 
254
        env->trap_arg1 = fail;
 
255
        env->trap_arg2 = (rw == 2 ? -1 : rw);
 
256
        return 1;
 
257
    }
 
258
 
 
259
    tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
 
260
                 prot, mmu_idx, TARGET_PAGE_SIZE);
 
261
    return 0;
 
262
}
 
263
#endif /* USER_ONLY */
 
264
 
 
265
void alpha_cpu_do_interrupt(CPUState *cs)
 
266
{
 
267
    AlphaCPU *cpu = ALPHA_CPU(cs);
 
268
    CPUAlphaState *env = &cpu->env;
 
269
    int i = cs->exception_index;
 
270
 
 
271
    if (qemu_loglevel_mask(CPU_LOG_INT)) {
 
272
        static int count;
 
273
        const char *name = "<unknown>";
 
274
 
 
275
        switch (i) {
 
276
        case EXCP_RESET:
 
277
            name = "reset";
 
278
            break;
 
279
        case EXCP_MCHK:
 
280
            name = "mchk";
 
281
            break;
 
282
        case EXCP_SMP_INTERRUPT:
 
283
            name = "smp_interrupt";
 
284
            break;
 
285
        case EXCP_CLK_INTERRUPT:
 
286
            name = "clk_interrupt";
 
287
            break;
 
288
        case EXCP_DEV_INTERRUPT:
 
289
            name = "dev_interrupt";
 
290
            break;
 
291
        case EXCP_MMFAULT:
 
292
            name = "mmfault";
 
293
            break;
 
294
        case EXCP_UNALIGN:
 
295
            name = "unalign";
 
296
            break;
 
297
        case EXCP_OPCDEC:
 
298
            name = "opcdec";
 
299
            break;
 
300
        case EXCP_ARITH:
 
301
            name = "arith";
 
302
            break;
 
303
        case EXCP_FEN:
 
304
            name = "fen";
 
305
            break;
 
306
        case EXCP_CALL_PAL:
 
307
            name = "call_pal";
 
308
            break;
 
309
        }
 
310
        qemu_log("INT %6d: %s(%#x) cpu=%d pc=%016"
 
311
                 PRIx64 " sp=%016" PRIx64 "\n",
 
312
                 ++count, name, env->error_code, cs->cpu_index,
 
313
                 env->pc, env->ir[IR_SP]);
 
314
    }
 
315
 
 
316
    cs->exception_index = -1;
 
317
 
 
318
#if !defined(CONFIG_USER_ONLY)
 
319
    switch (i) {
 
320
    case EXCP_RESET:
 
321
        i = 0x0000;
 
322
        break;
 
323
    case EXCP_MCHK:
 
324
        i = 0x0080;
 
325
        break;
 
326
    case EXCP_SMP_INTERRUPT:
 
327
        i = 0x0100;
 
328
        break;
 
329
    case EXCP_CLK_INTERRUPT:
 
330
        i = 0x0180;
 
331
        break;
 
332
    case EXCP_DEV_INTERRUPT:
 
333
        i = 0x0200;
 
334
        break;
 
335
    case EXCP_MMFAULT:
 
336
        i = 0x0280;
 
337
        break;
 
338
    case EXCP_UNALIGN:
 
339
        i = 0x0300;
 
340
        break;
 
341
    case EXCP_OPCDEC:
 
342
        i = 0x0380;
 
343
        break;
 
344
    case EXCP_ARITH:
 
345
        i = 0x0400;
 
346
        break;
 
347
    case EXCP_FEN:
 
348
        i = 0x0480;
 
349
        break;
 
350
    case EXCP_CALL_PAL:
 
351
        i = env->error_code;
 
352
        /* There are 64 entry points for both privileged and unprivileged,
 
353
           with bit 0x80 indicating unprivileged.  Each entry point gets
 
354
           64 bytes to do its job.  */
 
355
        if (i & 0x80) {
 
356
            i = 0x2000 + (i - 0x80) * 64;
 
357
        } else {
 
358
            i = 0x1000 + i * 64;
 
359
        }
 
360
        break;
 
361
    default:
 
362
        cpu_abort(cs, "Unhandled CPU exception");
 
363
    }
 
364
 
 
365
    /* Remember where the exception happened.  Emulate real hardware in
 
366
       that the low bit of the PC indicates PALmode.  */
 
367
    env->exc_addr = env->pc | env->pal_mode;
 
368
 
 
369
    /* Continue execution at the PALcode entry point.  */
 
370
    env->pc = env->palbr + i;
 
371
 
 
372
    /* Switch to PALmode.  */
 
373
    env->pal_mode = 1;
 
374
#endif /* !USER_ONLY */
 
375
}
 
376
 
 
377
bool alpha_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 
378
{
 
379
    AlphaCPU *cpu = ALPHA_CPU(cs);
 
380
    CPUAlphaState *env = &cpu->env;
 
381
    int idx = -1;
 
382
 
 
383
    /* We never take interrupts while in PALmode.  */
 
384
    if (env->pal_mode) {
 
385
        return false;
 
386
    }
 
387
 
 
388
    /* Fall through the switch, collecting the highest priority
 
389
       interrupt that isn't masked by the processor status IPL.  */
 
390
    /* ??? This hard-codes the OSF/1 interrupt levels.  */
 
391
    switch (env->ps & PS_INT_MASK) {
 
392
    case 0 ... 3:
 
393
        if (interrupt_request & CPU_INTERRUPT_HARD) {
 
394
            idx = EXCP_DEV_INTERRUPT;
 
395
        }
 
396
        /* FALLTHRU */
 
397
    case 4:
 
398
        if (interrupt_request & CPU_INTERRUPT_TIMER) {
 
399
            idx = EXCP_CLK_INTERRUPT;
 
400
        }
 
401
        /* FALLTHRU */
 
402
    case 5:
 
403
        if (interrupt_request & CPU_INTERRUPT_SMP) {
 
404
            idx = EXCP_SMP_INTERRUPT;
 
405
        }
 
406
        /* FALLTHRU */
 
407
    case 6:
 
408
        if (interrupt_request & CPU_INTERRUPT_MCHK) {
 
409
            idx = EXCP_MCHK;
 
410
        }
 
411
    }
 
412
    if (idx >= 0) {
 
413
        cs->exception_index = idx;
 
414
        env->error_code = 0;
 
415
        alpha_cpu_do_interrupt(cs);
 
416
        return true;
 
417
    }
 
418
    return false;
 
419
}
 
420
 
 
421
void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
 
422
                          int flags)
 
423
{
 
424
    static const char *linux_reg_names[] = {
 
425
        "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
 
426
        "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
 
427
        "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
 
428
        "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
 
429
    };
 
430
    AlphaCPU *cpu = ALPHA_CPU(cs);
 
431
    CPUAlphaState *env = &cpu->env;
 
432
    int i;
 
433
 
 
434
    cpu_fprintf(f, "     PC  " TARGET_FMT_lx "      PS  %02x\n",
 
435
                env->pc, env->ps);
 
436
    for (i = 0; i < 31; i++) {
 
437
        cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
 
438
                    linux_reg_names[i], cpu_alpha_load_gr(env, i));
 
439
        if ((i % 3) == 2)
 
440
            cpu_fprintf(f, "\n");
 
441
    }
 
442
 
 
443
    cpu_fprintf(f, "lock_a   " TARGET_FMT_lx " lock_v   " TARGET_FMT_lx "\n",
 
444
                env->lock_addr, env->lock_value);
 
445
 
 
446
    for (i = 0; i < 31; i++) {
 
447
        cpu_fprintf(f, "FIR%02d    " TARGET_FMT_lx " ", i,
 
448
                    *((uint64_t *)(&env->fir[i])));
 
449
        if ((i % 3) == 2)
 
450
            cpu_fprintf(f, "\n");
 
451
    }
 
452
    cpu_fprintf(f, "\n");
 
453
}
 
454
 
 
455
/* This should only be called from translate, via gen_excp.
 
456
   We expect that ENV->PC has already been updated.  */
 
457
void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
 
458
{
 
459
    AlphaCPU *cpu = alpha_env_get_cpu(env);
 
460
    CPUState *cs = CPU(cpu);
 
461
 
 
462
    cs->exception_index = excp;
 
463
    env->error_code = error;
 
464
    cpu_loop_exit(cs);
 
465
}
 
466
 
 
467
/* This may be called from any of the helpers to set up EXCEPTION_INDEX.  */
 
468
void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
 
469
                                int excp, int error)
 
470
{
 
471
    AlphaCPU *cpu = alpha_env_get_cpu(env);
 
472
    CPUState *cs = CPU(cpu);
 
473
 
 
474
    cs->exception_index = excp;
 
475
    env->error_code = error;
 
476
    if (retaddr) {
 
477
        cpu_restore_state(cs, retaddr);
 
478
        /* Floating-point exceptions (our only users) point to the next PC.  */
 
479
        env->pc += 4;
 
480
    }
 
481
    cpu_loop_exit(cs);
 
482
}
 
483
 
 
484
void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
 
485
                              int exc, uint64_t mask)
 
486
{
 
487
    env->trap_arg0 = exc;
 
488
    env->trap_arg1 = mask;
 
489
    dynamic_excp(env, retaddr, EXCP_ARITH, 0);
 
490
}