cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

helper.c (15319B)


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