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 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
 327int 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
 348void 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
 465void 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/* This should only be called from translate, via gen_excp.
 498   We expect that ENV->PC has already been updated.  */
 499void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error)
 500{
 501    env->exception_index = excp;
 502    env->error_code = error;
 503    cpu_loop_exit(env);
 504}
 505
 506/* This may be called from any of the helpers to set up EXCEPTION_INDEX.  */
 507void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
 508                                int excp, int error)
 509{
 510    env->exception_index = excp;
 511    env->error_code = error;
 512    if (retaddr) {
 513        cpu_restore_state(env, retaddr);
 514    }
 515    cpu_loop_exit(env);
 516}
 517
 518void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
 519                              int exc, uint64_t mask)
 520{
 521    env->trap_arg0 = exc;
 522    env->trap_arg1 = mask;
 523    dynamic_excp(env, retaddr, EXCP_ARITH, 0);
 524}
 525