Statistics
| Branch: | Revision:

root / target-alpha / helper.c @ a8170e5e

History | View | Annotate | Download (13.5 kB)

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 <stdint.h>
21
#include <stdlib.h>
22
#include <stdio.h>
23

    
24
#include "cpu.h"
25
#include "softfloat.h"
26
#include "helper.h"
27

    
28
uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env)
29
{
30
    uint64_t r = 0;
31
    uint8_t t;
32

    
33
    t = env->fpcr_exc_status;
34
    if (t) {
35
        r = FPCR_SUM;
36
        if (t & float_flag_invalid) {
37
            r |= FPCR_INV;
38
        }
39
        if (t & float_flag_divbyzero) {
40
            r |= FPCR_DZE;
41
        }
42
        if (t & float_flag_overflow) {
43
            r |= FPCR_OVF;
44
        }
45
        if (t & float_flag_underflow) {
46
            r |= FPCR_UNF;
47
        }
48
        if (t & float_flag_inexact) {
49
            r |= FPCR_INE;
50
        }
51
    }
52

    
53
    t = env->fpcr_exc_mask;
54
    if (t & float_flag_invalid) {
55
        r |= FPCR_INVD;
56
    }
57
    if (t & float_flag_divbyzero) {
58
        r |= FPCR_DZED;
59
    }
60
    if (t & float_flag_overflow) {
61
        r |= FPCR_OVFD;
62
    }
63
    if (t & float_flag_underflow) {
64
        r |= FPCR_UNFD;
65
    }
66
    if (t & float_flag_inexact) {
67
        r |= FPCR_INED;
68
    }
69

    
70
    switch (env->fpcr_dyn_round) {
71
    case float_round_nearest_even:
72
        r |= FPCR_DYN_NORMAL;
73
        break;
74
    case float_round_down:
75
        r |= FPCR_DYN_MINUS;
76
        break;
77
    case float_round_up:
78
        r |= FPCR_DYN_PLUS;
79
        break;
80
    case float_round_to_zero:
81
        r |= FPCR_DYN_CHOPPED;
82
        break;
83
    }
84

    
85
    if (env->fp_status.flush_inputs_to_zero) {
86
        r |= FPCR_DNZ;
87
    }
88
    if (env->fpcr_dnod) {
89
        r |= FPCR_DNOD;
90
    }
91
    if (env->fpcr_undz) {
92
        r |= FPCR_UNDZ;
93
    }
94

    
95
    return r;
96
}
97

    
98
void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val)
99
{
100
    uint8_t t;
101

    
102
    t = 0;
103
    if (val & FPCR_INV) {
104
        t |= float_flag_invalid;
105
    }
106
    if (val & FPCR_DZE) {
107
        t |= float_flag_divbyzero;
108
    }
109
    if (val & FPCR_OVF) {
110
        t |= float_flag_overflow;
111
    }
112
    if (val & FPCR_UNF) {
113
        t |= float_flag_underflow;
114
    }
115
    if (val & FPCR_INE) {
116
        t |= float_flag_inexact;
117
    }
118
    env->fpcr_exc_status = t;
119

    
120
    t = 0;
121
    if (val & FPCR_INVD) {
122
        t |= float_flag_invalid;
123
    }
124
    if (val & FPCR_DZED) {
125
        t |= float_flag_divbyzero;
126
    }
127
    if (val & FPCR_OVFD) {
128
        t |= float_flag_overflow;
129
    }
130
    if (val & FPCR_UNFD) {
131
        t |= float_flag_underflow;
132
    }
133
    if (val & FPCR_INED) {
134
        t |= float_flag_inexact;
135
    }
136
    env->fpcr_exc_mask = t;
137

    
138
    switch (val & FPCR_DYN_MASK) {
139
    case FPCR_DYN_CHOPPED:
140
        t = float_round_to_zero;
141
        break;
142
    case FPCR_DYN_MINUS:
143
        t = float_round_down;
144
        break;
145
    case FPCR_DYN_NORMAL:
146
        t = float_round_nearest_even;
147
        break;
148
    case FPCR_DYN_PLUS:
149
        t = float_round_up;
150
        break;
151
    }
152
    env->fpcr_dyn_round = t;
153

    
154
    env->fpcr_dnod = (val & FPCR_DNOD) != 0;
155
    env->fpcr_undz = (val & FPCR_UNDZ) != 0;
156
    env->fpcr_flush_to_zero = env->fpcr_dnod & env->fpcr_undz;
157
    env->fp_status.flush_inputs_to_zero = (val & FPCR_DNZ) != 0;
158
}
159

    
160
uint64_t helper_load_fpcr(CPUAlphaState *env)
161
{
162
    return cpu_alpha_load_fpcr(env);
163
}
164

    
165
void helper_store_fpcr(CPUAlphaState *env, uint64_t val)
166
{
167
    cpu_alpha_store_fpcr(env, val);
168
}
169

    
170
#if defined(CONFIG_USER_ONLY)
171
int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong address,
172
                               int rw, int mmu_idx)
