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