qemu/target-alpha/helper.c
<<
>>
Prefs
   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 "fpu/softfloat.h"
  26#include "helper.h"
  27
  28uint64_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
  98void 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
 160uint64_t helper_load_fpcr(CPUAlphaState *env)
 161{
 162    return cpu_alpha_load_fpcr(env);
 163}
 164
 165void helper_store_fpcr(CPUAlphaState *env, uint64_t val)
 166{
 167    cpu_alpha_store_fpcr(env, val);
 168}
 169
 170#if defined(CONFIG_USER_ONLY)
 171int 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
 179void 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.  */
 212static 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
 318hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 319{
 320    AlphaCPU *cpu = ALPHA_CPU(cs);
 321    target_ulong phys;
 322    int prot, fail;
 323
 324    fail = get_physical_address(&cpu->env, addr, 0, 0, &phys, &prot);
 325    return (fail >= 0 ? -1 : phys);
 326}
 327
 328int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong addr, int rw,
 329                               int mmu_idx)
 330{
 331    target_ulong phys;
 332    int prot, fail;
 333
 334    fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot);
 335    if (unlikely(fail >= 0)) {
 336        env->exception_index = EXCP_MMFAULT;
 337        env->trap_arg0 = addr;
 338        env->trap_arg1 = fail;
 339        env->trap_arg2 = (rw == 2 ? -1 : rw);
 340        return 1;
 341    }
 342
 343    tlb_set_page(env, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
 344                 prot, mmu_idx, TARGET_PAGE_SIZE);
 345    return 0;
 346}
 347#endif /* USER_ONLY */
 348
 349void alpha_cpu_do_interrupt(CPUState *cs)
 350{
 351    AlphaCPU *cpu = ALPHA_CPU(cs);
 352    CPUAlphaState *env = &cpu->env;
 353    int i = env->exception_index;
 354
 355    if (qemu_loglevel_mask(CPU_LOG_INT)) {
 356        static int count;
 357        const char *name = "<unknown>";
 358
 359        switch (i) {
 360        case EXCP_RESET:
 361            name = "reset";
 362            break;
 363        case EXCP_MCHK:
 364            name = "mchk";
 365            break;
 366        case EXCP_SMP_INTERRUPT:
 367            name = "smp_interrupt";
 368            break;
 369        case EXCP_CLK_INTERRUPT:
 370            name = "clk_interrupt";
 371            break;
 372        case EXCP_DEV_INTERRUPT:
 373            name = "dev_interrupt";
 374            break;
 375        case EXCP_MMFAULT:
 376            name = "mmfault";
 377            break;
 378        case EXCP_UNALIGN:
 379            name = "unalign";
 380            break;
 381        case EXCP_OPCDEC:
 382            name = "opcdec";
 383            break;
 384        case EXCP_ARITH:
 385            name = "arith";
 386            break;
 387        case EXCP_FEN:
 388            name = "fen";
 389            break;
 390        case EXCP_CALL_PAL:
 391            name = "call_pal";
 392            break;
 393        case EXCP_STL_C:
 394            name = "stl_c";
 395            break;
 396        case EXCP_STQ_C:
 397            name = "stq_c";
 398            break;
 399        }
 400        qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n",
 401                 ++count, name, env->error_code, env->pc, env->ir[IR_SP]);
 402    }
 403
 404    env->exception_index = -1;
 405
 406#if !defined(CONFIG_USER_ONLY)
 407    switch (i) {
 408    case EXCP_RESET:
 409        i = 0x0000;
 410        break;
 411    case EXCP_MCHK:
 412        i = 0x0080;
 413        break;
 414    case EXCP_SMP_INTERRUPT:
 415        i = 0x0100;
 416        break;
 417    case EXCP_CLK_INTERRUPT:
 418        i = 0x0180;
 419        break;
 420    case EXCP_DEV_INTERRUPT:
 421        i = 0x0200;
 422        break;
 423    case EXCP_MMFAULT:
 424        i = 0x0280;
 425        break;
 426    case EXCP_UNALIGN:
 427        i = 0x0300;
 428        break;
 429    case EXCP_OPCDEC:
 430        i = 0x0380;
 431        break;
 432    case EXCP_ARITH:
 433        i = 0x0400;
 434        break;
 435    case EXCP_FEN:
 436        i = 0x0480;
 437        break;
 438    case EXCP_CALL_PAL:
 439        i = env->error_code;
 440        /* There are 64 entry points for both privileged and unprivileged,
 441           with bit 0x80 indicating unprivileged.  Each entry point gets
 442           64 bytes to do its job.  */
 443        if (i & 0x80) {
 444            i = 0x2000 + (i - 0x80) * 64;
 445        } else {
 446            i = 0x1000 + i * 64;
 447        }
 448        break;
 449    default:
 450        cpu_abort(env, "Unhandled CPU exception");
 451    }
 452
 453    /* Remember where the exception happened.  Emulate real hardware in
 454       that the low bit of the PC indicates PALmode.  */
 455    env->exc_addr = env->pc | env->pal_mode;
 456
 457    /* Continue execution at the PALcode entry point.  */
 458    env->pc = env->palbr + i;
 459
 460    /* Switch to PALmode.  */
 461    if (!env->pal_mode) {
 462        env->pal_mode = 1;
 463        swap_shadow_regs(env);
 464    }
 465#endif /* !USER_ONLY */
 466}
 467
 468void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
 469                          int flags)
 470{
 471    static const char *linux_reg_names[] = {
 472        "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
 473        "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
 474        "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
 475        "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
 476    };
 477    AlphaCPU *cpu = ALPHA_CPU(cs);
 478    CPUAlphaState *env = &cpu->env;
 479    int i;
 480
 481    cpu_fprintf(f, "     PC  " TARGET_FMT_lx "      PS  %02x\n",
 482                env->pc, env->ps);
 483    for (i = 0; i < 31; i++) {
 484        cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i,
 485                    linux_reg_names[i], env->ir[i]);
 486        if ((i % 3) == 2)
 487            cpu_fprintf(f, "\n");
 488    }
 489
 490    cpu_fprintf(f, "lock_a   " TARGET_FMT_lx " lock_v   " TARGET_FMT_lx "\n",
 491                env->lock_addr, env->lock_value);
 492
 493    for (i = 0; i < 31; i++) {
 494        cpu_fprintf(f, "FIR%02d    " TARGET_FMT_lx " ", i,
 495                    *((uint64_t *)(&env->fir[i])));
 496        if ((i % 3) == 2)
 497            cpu_fprintf(f, "\n");
 498    }
 499    cpu_fprintf(f, "\n");
 500}
 501
 502/* This should only be called from translate, via gen_excp.
 503   We expect that ENV->PC has already been updated.  */
 504void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
 505{
 506    env->exception_index = excp;
 507    env->error_code = error;
 508    cpu_loop_exit(env);
 509}
 510
 511/* This may be called from any of the helpers to set up EXCEPTION_INDEX.  */
 512void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
 513                                int excp, int error)
 514{
 515    env->exception_index = excp;
 516    env->error_code = error;
 517    if (retaddr) {
 518        cpu_restore_state(env, retaddr);
 519    }
 520    cpu_loop_exit(env);
 521}
 522
 523void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
 524                              int exc, uint64_t mask)
 525{
 526    env->trap_arg0 = exc;
 527    env->trap_arg1 = mask;
 528    dynamic_excp(env, retaddr, EXCP_ARITH, 0);
 529}
 530