173
{
174
    env->exception_index = EXCP_MMFAULT;
175
    env->trap_arg0 = address;
176
    return 1;
177
}
178
#else
179
void swap_shadow_regs(CPUAlphaState *env)
180
{
181
    uint64_t i0, i1, i2, i3, i4, i5, i6, i7;
182

    
183
    i0 = env->ir[8];
184
    i1 = env->ir[9];
185
    i2 = env->ir[10];
186
    i3 = env->ir[11];
187
    i4 = env->ir[12];
188
    i5 = env->ir[13];
189
    i6 = env->ir[14];
190
    i7 = env->ir[25];
191

    
192
    env->ir[8]  = env->shadow[0];
193
    env->ir[9]  = env->shadow[1];
194
    env->ir[10] = env->shadow[2];
195
    env->ir[11] = env->shadow[3];
196
    env->ir[12] = env->shadow[4];
197
    env->ir[13] = env->shadow[5];
198
    env->ir[14] = env->shadow[6];
199
    env->ir[25] = env->shadow[7];
200

    
201
    env->shadow[0] = i0;
202
    env->shadow[1] = i1;
203
    env->shadow[2] = i2;
204
    env->shadow[3] = i3;
205
    env->shadow[4] = i4;
206
    env->shadow[5] = i5;
207
    env->shadow[6] = i6;
208
    env->shadow[7] = i7;
209
}
210

    
211
/* Returns the OSF/1 entMM failure indication, or -1 on success.  */
212
static int get_physical_address(CPUAlphaState *env, target_ulong addr,
213
                                int prot_need, int mmu_idx,
214
                                target_ulong *pphys, int *pprot)
215
{
216
    target_long saddr = addr;
217
    target_ulong phys = 0;
218
    target_ulong L1pte, L2pte, L3pte;
219
    target_ulong pt, index;
220
    int prot = 0;
221
    int ret = MM_K_ACV;
222

    
223
    /* Ensure that the virtual address is properly sign-extended from
224
       the last implemented virtual address bit.  */
225
    if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) {
226
        goto exit;
227
    }
228

    
229
    /* Translate the superpage.  */
230
    /* ??? When we do more than emulate Unix PALcode, we'll need to
231
       determine which KSEG is actually active.  */
232
    if (saddr < 0 && ((saddr >> 41) & 3) == 2) {
233
        /* User-space cannot access KSEG addresses.  */
234
        if (mmu_idx != MMU_KERNEL_IDX) {
235
            goto exit;
236
        }
237

    
238
        /* For the benefit of the Typhoon chipset, move bit 40 to bit 43.
239
           We would not do this if the 48-bit KSEG is enabled.  */
240
        phys = saddr & ((1ull << 40) - 1);
241
        phys |= (saddr & (1ull << 40)) << 3;
242

    
243
        prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
244
        ret = -1;
245
        goto exit;
246
    }
247

    
248
    /* Interpret the page table exactly like PALcode does.  */
249

    
250
    pt = env->ptbr;
251

    
252
    /* L1 page table read.  */
253
    index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff;
254
    L1pte = ldq_phys(pt + index*8);
255

    
256
    if (unlikely((L1pte & PTE_VALID) == 0)) {
257
        ret = MM_K_TNV;
258
        goto exit;
259
    }
260
    if (unlikely((L1pte & PTE_KRE) == 0)) {
261
        goto exit;
262
    }
263
    pt = L1pte >> 32 << TARGET_PAGE_BITS;
264

    
265
    /* L2 page table read.  */
266
    index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff;
267
    L2pte = ldq_phys(pt + index*8);
268

    
269
    if (unlikely((L2pte & PTE_VALID) == 0)) {
270
        ret = MM_K_TNV;
271
        goto exit;
272
    }
