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 "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
  31uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env)
  32{
  33    return (uint64_t)env->fpcr << 32;
  34}
  35
  36void 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
  71uint64_t helper_load_fpcr(CPUAlphaState *env)
  72{
  73    return cpu_alpha_load_fpcr(env);
  74}
  75
  76void helper_store_fpcr(CPUAlphaState *env, uint64_t val)
  77{
  78    cpu_alpha_store_fpcr(env, val);
  79}
  80
  81static uint64_t *cpu_alpha_addr_gr(CPUAlphaState *env, unsigned reg)
  82{
  83#ifndef CONFIG_USER_ONLY
  84    if (env->flags & ENV_FLAG_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
  95uint64_t cpu_alpha_load_gr(CPUAlphaState *env, unsigned reg)
  96{
  97    return *cpu_alpha_addr_gr(env, reg);
  98}
  99
 100void 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)
 106int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
 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.  */
 117static 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    /* TODO: rather than using ldq_phys() to read the page table we should
 167     * use address_space_ldq() so that we can handle the case when
 168     * the page table read gives a bus fault, rather than ignoring it.
 169     * For the existing code the zero data that ldq_phys will return for
 170     * an access to invalid memory will result in our treating the page
 171     * table as invalid, which may even be the right behaviour.
 172     */
 173
 174    /* L1 page table read.  */
 175    index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff;
 176    L1pte = ldq_phys(cs->as, pt + index*8);
 177
 178    if (unlikely((L1pte & PTE_VALID) == 0)) {
 179        ret = MM_K_TNV;
 180        goto exit;
 181    }
 182    if (unlikely((L1pte & PTE_KRE) == 0)) {
 183        goto exit;
 184    }
 185    pt = L1pte >> 32 << TARGET_PAGE_BITS;
 186
 187    /* L2 page table read.  */
 188    index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff;
 189    L2pte = ldq_phys(cs->as, pt + index*8);
 190
 191    if (unlikely((L2pte & PTE_VALID) == 0)) {
 192        ret = MM_K_TNV;
 193        goto exit;
 194    }
 195    if (unlikely((L2pte & PTE_KRE) == 0)) {
 196        goto exit;
 197    }
 198    pt = L2pte >> 32 << TARGET_PAGE_BITS;
 199
 200    /* L3 page table read.  */
 201    index = (addr >> TARGET_PAGE_BITS) & 0x3ff;
 202    L3pte = ldq_phys(cs->as, pt + index*8);
 203
 204    phys = L3pte >> 32 << TARGET_PAGE_BITS;
 205    if (unlikely((L3pte & PTE_VALID) == 0)) {
 206        ret = MM_K_TNV;
 207        goto exit;
 208    }
 209
 210#if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4
 211# error page bits out of date
 212#endif
 213
 214    /* Check access violations.  */
 215    if (L3pte & (PTE_KRE << mmu_idx)) {
 216        prot |= PAGE_READ | PAGE_EXEC;
 217    }
 218    if (L3pte & (PTE_KWE << mmu_idx)) {
 219        prot |= PAGE_WRITE;
 220    }
 221    if (unlikely((prot & prot_need) == 0 && prot_need)) {
 222        goto exit;
 223    }
 224
 225    /* Check fault-on-operation violations.  */
 226    prot &= ~(L3pte >> 1);
 227    ret = -1;
 228    if (unlikely((prot & prot_need) == 0)) {
 229        ret = (prot_need & PAGE_EXEC ? MM_K_FOE :
 230               prot_need & PAGE_WRITE ? MM_K_FOW :
 231               prot_need & PAGE_READ ? MM_K_FOR : -1);
 232    }
 233
 234 exit:
 235    *pphys = phys;
 236    *pprot = prot;
 237    return ret;
 238}
 239
 240hwaddr alpha_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
 241{
 242    AlphaCPU *cpu = ALPHA_CPU(cs);
 243    target_ulong phys;
 244    int prot, fail;
 245
 246    fail = get_physical_address(&cpu->env, addr, 0, 0, &phys, &prot);
 247    return (fail >= 0 ? -1 : phys);
 248}
 249
 250int alpha_cpu_handle_mmu_fault(CPUState *cs, vaddr addr, int size, int rw,
 251                               int mmu_idx)
 252{
 253    AlphaCPU *cpu = ALPHA_CPU(cs);
 254    CPUAlphaState *env = &cpu->env;
 255    target_ulong phys;
 256    int prot, fail;
 257
 258    fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot);
 259    if (unlikely(fail >= 0)) {
 260        cs->exception_index = EXCP_MMFAULT;
 261        env->trap_arg0 = addr;
 262        env->trap_arg1 = fail;
 263        env->trap_arg2 = (rw == 2 ? -1 : rw);
 264        return 1;
 265    }
 266
 267    tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
 268                 prot, mmu_idx, TARGET_PAGE_SIZE);
 269    return 0;
 270}
 271#endif /* USER_ONLY */
 272
 273void alpha_cpu_do_interrupt(CPUState *cs)
 274{
 275    AlphaCPU *cpu = ALPHA_CPU(cs);
 276    CPUAlphaState *env = &cpu->env;
 277    int i = cs->exception_index;
 278
 279    if (qemu_loglevel_mask(CPU_LOG_INT)) {
 280        static int count;
 281        const char *name = "<unknown>";
 282
 283        switch (i) {
 284        case EXCP_RESET:
 285            name = "reset";
 286            break;
 287        case EXCP_MCHK:
 288            name = "mchk";
 289            break;
 290        case EXCP_SMP_INTERRUPT:
 291            name = "smp_interrupt";
 292            break;
 293        case EXCP_CLK_INTERRUPT:
 294            name = "clk_interrupt";
 295            break;
 296        case EXCP_DEV_INTERRUPT:
 297            name = "dev_interrupt";
 298            break;
 299        case EXCP_MMFAULT:
 300            name = "mmfault";
 301            break;
 302        case EXCP_UNALIGN:
 303            name = "unalign";
 304            break;
 305        case EXCP_OPCDEC:
 306            name = "opcdec";
 307            break;
 308        case EXCP_ARITH:
 309            name = "arith";
 310            break;
 311        case EXCP_FEN:
 312            name = "fen";
 313            break;
 314        case EXCP_CALL_PAL:
 315            name = "call_pal";
 316            break;
 317        }
 318        qemu_log("INT %6d: %s(%#x) cpu=%d pc=%016"
 319                 PRIx64 " sp=%016" PRIx64 "\n",
 320                 ++count, name, env->error_code, cs->cpu_index,
 321                 env->pc, env->ir[IR_SP]);
 322    }
 323
 324    cs->exception_index = -1;
 325
 326#if !defined(CONFIG_USER_ONLY)
 327    switch (i) {
 328    case EXCP_RESET:
 329        i = 0x0000;
 330        break;
 331    case EXCP_MCHK:
 332        i = 0x0080;
 333        break;
 334    case EXCP_SMP_INTERRUPT:
 335        i = 0x0100;
 336        break;
 337    case EXCP_CLK_INTERRUPT:
 338        i = 0x0180;
 339        break;
 340    case EXCP_DEV_INTERRUPT:
 341        i = 0x0200;
 342        break;
 343    case EXCP_MMFAULT:
 344        i = 0x0280;
 345        break;
 346    case EXCP_UNALIGN:
 347        i = 0x0300;
 348        break;
 349    case EXCP_OPCDEC:
 350        i = 0x0380;
 351        break;
 352    case EXCP_ARITH:
 353        i = 0x0400;
 354        break;
 355    case EXCP_FEN:
 356        i = 0x0480;
 357        break;
 358    case EXCP_CALL_PAL:
 359        i = env->error_code;
 360        /* There are 64 entry points for both privileged and unprivileged,
 361           with bit 0x80 indicating unprivileged.  Each entry point gets
 362           64 bytes to do its job.  */
 363        if (i & 0x80) {
 364            i = 0x2000 + (i - 0x80) * 64;
 365        } else {
 366            i = 0x1000 + i * 64;
 367        }
 368        break;
 369    default:
 370        cpu_abort(cs, "Unhandled CPU exception");
 371    }
 372
 373    /* Remember where the exception happened.  Emulate real hardware in
 374       that the low bit of the PC indicates PALmode.  */
 375    env->exc_addr = env->pc | (env->flags & ENV_FLAG_PAL_MODE);
 376
 377    /* Continue execution at the PALcode entry point.  */
 378    env->pc = env->palbr + i;
 379
 380    /* Switch to PALmode.  */
 381    env->flags |= ENV_FLAG_PAL_MODE;
 382#endif /* !USER_ONLY */
 383}
 384
 385bool alpha_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
 386{
 387    AlphaCPU *cpu = ALPHA_CPU(cs);
 388    CPUAlphaState *env = &cpu->env;
 389    int idx = -1;
 390
 391    /* We never take interrupts while in PALmode.  */
 392    if (env->flags & ENV_FLAG_PAL_MODE) {
 393        return false;
 394    }
 395
 396    /* Fall through the switch, collecting the highest priority
 397       interrupt that isn't masked by the processor status IPL.  */
 398    /* ??? This hard-codes the OSF/1 interrupt levels.  */
 399    switch ((env->flags >> ENV_FLAG_PS_SHIFT) & PS_INT_MASK) {
 400    case 0 ... 3:
 401        if (interrupt_request & CPU_INTERRUPT_HARD) {
 402            idx = EXCP_DEV_INTERRUPT;
 403        }
 404        /* FALLTHRU */
 405    case 4:
 406        if (interrupt_request & CPU_INTERRUPT_TIMER) {
 407            idx = EXCP_CLK_INTERRUPT;
 408        }
 409        /* FALLTHRU */
 410    case 5:
 411        if (interrupt_request & CPU_INTERRUPT_SMP) {
 412            idx = EXCP_SMP_INTERRUPT;
 413        }
 414        /* FALLTHRU */
 415    case 6:
 416        if (interrupt_request & CPU_INTERRUPT_MCHK) {
 417            idx = EXCP_MCHK;
 418        }
 419    }
 420    if (idx >= 0) {
 421        cs->exception_index = idx;
 422        env->error_code = 0;
 423        alpha_cpu_do_interrupt(cs);
 424        return true;
 425    }
 426    return false;
 427}
 428
 429void alpha_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
 430                          int flags)
 431{
 432    static const char *linux_reg_names[] = {
 433        "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ",
 434        "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ",
 435        "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ",
 436        "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero",
 437    };
 438    AlphaCPU *cpu = ALPHA_CPU(cs);
 439    CPUAlphaState *env = &cpu->env;
 440    int i;
 441
 442    cpu_fprintf(f, "     PC  " TARGET_FMT_lx "      PS  %02x\n",
 443                env->pc, extract32(env->flags, ENV_FLAG_PS_SHIFT, 8));
 444    for (i = 0; i < 31; i++) {
 445        cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx "%c", i,
 446                    linux_reg_names[i], cpu_alpha_load_gr(env, i),
 447                    (i % 3) == 2 ? '\n' : ' ');
 448    }
 449
 450    cpu_fprintf(f, "lock_a   " TARGET_FMT_lx " lock_v   " TARGET_FMT_lx "\n",
 451                env->lock_addr, env->lock_value);
 452
 453    if (flags & CPU_DUMP_FPU) {
 454        for (i = 0; i < 31; i++) {
 455            cpu_fprintf(f, "FIR%02d    %016" PRIx64 "%c", i, env->fir[i],
 456                        (i % 3) == 2 ? '\n' : ' ');
 457        }
 458    }
 459    cpu_fprintf(f, "\n");
 460}
 461
 462/* This should only be called from translate, via gen_excp.
 463   We expect that ENV->PC has already been updated.  */
 464void QEMU_NORETURN helper_excp(CPUAlphaState *env, 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    cpu_loop_exit(cs);
 472}
 473
 474/* This may be called from any of the helpers to set up EXCEPTION_INDEX.  */
 475void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr,
 476                                int excp, int error)
 477{
 478    AlphaCPU *cpu = alpha_env_get_cpu(env);
 479    CPUState *cs = CPU(cpu);
 480
 481    cs->exception_index = excp;
 482    env->error_code = error;
 483    if (retaddr) {
 484        cpu_restore_state(cs, retaddr, true);
 485        /* Floating-point exceptions (our only users) point to the next PC.  */
 486        env->pc += 4;
 487    }
 488    cpu_loop_exit(cs);
 489}
 490
 491void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr,
 492                              int exc, uint64_t mask)
 493{
 494    env->trap_arg0 = exc;
 495    env->trap_arg1 = mask;
 496    dynamic_excp(env, retaddr, EXCP_ARITH, 0);
 497}
 498