273
    if (unlikely((L2pte & PTE_KRE) == 0)) {
274
        goto exit;
275
    }
276
    pt = L2pte >> 32 << TARGET_PAGE_BITS;
277

    
278
    /* L3 page table read.  */
279
    index = (addr >> TARGET_PAGE_BITS) & 0x3ff;
280
    L3pte = ldq_phys(pt + index*8);
281

    
282
    phys = L3pte >> 32 << TARGET_PAGE_BITS;
283
    if (unlikely((L3pte & PTE_VALID) == 0)) {
284
        ret = MM_K_TNV;
285
        goto exit;
286
    }
287

    
288
#if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
289
# error page bits out of date
290
#endif
291

    
292
    /* Check access violations.  */
293
    if (L3pte & (PTE_KRE << mmu_idx)) {
294
        prot |= PAGE_READ | PAGE_EXEC;
295
    }
296
    if (L3pte & (PTE_KWE << mmu_idx)) {
297
        prot |= PAGE_WRITE;
298
    }
299
    if (unlikely((prot & prot_need) == 0 && prot_need)) {
300
        goto exit;
301
    }
302

    
303
    /* Check fault-on-operation violations.  */
304
    prot &= ~(L3pte >> 1);
305
    ret = -1;
306
    if (unlikely((prot & prot_need) == 0)) {
307
        ret = (prot_need & PAGE_EXEC ? MM_K_FOE :
308
               prot_need & PAGE_WRITE ? MM_K_FOW :
309
               prot_need & PAGE_READ ? MM_K_FOR : -1);
310
    }
311

    
312
 exit:
313
    *pphys = phys;
314
    *pprot = prot;
315
    return ret;
316
}
317

    
318
hwaddr cpu_get_phys_page_debug(CPUAlphaState *env, target_ulong addr)
319
{
320
    target_ulong phys;
321
    int prot, fail;
322

    
323
    fail = get_physical_address(env, addr, 0, 0, &phys, &prot);
324
    return (fail >= 0 ? -1 : phys);
325
}
326

    
327
int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong addr, int rw,
328
                               int mmu_idx)
329
{
330
    target_ulong phys;
331
    int prot, fail;
332

    
333
    fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot);
334
    if (unlikely(fail >= 0)) {
335
        env->exception_index = EXCP_MMFAULT;
336
        env->trap_arg0 = addr;
337
        env->trap_arg1 = fail;
338
        env->trap_arg2 = (rw == 2 ? -1 : rw);
339
        return 1;
340
    }
341

    
342
    tlb_set_page(env, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
343
                 prot, mmu_idx, TARGET_PAGE_SIZE);
344
    return 0;
345
}
346
#endif /* USER_ONLY */
347

    
348
void do_interrupt (CPUAlphaState *env)
349
{
350
    int i = env->exception_index;
351

    
352
    if (qemu_loglevel_mask(CPU_LOG_INT)) {
353
        static int count;
354
        const char *name = "<unknown>";
355

    
356
        switch (i) {
357
        case EXCP_RESET:
358
            name = "reset";
359
            break;
360
        case EXCP_MCHK:
361
            name = "mchk";
362
            break;
363
        case EXCP_SMP_INTERRUPT:
364
            name = "smp_interrupt";
365
            break;
366
        case EXCP_CLK_INTERRUPT:
367
            name = "clk_interrupt";
368
            break;
369
        case EXCP_DEV_INTERRUPT:
370
            name = "dev_interrupt";
371
            break;
372
        case EXCP_MMFAULT:
373
            name = "mmfault";
374
            break;
375
        case EXCP_UNALIGN:
376
            name = "unalign";
377
            break;
378
        case EXCP_OPCDEC:
379
            name = "opcdec";
380
            break;
381
        case EXCP_ARITH:
382
            name = "arith";
383
            break;
384
        case EXCP_FEN:
385
            name = "fen";
386
            break;
387
        case EXCP_CALL_PAL:
388
            name = "call_pal";
389
            break;
390
        case EXCP_STL_C:
391
            name = "stl_c";
392
            break;
393
        case EXCP_STQ_C:
394
            name = "stq_c";
395
            break;
396
        }
397
        qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n",
398
                 ++count, name, env->error_code, env->pc, env->ir[IR_SP]);
399
    }
400

    
401
    env->exception_index = -1;
402

    
403
#if !defined(CONFIG_USER_ONLY)
404
    switch (i) {
405
    case EXCP_RESET:
406
        i = 0x0000;
407
        break;
408
    case EXCP_MCHK:
409
        i = 0x0080;
410
        break;
411
    case EXCP_SMP_INTERRUPT:
412
        i = 0x0100;
413
        break;
414
    case EXCP_CLK_INTERRUPT:
415
        i = 0x0180;
416
        break;
417
    case EXCP_DEV_INTERRUPT:
418
        i = 0x0200;
419
        break;
420
    case EXCP_MMFAULT:
421
        i = 0x0280;
422
        break;
423
    case EXCP_UNALIGN:
424
        i = 0x0300;
425
        break;
426
    case EXCP_OPCDEC:
427
        i = 0x0380;
428
        break;
429
    case EXCP_ARITH:
430
        i = 0x0400;
431
        break;
432
    case EXCP_FEN:
433
        i = 0x0480;
434
        break;
435
    case EXCP_CALL_PAL:
436
        i = env->error_code;
437
        /* There are 64 entry points for both privileged and unprivileged,
438
           with bit 0x80 indicating unprivileged.  Each entry point gets
439
           64 bytes to do its job.  */
440
        if (i & 0x80) {
441
            i = 0x2000 + (i - 0x80) * 64;
442
        } else {
443
            i = 0x1000 + i * 64;
444
        }
445
        break;
446
    default:
447
        cpu_abort(env, "Unhandled CPU exception");
448
    }
449

    
450
    /* Remember where the exception happened.  Emulate real hardware in
451
       that the low bit of the PC indicates PALmode.  */
452
    env->exc_addr = env->pc | env->pal_mode;
453

    
454
    /* Continue execution at the PALcode entry point.  */
455
    env->pc = env->palbr + i;
456

    
457
    /* Switch to PALmode.  */
458
    if (!env->pal_mode) {
459
        env->pal_mode = 1;
460
        swap_shadow_regs(env);
461
    }
462
#endif /* !USER_ONLY */
463
}
464

    
465
void cpu_dump_state (CPUAlphaState *env, FILE *f, fprintf_function cpu_fprintf,
466
                     int flags)
467
{
468
    static const char *linux_reg_names[] = {
469
        "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
470
        "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
471
        "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
472
        "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
473
    };
474
    int i;
475

    
476
    cpu_fprintf(f, "     PC  " TARGET_FMT_lx "      PS  %02x\n",
477
                env->pc, env->ps);
478
    for (i = 0; i < 31; i++) {
479
        cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
480
                    linux_reg_names[i], env->ir[i]);
481
        if ((i % 3) == 2)
482
            cpu_fprintf(f, "\n");
483
    }
484

    
485
    cpu_fprintf(f, "lock_a   " TARGET_FMT_lx " lock_v   " TARGET_FMT_lx "\n",
486
                env->lock_addr, env->lock_value);
487

    
488
    for (i = 0; i < 31; i++) {
489
        cpu_fprintf(f, "FIR%02d    " TARGET_FMT_lx " ", i,
490
                    *((uint64_t *)(&env->fir[i])));
491
        if ((i % 3) == 2)
492
            cpu_fprintf(f, "\n");
493
    }
494
    cpu_fprintf(f, "\n");
495
}
496

    
497
void do_restore_state(CPUAlphaState *env, uintptr_t retaddr)
498
{
499
    if (retaddr) {
500
        TranslationBlock *tb = tb_find_pc(retaddr);
501
        if (tb) {
502
            cpu_restore_state(tb, env, retaddr);
503
        }
504
    }
505
}
506

    
507
/* This should only be called from translate, via gen_excp.
508
   We expect that ENV->PC has already been updated.  */
509
void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
510
{
511
    env->exception_index = excp;
512
    env->error_code = error;
513
    cpu_loop_exit(env);
514
}
515

    
516
/* This may be called from any of the helpers to set up EXCEPTION_INDEX.  */
517
void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
518
                                int excp, int error)
519
{
520
    env->exception_index = excp;
521
    env->error_code = error;
522
    do_restore_state(env, retaddr);
523
    cpu_loop_exit(env);
524
}
525

    
526
void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
527
                              int exc, uint64_t mask)
528
{
529
    env->trap_arg0 = exc;
530
    env->trap_arg1 = mask;
531
    dynamic_excp(env, retaddr, EXCP_ARITH, 0);
532
}