diff options
Diffstat (limited to 'target-microblaze')
| -rw-r--r-- | target-microblaze/Makefile.objs | 3 | ||||
| -rw-r--r-- | target-microblaze/cpu-qom.h | 94 | ||||
| -rw-r--r-- | target-microblaze/cpu.c | 289 | ||||
| -rw-r--r-- | target-microblaze/cpu.h | 345 | ||||
| -rw-r--r-- | target-microblaze/gdbstub.c | 56 | ||||
| -rw-r--r-- | target-microblaze/helper.c | 304 | ||||
| -rw-r--r-- | target-microblaze/helper.h | 37 | ||||
| -rw-r--r-- | target-microblaze/microblaze-decode.h | 55 | ||||
| -rw-r--r-- | target-microblaze/mmu.c | 301 | ||||
| -rw-r--r-- | target-microblaze/mmu.h | 90 | ||||
| -rw-r--r-- | target-microblaze/op_helper.c | 522 | ||||
| -rw-r--r-- | target-microblaze/translate.c | 1957 | 
12 files changed, 4053 insertions, 0 deletions
diff --git a/target-microblaze/Makefile.objs b/target-microblaze/Makefile.objs new file mode 100644 index 00000000..f3d7b44c --- /dev/null +++ b/target-microblaze/Makefile.objs @@ -0,0 +1,3 @@ +obj-y += translate.o op_helper.o helper.o cpu.o +obj-y += gdbstub.o +obj-$(CONFIG_SOFTMMU) += mmu.o diff --git a/target-microblaze/cpu-qom.h b/target-microblaze/cpu-qom.h new file mode 100644 index 00000000..34f6273a --- /dev/null +++ b/target-microblaze/cpu-qom.h @@ -0,0 +1,94 @@ +/* + * QEMU MicroBlaze CPU + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ +#ifndef QEMU_MICROBLAZE_CPU_QOM_H +#define QEMU_MICROBLAZE_CPU_QOM_H + +#include "qom/cpu.h" + +#define TYPE_MICROBLAZE_CPU "microblaze-cpu" + +#define MICROBLAZE_CPU_CLASS(klass) \ +    OBJECT_CLASS_CHECK(MicroBlazeCPUClass, (klass), TYPE_MICROBLAZE_CPU) +#define MICROBLAZE_CPU(obj) \ +    OBJECT_CHECK(MicroBlazeCPU, (obj), TYPE_MICROBLAZE_CPU) +#define MICROBLAZE_CPU_GET_CLASS(obj) \ +    OBJECT_GET_CLASS(MicroBlazeCPUClass, (obj), TYPE_MICROBLAZE_CPU) + +/** + * MicroBlazeCPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * A MicroBlaze CPU model. + */ +typedef struct MicroBlazeCPUClass { +    /*< private >*/ +    CPUClass parent_class; +    /*< public >*/ + +    DeviceRealize parent_realize; +    void (*parent_reset)(CPUState *cpu); +} MicroBlazeCPUClass; + +/** + * MicroBlazeCPU: + * @env: #CPUMBState + * + * A MicroBlaze CPU. + */ +typedef struct MicroBlazeCPU { +    /*< private >*/ +    CPUState parent_obj; + +    /*< public >*/ + +    /* Microblaze Configuration Settings */ +    struct { +        bool stackprot; +        uint32_t base_vectors; +        uint8_t use_fpu; +        bool use_mmu; +        bool dcache_writeback; +        bool endi; +        char *version; +        uint8_t pvr; +    } cfg; + +    CPUMBState env; +} MicroBlazeCPU; + +static inline MicroBlazeCPU *mb_env_get_cpu(CPUMBState *env) +{ +    return container_of(env, MicroBlazeCPU, env); +} + +#define ENV_GET_CPU(e) CPU(mb_env_get_cpu(e)) + +#define ENV_OFFSET offsetof(MicroBlazeCPU, env) + +void mb_cpu_do_interrupt(CPUState *cs); +bool mb_cpu_exec_interrupt(CPUState *cs, int int_req); +void mb_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, +                       int flags); +hwaddr mb_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +int mb_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int mb_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); + +#endif diff --git a/target-microblaze/cpu.c b/target-microblaze/cpu.c new file mode 100644 index 00000000..15b46f76 --- /dev/null +++ b/target-microblaze/cpu.c @@ -0,0 +1,289 @@ +/* + * QEMU MicroBlaze CPU + * + * Copyright (c) 2009 Edgar E. Iglesias + * Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. + * Copyright (c) 2012 SUSE LINUX Products GmbH + * Copyright (c) 2009 Edgar E. Iglesias, Axis Communications AB. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "cpu.h" +#include "qemu-common.h" +#include "hw/qdev-properties.h" +#include "migration/vmstate.h" + +static const struct { +    const char *name; +    uint8_t version_id; +} mb_cpu_lookup[] = { +    /* These key value are as per MBV field in PVR0 */ +    {"5.00.a", 0x01}, +    {"5.00.b", 0x02}, +    {"5.00.c", 0x03}, +    {"6.00.a", 0x04}, +    {"6.00.b", 0x06}, +    {"7.00.a", 0x05}, +    {"7.00.b", 0x07}, +    {"7.10.a", 0x08}, +    {"7.10.b", 0x09}, +    {"7.10.c", 0x0a}, +    {"7.10.d", 0x0b}, +    {"7.20.a", 0x0c}, +    {"7.20.b", 0x0d}, +    {"7.20.c", 0x0e}, +    {"7.20.d", 0x0f}, +    {"7.30.a", 0x10}, +    {"7.30.b", 0x11}, +    {"8.00.a", 0x12}, +    {"8.00.b", 0x13}, +    {"8.10.a", 0x14}, +    {"8.20.a", 0x15}, +    {"8.20.b", 0x16}, +    {"8.30.a", 0x17}, +    {"8.40.a", 0x18}, +    {"8.40.b", 0x19}, +    {"8.50.a", 0x1A}, +    {"9.0", 0x1B}, +    {"9.1", 0x1D}, +    {"9.2", 0x1F}, +    {"9.3", 0x20}, +    {NULL, 0}, +}; + +static void mb_cpu_set_pc(CPUState *cs, vaddr value) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + +    cpu->env.sregs[SR_PC] = value; +} + +static bool mb_cpu_has_work(CPUState *cs) +{ +    return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI); +} + +#ifndef CONFIG_USER_ONLY +static void microblaze_cpu_set_irq(void *opaque, int irq, int level) +{ +    MicroBlazeCPU *cpu = opaque; +    CPUState *cs = CPU(cpu); +    int type = irq ? CPU_INTERRUPT_NMI : CPU_INTERRUPT_HARD; + +    if (level) { +        cpu_interrupt(cs, type); +    } else { +        cpu_reset_interrupt(cs, type); +    } +} +#endif + +/* CPUClass::reset() */ +static void mb_cpu_reset(CPUState *s) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(s); +    MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_GET_CLASS(cpu); +    CPUMBState *env = &cpu->env; + +    mcc->parent_reset(s); + +    memset(env, 0, offsetof(CPUMBState, pvr)); +    env->res_addr = RES_ADDR_NONE; +    tlb_flush(s, 1); + +    /* Disable stack protector.  */ +    env->shr = ~0; + +#if defined(CONFIG_USER_ONLY) +    /* start in user mode with interrupts enabled.  */ +    env->sregs[SR_MSR] = MSR_EE | MSR_IE | MSR_VM | MSR_UM; +#else +    env->sregs[SR_MSR] = 0; +    mmu_init(&env->mmu); +    env->mmu.c_mmu = 3; +    env->mmu.c_mmu_tlb_access = 3; +    env->mmu.c_mmu_zones = 16; +#endif +} + +static void mb_disas_set_info(CPUState *cpu, disassemble_info *info) +{ +    info->mach = bfd_arch_microblaze; +    info->print_insn = print_insn_microblaze; +} + +static void mb_cpu_realizefn(DeviceState *dev, Error **errp) +{ +    CPUState *cs = CPU(dev); +    MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_GET_CLASS(dev); +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); +    CPUMBState *env = &cpu->env; +    uint8_t version_code = 0; +    int i = 0; + +    qemu_init_vcpu(cs); + +    env->pvr.regs[0] = PVR0_USE_BARREL_MASK \ +                       | PVR0_USE_DIV_MASK \ +                       | PVR0_USE_HW_MUL_MASK \ +                       | PVR0_USE_EXC_MASK \ +                       | PVR0_USE_ICACHE_MASK \ +                       | PVR0_USE_DCACHE_MASK \ +                       | (0xb << 8); +    env->pvr.regs[2] = PVR2_D_OPB_MASK \ +                        | PVR2_D_LMB_MASK \ +                        | PVR2_I_OPB_MASK \ +                        | PVR2_I_LMB_MASK \ +                        | PVR2_USE_MSR_INSTR \ +                        | PVR2_USE_PCMP_INSTR \ +                        | PVR2_USE_BARREL_MASK \ +                        | PVR2_USE_DIV_MASK \ +                        | PVR2_USE_HW_MUL_MASK \ +                        | PVR2_USE_MUL64_MASK \ +                        | PVR2_FPU_EXC_MASK \ +                        | 0; + +    for (i = 0; mb_cpu_lookup[i].name && cpu->cfg.version; i++) { +        if (strcmp(mb_cpu_lookup[i].name, cpu->cfg.version) == 0) { +            version_code = mb_cpu_lookup[i].version_id; +            break; +        } +    } + +    if (!version_code) { +        qemu_log("Invalid MicroBlaze version number: %s\n", cpu->cfg.version); +    } + +    env->pvr.regs[0] |= (cpu->cfg.stackprot ? PVR0_SPROT_MASK : 0) | +                        (cpu->cfg.use_fpu ? PVR0_USE_FPU_MASK : 0) | +                        (cpu->cfg.use_mmu ? PVR0_USE_MMU_MASK : 0) | +                        (cpu->cfg.endi ? PVR0_ENDI_MASK : 0) | +                        (version_code << 16) | +                        (cpu->cfg.pvr == C_PVR_FULL ? PVR0_PVR_FULL_MASK : 0); + +    env->pvr.regs[2] |= (cpu->cfg.use_fpu ? PVR2_USE_FPU_MASK : 0) | +                        (cpu->cfg.use_fpu > 1 ? PVR2_USE_FPU2_MASK : 0); + +    env->pvr.regs[5] |= cpu->cfg.dcache_writeback ? +                                        PVR5_DCACHE_WRITEBACK_MASK : 0; + +    env->pvr.regs[10] = 0x0c000000; /* Default to spartan 3a dsp family.  */ +    env->pvr.regs[11] = PVR11_USE_MMU | (16 << 17); + +    env->sregs[SR_PC] = cpu->cfg.base_vectors; + +    mcc->parent_realize(dev, errp); +} + +static void mb_cpu_initfn(Object *obj) +{ +    CPUState *cs = CPU(obj); +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(obj); +    CPUMBState *env = &cpu->env; +    static bool tcg_initialized; + +    cs->env_ptr = env; +    cpu_exec_init(cs, &error_abort); + +    set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + +#ifndef CONFIG_USER_ONLY +    /* Inbound IRQ and FIR lines */ +    qdev_init_gpio_in(DEVICE(cpu), microblaze_cpu_set_irq, 2); +#endif + +    if (tcg_enabled() && !tcg_initialized) { +        tcg_initialized = true; +        mb_tcg_init(); +    } +} + +static const VMStateDescription vmstate_mb_cpu = { +    .name = "cpu", +    .unmigratable = 1, +}; + +static Property mb_properties[] = { +    DEFINE_PROP_UINT32("base-vectors", MicroBlazeCPU, cfg.base_vectors, 0), +    DEFINE_PROP_BOOL("use-stack-protection", MicroBlazeCPU, cfg.stackprot, +                     false), +    /* If use-fpu > 0 - FPU is enabled +     * If use-fpu = 2 - Floating point conversion and square root instructions +     *                  are enabled +     */ +    DEFINE_PROP_UINT8("use-fpu", MicroBlazeCPU, cfg.use_fpu, 2), +    DEFINE_PROP_BOOL("use-mmu", MicroBlazeCPU, cfg.use_mmu, true), +    DEFINE_PROP_BOOL("dcache-writeback", MicroBlazeCPU, cfg.dcache_writeback, +                     false), +    DEFINE_PROP_BOOL("endianness", MicroBlazeCPU, cfg.endi, false), +    DEFINE_PROP_STRING("version", MicroBlazeCPU, cfg.version), +    DEFINE_PROP_UINT8("pvr", MicroBlazeCPU, cfg.pvr, C_PVR_FULL), +    DEFINE_PROP_END_OF_LIST(), +}; + +static void mb_cpu_class_init(ObjectClass *oc, void *data) +{ +    DeviceClass *dc = DEVICE_CLASS(oc); +    CPUClass *cc = CPU_CLASS(oc); +    MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_CLASS(oc); + +    mcc->parent_realize = dc->realize; +    dc->realize = mb_cpu_realizefn; + +    mcc->parent_reset = cc->reset; +    cc->reset = mb_cpu_reset; + +    cc->has_work = mb_cpu_has_work; +    cc->do_interrupt = mb_cpu_do_interrupt; +    cc->cpu_exec_interrupt = mb_cpu_exec_interrupt; +    cc->dump_state = mb_cpu_dump_state; +    cc->set_pc = mb_cpu_set_pc; +    cc->gdb_read_register = mb_cpu_gdb_read_register; +    cc->gdb_write_register = mb_cpu_gdb_write_register; +#ifdef CONFIG_USER_ONLY +    cc->handle_mmu_fault = mb_cpu_handle_mmu_fault; +#else +    cc->do_unassigned_access = mb_cpu_unassigned_access; +    cc->get_phys_page_debug = mb_cpu_get_phys_page_debug; +#endif +    dc->vmsd = &vmstate_mb_cpu; +    dc->props = mb_properties; +    cc->gdb_num_core_regs = 32 + 5; + +    cc->disas_set_info = mb_disas_set_info; + +    /* +     * Reason: mb_cpu_initfn() calls cpu_exec_init(), which saves the +     * object in cpus -> dangling pointer after final object_unref(). +     */ +    dc->cannot_destroy_with_object_finalize_yet = true; +} + +static const TypeInfo mb_cpu_type_info = { +    .name = TYPE_MICROBLAZE_CPU, +    .parent = TYPE_CPU, +    .instance_size = sizeof(MicroBlazeCPU), +    .instance_init = mb_cpu_initfn, +    .class_size = sizeof(MicroBlazeCPUClass), +    .class_init = mb_cpu_class_init, +}; + +static void mb_cpu_register_types(void) +{ +    type_register_static(&mb_cpu_type_info); +} + +type_init(mb_cpu_register_types) diff --git a/target-microblaze/cpu.h b/target-microblaze/cpu.h new file mode 100644 index 00000000..7e20e59b --- /dev/null +++ b/target-microblaze/cpu.h @@ -0,0 +1,345 @@ +/* + *  MicroBlaze virtual CPU header + * + *  Copyright (c) 2009 Edgar E. Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef CPU_MICROBLAZE_H +#define CPU_MICROBLAZE_H + +#include "config.h" +#include "qemu-common.h" + +#define TARGET_LONG_BITS 32 + +#define CPUArchState struct CPUMBState + +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" +struct CPUMBState; +typedef struct CPUMBState CPUMBState; +#if !defined(CONFIG_USER_ONLY) +#include "mmu.h" +#endif + +#define ELF_MACHINE	EM_MICROBLAZE + +#define EXCP_MMU        1 +#define EXCP_IRQ        2 +#define EXCP_BREAK      3 +#define EXCP_HW_BREAK   4 +#define EXCP_HW_EXCP    5 + +/* MicroBlaze-specific interrupt pending bits.  */ +#define CPU_INTERRUPT_NMI       CPU_INTERRUPT_TGT_EXT_3 + +/* Meanings of the MBCPU object's two inbound GPIO lines */ +#define MB_CPU_IRQ 0 +#define MB_CPU_FIR 1 + +/* Register aliases. R0 - R15 */ +#define R_SP     1 +#define SR_PC    0 +#define SR_MSR   1 +#define SR_EAR   3 +#define SR_ESR   5 +#define SR_FSR   7 +#define SR_BTR   0xb +#define SR_EDR   0xd + +/* MSR flags.  */ +#define MSR_BE  (1<<0) /* 0x001 */ +#define MSR_IE  (1<<1) /* 0x002 */ +#define MSR_C   (1<<2) /* 0x004 */ +#define MSR_BIP (1<<3) /* 0x008 */ +#define MSR_FSL (1<<4) /* 0x010 */ +#define MSR_ICE (1<<5) /* 0x020 */ +#define MSR_DZ  (1<<6) /* 0x040 */ +#define MSR_DCE (1<<7) /* 0x080 */ +#define MSR_EE  (1<<8) /* 0x100 */ +#define MSR_EIP (1<<9) /* 0x200 */ +#define MSR_PVR (1<<10) /* 0x400 */ +#define MSR_CC  (1<<31) + +/* Machine State Register (MSR) Fields */ +#define MSR_UM (1<<11) /* User Mode */ +#define MSR_UMS        (1<<12) /* User Mode Save */ +#define MSR_VM (1<<13) /* Virtual Mode */ +#define MSR_VMS        (1<<14) /* Virtual Mode Save */ + +#define MSR_KERNEL      MSR_EE|MSR_VM +//#define MSR_USER     MSR_KERNEL|MSR_UM|MSR_IE +#define MSR_KERNEL_VMS  MSR_EE|MSR_VMS +//#define MSR_USER_VMS MSR_KERNEL_VMS|MSR_UMS|MSR_IE + +/* Exception State Register (ESR) Fields */ +#define          ESR_DIZ       (1<<11) /* Zone Protection */ +#define          ESR_S         (1<<10) /* Store instruction */ + +#define          ESR_ESS_FSL_OFFSET     5 + +#define          ESR_EC_FSL             0 +#define          ESR_EC_UNALIGNED_DATA  1 +#define          ESR_EC_ILLEGAL_OP      2 +#define          ESR_EC_INSN_BUS        3 +#define          ESR_EC_DATA_BUS        4 +#define          ESR_EC_DIVZERO         5 +#define          ESR_EC_FPU             6 +#define          ESR_EC_PRIVINSN        7 +#define          ESR_EC_STACKPROT       7  /* Same as PRIVINSN.  */ +#define          ESR_EC_DATA_STORAGE    8 +#define          ESR_EC_INSN_STORAGE    9 +#define          ESR_EC_DATA_TLB        10 +#define          ESR_EC_INSN_TLB        11 +#define          ESR_EC_MASK            31 + +/* Floating Point Status Register (FSR) Bits */ +#define FSR_IO          (1<<4) /* Invalid operation */ +#define FSR_DZ          (1<<3) /* Divide-by-zero */ +#define FSR_OF          (1<<2) /* Overflow */ +#define FSR_UF          (1<<1) /* Underflow */ +#define FSR_DO          (1<<0) /* Denormalized operand error */ + +/* Version reg.  */ +/* Basic PVR mask */ +#define PVR0_PVR_FULL_MASK              0x80000000 +#define PVR0_USE_BARREL_MASK            0x40000000 +#define PVR0_USE_DIV_MASK               0x20000000 +#define PVR0_USE_HW_MUL_MASK            0x10000000 +#define PVR0_USE_FPU_MASK               0x08000000 +#define PVR0_USE_EXC_MASK               0x04000000 +#define PVR0_USE_ICACHE_MASK            0x02000000 +#define PVR0_USE_DCACHE_MASK            0x01000000 +#define PVR0_USE_MMU_MASK               0x00800000 +#define PVR0_USE_BTC			0x00400000 +#define PVR0_ENDI_MASK                  0x00200000 +#define PVR0_FAULT			0x00100000 +#define PVR0_VERSION_MASK               0x0000FF00 +#define PVR0_USER1_MASK                 0x000000FF +#define PVR0_SPROT_MASK                 0x00000001 + +/* User 2 PVR mask */ +#define PVR1_USER2_MASK                 0xFFFFFFFF + +/* Configuration PVR masks */ +#define PVR2_D_OPB_MASK                 0x80000000 +#define PVR2_D_LMB_MASK                 0x40000000 +#define PVR2_I_OPB_MASK                 0x20000000 +#define PVR2_I_LMB_MASK                 0x10000000 +#define PVR2_INTERRUPT_IS_EDGE_MASK     0x08000000 +#define PVR2_EDGE_IS_POSITIVE_MASK      0x04000000 +#define PVR2_D_PLB_MASK                 0x02000000      /* new */ +#define PVR2_I_PLB_MASK                 0x01000000      /* new */ +#define PVR2_INTERCONNECT               0x00800000      /* new */ +#define PVR2_USE_EXTEND_FSL             0x00080000      /* new */ +#define PVR2_USE_FSL_EXC                0x00040000      /* new */ +#define PVR2_USE_MSR_INSTR              0x00020000 +#define PVR2_USE_PCMP_INSTR             0x00010000 +#define PVR2_AREA_OPTIMISED             0x00008000 +#define PVR2_USE_BARREL_MASK            0x00004000 +#define PVR2_USE_DIV_MASK               0x00002000 +#define PVR2_USE_HW_MUL_MASK            0x00001000 +#define PVR2_USE_FPU_MASK               0x00000800 +#define PVR2_USE_MUL64_MASK             0x00000400 +#define PVR2_USE_FPU2_MASK              0x00000200      /* new */ +#define PVR2_USE_IPLBEXC                0x00000100 +#define PVR2_USE_DPLBEXC                0x00000080 +#define PVR2_OPCODE_0x0_ILL_MASK        0x00000040 +#define PVR2_UNALIGNED_EXC_MASK         0x00000020 +#define PVR2_ILL_OPCODE_EXC_MASK        0x00000010 +#define PVR2_IOPB_BUS_EXC_MASK          0x00000008 +#define PVR2_DOPB_BUS_EXC_MASK          0x00000004 +#define PVR2_DIV_ZERO_EXC_MASK          0x00000002 +#define PVR2_FPU_EXC_MASK               0x00000001 + +/* Debug and exception PVR masks */ +#define PVR3_DEBUG_ENABLED_MASK         0x80000000 +#define PVR3_NUMBER_OF_PC_BRK_MASK      0x1E000000 +#define PVR3_NUMBER_OF_RD_ADDR_BRK_MASK 0x00380000 +#define PVR3_NUMBER_OF_WR_ADDR_BRK_MASK 0x0000E000 +#define PVR3_FSL_LINKS_MASK             0x00000380 + +/* ICache config PVR masks */ +#define PVR4_USE_ICACHE_MASK            0x80000000 +#define PVR4_ICACHE_ADDR_TAG_BITS_MASK  0x7C000000 +#define PVR4_ICACHE_USE_FSL_MASK        0x02000000 +#define PVR4_ICACHE_ALLOW_WR_MASK       0x01000000 +#define PVR4_ICACHE_LINE_LEN_MASK       0x00E00000 +#define PVR4_ICACHE_BYTE_SIZE_MASK      0x001F0000 + +/* DCache config PVR masks */ +#define PVR5_USE_DCACHE_MASK            0x80000000 +#define PVR5_DCACHE_ADDR_TAG_BITS_MASK  0x7C000000 +#define PVR5_DCACHE_USE_FSL_MASK        0x02000000 +#define PVR5_DCACHE_ALLOW_WR_MASK       0x01000000 +#define PVR5_DCACHE_LINE_LEN_MASK       0x00E00000 +#define PVR5_DCACHE_BYTE_SIZE_MASK      0x001F0000 +#define PVR5_DCACHE_WRITEBACK_MASK      0x00004000 + +/* ICache base address PVR mask */ +#define PVR6_ICACHE_BASEADDR_MASK       0xFFFFFFFF + +/* ICache high address PVR mask */ +#define PVR7_ICACHE_HIGHADDR_MASK       0xFFFFFFFF + +/* DCache base address PVR mask */ +#define PVR8_DCACHE_BASEADDR_MASK       0xFFFFFFFF + +/* DCache high address PVR mask */ +#define PVR9_DCACHE_HIGHADDR_MASK       0xFFFFFFFF + +/* Target family PVR mask */ +#define PVR10_TARGET_FAMILY_MASK        0xFF000000 + +/* MMU descrtiption */ +#define PVR11_USE_MMU                   0xC0000000 +#define PVR11_MMU_ITLB_SIZE             0x38000000 +#define PVR11_MMU_DTLB_SIZE             0x07000000 +#define PVR11_MMU_TLB_ACCESS            0x00C00000 +#define PVR11_MMU_ZONES                 0x003E0000 +/* MSR Reset value PVR mask */ +#define PVR11_MSR_RESET_VALUE_MASK      0x000007FF + +#define C_PVR_NONE                      0 +#define C_PVR_BASIC                     1 +#define C_PVR_FULL                      2 + +/* CPU flags.  */ + +/* Condition codes.  */ +#define CC_GE  5 +#define CC_GT  4 +#define CC_LE  3 +#define CC_LT  2 +#define CC_NE  1 +#define CC_EQ  0 + +#define NB_MMU_MODES    3 + +#define STREAM_EXCEPTION (1 << 0) +#define STREAM_ATOMIC    (1 << 1) +#define STREAM_TEST      (1 << 2) +#define STREAM_CONTROL   (1 << 3) +#define STREAM_NONBLOCK  (1 << 4) + +struct CPUMBState { +    uint32_t debug; +    uint32_t btaken; +    uint32_t btarget; +    uint32_t bimm; + +    uint32_t imm; +    uint32_t regs[33]; +    uint32_t sregs[24]; +    float_status fp_status; +    /* Stack protectors. Yes, it's a hw feature.  */ +    uint32_t slr, shr; + +    /* lwx/swx reserved address */ +#define RES_ADDR_NONE 0xffffffff /* Use 0xffffffff to indicate no reservation */ +    uint32_t res_addr; +    uint32_t res_val; + +    /* Internal flags.  */ +#define IMM_FLAG	4 +#define MSR_EE_FLAG     (1 << 8) +#define DRTI_FLAG	(1 << 16) +#define DRTE_FLAG	(1 << 17) +#define DRTB_FLAG	(1 << 18) +#define D_FLAG		(1 << 19)  /* Bit in ESR.  */ +/* TB dependent CPUMBState.  */ +#define IFLAGS_TB_MASK  (D_FLAG | IMM_FLAG | DRTI_FLAG | DRTE_FLAG | DRTB_FLAG) +    uint32_t iflags; + +#if !defined(CONFIG_USER_ONLY) +    /* Unified MMU.  */ +    struct microblaze_mmu mmu; +#endif + +    CPU_COMMON + +    /* These fields are preserved on reset.  */ + +    struct { +        uint32_t regs[16]; +    } pvr; +}; + +#include "cpu-qom.h" + +void mb_tcg_init(void); +MicroBlazeCPU *cpu_mb_init(const char *cpu_model); +int cpu_mb_exec(CPUState *cpu); +/* you can call this signal handler from your SIGBUS and SIGSEGV +   signal handlers to inform the virtual CPU of exceptions. non zero +   is returned if the signal was handled by the virtual CPU.  */ +int cpu_mb_signal_handler(int host_signum, void *pinfo, +                          void *puc); + +/* FIXME: MB uses variable pages down to 1K but linux only uses 4k.  */ +#define TARGET_PAGE_BITS 12 + +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +#define cpu_init(cpu_model) CPU(cpu_mb_init(cpu_model)) + +#define cpu_exec cpu_mb_exec +#define cpu_gen_code cpu_mb_gen_code +#define cpu_signal_handler cpu_mb_signal_handler + +/* MMU modes definitions */ +#define MMU_MODE0_SUFFIX _nommu +#define MMU_MODE1_SUFFIX _kernel +#define MMU_MODE2_SUFFIX _user +#define MMU_NOMMU_IDX   0 +#define MMU_KERNEL_IDX  1 +#define MMU_USER_IDX    2 +/* See NB_MMU_MODES further up the file.  */ + +static inline int cpu_mmu_index (CPUMBState *env) +{ +        /* Are we in nommu mode?.  */ +        if (!(env->sregs[SR_MSR] & MSR_VM)) +            return MMU_NOMMU_IDX; + +	if (env->sregs[SR_MSR] & MSR_UM) +            return MMU_USER_IDX; +        return MMU_KERNEL_IDX; +} + +int mb_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, +                            int mmu_idx); + +#include "exec/cpu-all.h" + +static inline void cpu_get_tb_cpu_state(CPUMBState *env, target_ulong *pc, +                                        target_ulong *cs_base, int *flags) +{ +    *pc = env->sregs[SR_PC]; +    *cs_base = 0; +    *flags = (env->iflags & IFLAGS_TB_MASK) | +                 (env->sregs[SR_MSR] & (MSR_UM | MSR_VM | MSR_EE)); +} + +#if !defined(CONFIG_USER_ONLY) +void mb_cpu_unassigned_access(CPUState *cpu, hwaddr addr, +                              bool is_write, bool is_exec, int is_asi, +                              unsigned size); +#endif + +#include "exec/exec-all.h" + +#endif diff --git a/target-microblaze/gdbstub.c b/target-microblaze/gdbstub.c new file mode 100644 index 00000000..a70e2ee3 --- /dev/null +++ b/target-microblaze/gdbstub.c @@ -0,0 +1,56 @@ +/* + * MicroBlaze gdb server stub + * + * Copyright (c) 2003-2005 Fabrice Bellard + * Copyright (c) 2013 SUSE LINUX Products GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#include "config.h" +#include "qemu-common.h" +#include "exec/gdbstub.h" + +int mb_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); +    CPUMBState *env = &cpu->env; + +    if (n < 32) { +        return gdb_get_reg32(mem_buf, env->regs[n]); +    } else { +        return gdb_get_reg32(mem_buf, env->sregs[n - 32]); +    } +    return 0; +} + +int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); +    CPUClass *cc = CPU_GET_CLASS(cs); +    CPUMBState *env = &cpu->env; +    uint32_t tmp; + +    if (n > cc->gdb_num_core_regs) { +        return 0; +    } + +    tmp = ldl_p(mem_buf); + +    if (n < 32) { +        env->regs[n] = tmp; +    } else { +        env->sregs[n - 32] = tmp; +    } +    return 4; +} diff --git a/target-microblaze/helper.c b/target-microblaze/helper.c new file mode 100644 index 00000000..8257b0e0 --- /dev/null +++ b/target-microblaze/helper.c @@ -0,0 +1,304 @@ +/* + *  MicroBlaze helper routines. + * + *  Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com> + *  Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "qemu/host-utils.h" + +#define D(x) + +#if defined(CONFIG_USER_ONLY) + +void mb_cpu_do_interrupt(CPUState *cs) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); +    CPUMBState *env = &cpu->env; + +    cs->exception_index = -1; +    env->res_addr = RES_ADDR_NONE; +    env->regs[14] = env->sregs[SR_PC]; +} + +int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, +                            int mmu_idx) +{ +    cs->exception_index = 0xaa; +    cpu_dump_state(cs, stderr, fprintf, 0); +    return 1; +} + +#else /* !CONFIG_USER_ONLY */ + +int mb_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, +                            int mmu_idx) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); +    CPUMBState *env = &cpu->env; +    unsigned int hit; +    unsigned int mmu_available; +    int r = 1; +    int prot; + +    mmu_available = 0; +    if (cpu->cfg.use_mmu) { +        mmu_available = 1; +        if ((cpu->cfg.pvr == C_PVR_FULL) && +            (env->pvr.regs[11] & PVR11_USE_MMU) != PVR11_USE_MMU) { +            mmu_available = 0; +        } +    } + +    /* Translate if the MMU is available and enabled.  */ +    if (mmu_available && (env->sregs[SR_MSR] & MSR_VM)) { +        target_ulong vaddr, paddr; +        struct microblaze_mmu_lookup lu; + +        hit = mmu_translate(&env->mmu, &lu, address, rw, mmu_idx); +        if (hit) { +            vaddr = address & TARGET_PAGE_MASK; +            paddr = lu.paddr + vaddr - lu.vaddr; + +            qemu_log_mask(CPU_LOG_MMU, "MMU map mmu=%d v=%x p=%x prot=%x\n", +                    mmu_idx, vaddr, paddr, lu.prot); +            tlb_set_page(cs, vaddr, paddr, lu.prot, mmu_idx, TARGET_PAGE_SIZE); +            r = 0; +        } else { +            env->sregs[SR_EAR] = address; +            qemu_log_mask(CPU_LOG_MMU, "mmu=%d miss v=%" VADDR_PRIx "\n", +                                        mmu_idx, address); + +            switch (lu.err) { +                case ERR_PROT: +                    env->sregs[SR_ESR] = rw == 2 ? 17 : 16; +                    env->sregs[SR_ESR] |= (rw == 1) << 10; +                    break; +                case ERR_MISS: +                    env->sregs[SR_ESR] = rw == 2 ? 19 : 18; +                    env->sregs[SR_ESR] |= (rw == 1) << 10; +                    break; +                default: +                    abort(); +                    break; +            } + +            if (cs->exception_index == EXCP_MMU) { +                cpu_abort(cs, "recursive faults\n"); +            } + +            /* TLB miss.  */ +            cs->exception_index = EXCP_MMU; +        } +    } else { +        /* MMU disabled or not available.  */ +        address &= TARGET_PAGE_MASK; +        prot = PAGE_BITS; +        tlb_set_page(cs, address, address, prot, mmu_idx, TARGET_PAGE_SIZE); +        r = 0; +    } +    return r; +} + +void mb_cpu_do_interrupt(CPUState *cs) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); +    CPUMBState *env = &cpu->env; +    uint32_t t; + +    /* IMM flag cannot propagate across a branch and into the dslot.  */ +    assert(!((env->iflags & D_FLAG) && (env->iflags & IMM_FLAG))); +    assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG))); +/*    assert(env->sregs[SR_MSR] & (MSR_EE)); Only for HW exceptions.  */ +    env->res_addr = RES_ADDR_NONE; +    switch (cs->exception_index) { +        case EXCP_HW_EXCP: +            if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) { +                qemu_log("Exception raised on system without exceptions!\n"); +                return; +            } + +            env->regs[17] = env->sregs[SR_PC] + 4; +            env->sregs[SR_ESR] &= ~(1 << 12); + +            /* Exception breaks branch + dslot sequence?  */ +            if (env->iflags & D_FLAG) { +                env->sregs[SR_ESR] |= 1 << 12 ; +                env->sregs[SR_BTR] = env->btarget; +            } + +            /* Disable the MMU.  */ +            t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; +            env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); +            env->sregs[SR_MSR] |= t; +            /* Exception in progress.  */ +            env->sregs[SR_MSR] |= MSR_EIP; + +            qemu_log_mask(CPU_LOG_INT, +                          "hw exception at pc=%x ear=%x esr=%x iflags=%x\n", +                          env->sregs[SR_PC], env->sregs[SR_EAR], +                          env->sregs[SR_ESR], env->iflags); +            log_cpu_state_mask(CPU_LOG_INT, cs, 0); +            env->iflags &= ~(IMM_FLAG | D_FLAG); +            env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x20; +            break; + +        case EXCP_MMU: +            env->regs[17] = env->sregs[SR_PC]; + +            env->sregs[SR_ESR] &= ~(1 << 12); +            /* Exception breaks branch + dslot sequence?  */ +            if (env->iflags & D_FLAG) { +                D(qemu_log("D_FLAG set at exception bimm=%d\n", env->bimm)); +                env->sregs[SR_ESR] |= 1 << 12 ; +                env->sregs[SR_BTR] = env->btarget; + +                /* Reexecute the branch.  */ +                env->regs[17] -= 4; +                /* was the branch immprefixed?.  */ +                if (env->bimm) { +                    qemu_log_mask(CPU_LOG_INT, +                                  "bimm exception at pc=%x iflags=%x\n", +                                  env->sregs[SR_PC], env->iflags); +                    env->regs[17] -= 4; +                    log_cpu_state_mask(CPU_LOG_INT, cs, 0); +                } +            } else if (env->iflags & IMM_FLAG) { +                D(qemu_log("IMM_FLAG set at exception\n")); +                env->regs[17] -= 4; +            } + +            /* Disable the MMU.  */ +            t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; +            env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); +            env->sregs[SR_MSR] |= t; +            /* Exception in progress.  */ +            env->sregs[SR_MSR] |= MSR_EIP; + +            qemu_log_mask(CPU_LOG_INT, +                          "exception at pc=%x ear=%x iflags=%x\n", +                          env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); +            log_cpu_state_mask(CPU_LOG_INT, cs, 0); +            env->iflags &= ~(IMM_FLAG | D_FLAG); +            env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x20; +            break; + +        case EXCP_IRQ: +            assert(!(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP))); +            assert(env->sregs[SR_MSR] & MSR_IE); +            assert(!(env->iflags & D_FLAG)); + +            t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; + +#if 0 +#include "disas/disas.h" + +/* Useful instrumentation when debugging interrupt issues in either +   the models or in sw.  */ +            { +                const char *sym; + +                sym = lookup_symbol(env->sregs[SR_PC]); +                if (sym +                    && (!strcmp("netif_rx", sym) +                        || !strcmp("process_backlog", sym))) { + +                    qemu_log( +                         "interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n", +                         env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags, +                         sym); + +                    log_cpu_state(cs, 0); +                } +            } +#endif +            qemu_log_mask(CPU_LOG_INT, +                         "interrupt at pc=%x msr=%x %x iflags=%x\n", +                         env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); + +            env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ +                                    | MSR_UM | MSR_IE); +            env->sregs[SR_MSR] |= t; + +            env->regs[14] = env->sregs[SR_PC]; +            env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x10; +            //log_cpu_state_mask(CPU_LOG_INT, cs, 0); +            break; + +        case EXCP_BREAK: +        case EXCP_HW_BREAK: +            assert(!(env->iflags & IMM_FLAG)); +            assert(!(env->iflags & D_FLAG)); +            t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; +            qemu_log_mask(CPU_LOG_INT, +                        "break at pc=%x msr=%x %x iflags=%x\n", +                        env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); +            log_cpu_state_mask(CPU_LOG_INT, cs, 0); +            env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); +            env->sregs[SR_MSR] |= t; +            env->sregs[SR_MSR] |= MSR_BIP; +            if (cs->exception_index == EXCP_HW_BREAK) { +                env->regs[16] = env->sregs[SR_PC]; +                env->sregs[SR_MSR] |= MSR_BIP; +                env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x18; +            } else +                env->sregs[SR_PC] = env->btarget; +            break; +        default: +            cpu_abort(cs, "unhandled exception type=%d\n", +                      cs->exception_index); +            break; +    } +} + +hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); +    CPUMBState *env = &cpu->env; +    target_ulong vaddr, paddr = 0; +    struct microblaze_mmu_lookup lu; +    unsigned int hit; + +    if (env->sregs[SR_MSR] & MSR_VM) { +        hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); +        if (hit) { +            vaddr = addr & TARGET_PAGE_MASK; +            paddr = lu.paddr + vaddr - lu.vaddr; +        } else +            paddr = 0; /* ???.  */ +    } else +        paddr = addr & TARGET_PAGE_MASK; + +    return paddr; +} +#endif + +bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); +    CPUMBState *env = &cpu->env; + +    if ((interrupt_request & CPU_INTERRUPT_HARD) +        && (env->sregs[SR_MSR] & MSR_IE) +        && !(env->sregs[SR_MSR] & (MSR_EIP | MSR_BIP)) +        && !(env->iflags & (D_FLAG | IMM_FLAG))) { +        cs->exception_index = EXCP_IRQ; +        mb_cpu_do_interrupt(cs); +        return true; +    } +    return false; +} diff --git a/target-microblaze/helper.h b/target-microblaze/helper.h new file mode 100644 index 00000000..bd13826d --- /dev/null +++ b/target-microblaze/helper.h @@ -0,0 +1,37 @@ +DEF_HELPER_2(raise_exception, void, env, i32) +DEF_HELPER_1(debug, void, env) +DEF_HELPER_FLAGS_3(carry, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) +DEF_HELPER_2(cmp, i32, i32, i32) +DEF_HELPER_2(cmpu, i32, i32, i32) +DEF_HELPER_FLAGS_1(clz, TCG_CALL_NO_RWG_SE, i32, i32) + +DEF_HELPER_3(divs, i32, env, i32, i32) +DEF_HELPER_3(divu, i32, env, i32, i32) + +DEF_HELPER_3(fadd, i32, env, i32, i32) +DEF_HELPER_3(frsub, i32, env, i32, i32) +DEF_HELPER_3(fmul, i32, env, i32, i32) +DEF_HELPER_3(fdiv, i32, env, i32, i32) +DEF_HELPER_2(flt, i32, env, i32) +DEF_HELPER_2(fint, i32, env, i32) +DEF_HELPER_2(fsqrt, i32, env, i32) + +DEF_HELPER_3(fcmp_un, i32, env, i32, i32) +DEF_HELPER_3(fcmp_lt, i32, env, i32, i32) +DEF_HELPER_3(fcmp_eq, i32, env, i32, i32) +DEF_HELPER_3(fcmp_le, i32, env, i32, i32) +DEF_HELPER_3(fcmp_gt, i32, env, i32, i32) +DEF_HELPER_3(fcmp_ne, i32, env, i32, i32) +DEF_HELPER_3(fcmp_ge, i32, env, i32, i32) + +DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_NO_RWG_SE, i32, i32, i32) +#if !defined(CONFIG_USER_ONLY) +DEF_HELPER_2(mmu_read, i32, env, i32) +DEF_HELPER_3(mmu_write, void, env, i32, i32) +#endif + +DEF_HELPER_5(memalign, void, env, i32, i32, i32, i32) +DEF_HELPER_2(stackprot, void, env, i32) + +DEF_HELPER_2(get, i32, i32, i32) +DEF_HELPER_3(put, void, i32, i32, i32) diff --git a/target-microblaze/microblaze-decode.h b/target-microblaze/microblaze-decode.h new file mode 100644 index 00000000..401319ed --- /dev/null +++ b/target-microblaze/microblaze-decode.h @@ -0,0 +1,55 @@ +/* + *  MicroBlaze insn decoding macros. + * + *  Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Convenient binary macros.  */ +#define HEX__(n) 0x##n##LU +#define B8__(x) ((x&0x0000000FLU)?1:0) \ +                 + ((x&0x000000F0LU)?2:0) \ +                 + ((x&0x00000F00LU)?4:0) \ +                 + ((x&0x0000F000LU)?8:0) \ +                 + ((x&0x000F0000LU)?16:0) \ +                 + ((x&0x00F00000LU)?32:0) \ +                 + ((x&0x0F000000LU)?64:0) \ +                 + ((x&0xF0000000LU)?128:0) +#define B8(d) ((unsigned char)B8__(HEX__(d))) + +/* Decode logic, value and mask.  */ +#define DEC_ADD     {B8(00000000), B8(00110001)} +#define DEC_SUB     {B8(00000001), B8(00110001)} +#define DEC_AND     {B8(00100001), B8(00110101)} +#define DEC_XOR     {B8(00100010), B8(00110111)} +#define DEC_OR      {B8(00100000), B8(00110111)} +#define DEC_BIT     {B8(00100100), B8(00111111)} +#define DEC_MSR     {B8(00100101), B8(00111111)} + +#define DEC_BARREL  {B8(00010001), B8(00110111)} +#define DEC_MUL     {B8(00010000), B8(00110111)} +#define DEC_DIV     {B8(00010010), B8(00110111)} +#define DEC_FPU     {B8(00010110), B8(00111111)} + +#define DEC_LD      {B8(00110000), B8(00110100)} +#define DEC_ST      {B8(00110100), B8(00110100)} +#define DEC_IMM     {B8(00101100), B8(00111111)} + +#define DEC_BR      {B8(00100110), B8(00110111)} +#define DEC_BCC     {B8(00100111), B8(00110111)} +#define DEC_RTS     {B8(00101101), B8(00111111)} + +#define DEC_STREAM  {B8(00010011), B8(00110111)} + diff --git a/target-microblaze/mmu.c b/target-microblaze/mmu.c new file mode 100644 index 00000000..728da133 --- /dev/null +++ b/target-microblaze/mmu.c @@ -0,0 +1,301 @@ +/* + *  Microblaze MMU emulation for qemu. + * + *  Copyright (c) 2009 Edgar E. Iglesias + *  Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" + +#define D(x) + +static unsigned int tlb_decode_size(unsigned int f) +{ +    static const unsigned int sizes[] = { +        1 * 1024, 4 * 1024, 16 * 1024, 64 * 1024, 256 * 1024, +        1 * 1024 * 1024, 4 * 1024 * 1024, 16 * 1024 * 1024 +    }; +    assert(f < ARRAY_SIZE(sizes)); +    return sizes[f]; +} + +static void mmu_flush_idx(CPUMBState *env, unsigned int idx) +{ +    CPUState *cs = CPU(mb_env_get_cpu(env)); +    struct microblaze_mmu *mmu = &env->mmu; +    unsigned int tlb_size; +    uint32_t tlb_tag, end, t; + +    t = mmu->rams[RAM_TAG][idx]; +    if (!(t & TLB_VALID)) +        return; + +    tlb_tag = t & TLB_EPN_MASK; +    tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); +    end = tlb_tag + tlb_size; + +    while (tlb_tag < end) { +        tlb_flush_page(cs, tlb_tag); +        tlb_tag += TARGET_PAGE_SIZE; +    } +} + +static void mmu_change_pid(CPUMBState *env, unsigned int newpid)  +{ +    struct microblaze_mmu *mmu = &env->mmu; +    unsigned int i; +    uint32_t t; + +    if (newpid & ~0xff) +        qemu_log("Illegal rpid=%x\n", newpid); + +    for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { +        /* Lookup and decode.  */ +        t = mmu->rams[RAM_TAG][i]; +        if (t & TLB_VALID) { +            if (mmu->tids[i] && ((mmu->regs[MMU_R_PID] & 0xff) == mmu->tids[i])) +                mmu_flush_idx(env, i); +        } +    } +} + +/* rw - 0 = read, 1 = write, 2 = fetch.  */ +unsigned int mmu_translate(struct microblaze_mmu *mmu, +                           struct microblaze_mmu_lookup *lu, +                           target_ulong vaddr, int rw, int mmu_idx) +{ +    unsigned int i, hit = 0; +    unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel; +    unsigned int tlb_size; +    uint32_t tlb_tag, tlb_rpn, mask, t0; + +    lu->err = ERR_MISS; +    for (i = 0; i < ARRAY_SIZE(mmu->rams[RAM_TAG]); i++) { +        uint32_t t, d; + +        /* Lookup and decode.  */ +        t = mmu->rams[RAM_TAG][i]; +        D(qemu_log("TLB %d valid=%d\n", i, t & TLB_VALID)); +        if (t & TLB_VALID) { +            tlb_size = tlb_decode_size((t & TLB_PAGESZ_MASK) >> 7); +            if (tlb_size < TARGET_PAGE_SIZE) { +                qemu_log("%d pages not supported\n", tlb_size); +                abort(); +            } + +            mask = ~(tlb_size - 1); +            tlb_tag = t & TLB_EPN_MASK; +            if ((vaddr & mask) != (tlb_tag & mask)) { +                D(qemu_log("TLB %d vaddr=%x != tag=%x\n", +                           i, vaddr & mask, tlb_tag & mask)); +                continue; +            } +            if (mmu->tids[i] +                && ((mmu->regs[MMU_R_PID] & 0xff) != mmu->tids[i])) { +                D(qemu_log("TLB %d pid=%x != tid=%x\n", +                           i, mmu->regs[MMU_R_PID], mmu->tids[i])); +                continue; +            } + +            /* Bring in the data part.  */ +            d = mmu->rams[RAM_DATA][i]; +            tlb_ex = d & TLB_EX; +            tlb_wr = d & TLB_WR; + +            /* Now let's see if there is a zone that overrides the protbits.  */ +            tlb_zsel = (d >> 4) & 0xf; +            t0 = mmu->regs[MMU_R_ZPR] >> (30 - (tlb_zsel * 2)); +            t0 &= 0x3; + +            if (tlb_zsel > mmu->c_mmu_zones) { +                qemu_log("tlb zone select out of range! %d\n", tlb_zsel); +                t0 = 1; /* Ignore.  */ +            } + +            if (mmu->c_mmu == 1) { +                t0 = 1; /* Zones are disabled.  */ +            } + +            switch (t0) { +                case 0: +                    if (mmu_idx == MMU_USER_IDX) +                        continue; +                    break; +                case 2: +                    if (mmu_idx != MMU_USER_IDX) { +                        tlb_ex = 1; +                        tlb_wr = 1; +                    } +                    break; +                case 3: +                    tlb_ex = 1; +                    tlb_wr = 1; +                    break; +                default: break; +            } + +            lu->err = ERR_PROT; +            lu->prot = PAGE_READ; +            if (tlb_wr) +                lu->prot |= PAGE_WRITE; +            else if (rw == 1) +                goto done; +            if (tlb_ex) +                lu->prot |=PAGE_EXEC; +            else if (rw == 2) { +                goto done; +            } + +            tlb_rpn = d & TLB_RPN_MASK; + +            lu->vaddr = tlb_tag; +            lu->paddr = tlb_rpn; +            lu->size = tlb_size; +            lu->err = ERR_HIT; +            lu->idx = i; +            hit = 1; +            goto done; +        } +    } +done: +    D(qemu_log("MMU vaddr=%x rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", +              vaddr, rw, tlb_wr, tlb_ex, hit)); +    return hit; +} + +/* Writes/reads to the MMU's special regs end up here.  */ +uint32_t mmu_read(CPUMBState *env, uint32_t rn) +{ +    unsigned int i; +    uint32_t r; + +    if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { +        qemu_log("MMU access on MMU-less system\n"); +        return 0; +    } + +    switch (rn) { +        /* Reads to HI/LO trig reads from the mmu rams.  */ +        case MMU_R_TLBLO: +        case MMU_R_TLBHI: +            if (!(env->mmu.c_mmu_tlb_access & 1)) { +                qemu_log("Invalid access to MMU reg %d\n", rn); +                return 0; +            } + +            i = env->mmu.regs[MMU_R_TLBX] & 0xff; +            r = env->mmu.rams[rn & 1][i]; +            if (rn == MMU_R_TLBHI) +                env->mmu.regs[MMU_R_PID] = env->mmu.tids[i]; +            break; +        case MMU_R_PID: +        case MMU_R_ZPR: +            if (!(env->mmu.c_mmu_tlb_access & 1)) { +                qemu_log("Invalid access to MMU reg %d\n", rn); +                return 0; +            } +            r = env->mmu.regs[rn]; +            break; +        default: +            r = env->mmu.regs[rn]; +            break; +    } +    D(qemu_log("%s rn=%d=%x\n", __func__, rn, r)); +    return r; +} + +void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) +{ +    MicroBlazeCPU *cpu = mb_env_get_cpu(env); +    unsigned int i; +    D(qemu_log("%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn])); + +    if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { +        qemu_log("MMU access on MMU-less system\n"); +        return; +    } + +    switch (rn) { +        /* Writes to HI/LO trig writes to the mmu rams.  */ +        case MMU_R_TLBLO: +        case MMU_R_TLBHI: +            i = env->mmu.regs[MMU_R_TLBX] & 0xff; +            if (rn == MMU_R_TLBHI) { +                if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0)) +                    qemu_log("invalidating index %x at pc=%x\n", +                             i, env->sregs[SR_PC]); +                env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; +                mmu_flush_idx(env, i); +            } +            env->mmu.rams[rn & 1][i] = v; + +            D(qemu_log("%s ram[%d][%d]=%x\n", __func__, rn & 1, i, v)); +            break; +        case MMU_R_ZPR: +            if (env->mmu.c_mmu_tlb_access <= 1) { +                qemu_log("Invalid access to MMU reg %d\n", rn); +                return; +            } + +            /* Changes to the zone protection reg flush the QEMU TLB. +               Fortunately, these are very uncommon.  */ +            if (v != env->mmu.regs[rn]) { +                tlb_flush(CPU(cpu), 1); +            } +            env->mmu.regs[rn] = v; +            break; +        case MMU_R_PID: +            if (env->mmu.c_mmu_tlb_access <= 1) { +                qemu_log("Invalid access to MMU reg %d\n", rn); +                return; +            } + +            if (v != env->mmu.regs[rn]) { +                mmu_change_pid(env, v); +                env->mmu.regs[rn] = v; +            } +            break; +        case MMU_R_TLBSX: +        { +            struct microblaze_mmu_lookup lu; +            int hit; + +            if (env->mmu.c_mmu_tlb_access <= 1) { +                qemu_log("Invalid access to MMU reg %d\n", rn); +                return; +            } + +            hit = mmu_translate(&env->mmu, &lu, +                                v & TLB_EPN_MASK, 0, cpu_mmu_index(env)); +            if (hit) { +                env->mmu.regs[MMU_R_TLBX] = lu.idx; +            } else +                env->mmu.regs[MMU_R_TLBX] |= 0x80000000; +            break; +        } +        default: +            env->mmu.regs[rn] = v; +            break; +   } +} + +void mmu_init(struct microblaze_mmu *mmu) +{ +    int i; +    for (i = 0; i < ARRAY_SIZE(mmu->regs); i++) { +        mmu->regs[i] = 0; +    } +} diff --git a/target-microblaze/mmu.h b/target-microblaze/mmu.h new file mode 100644 index 00000000..3b7a9983 --- /dev/null +++ b/target-microblaze/mmu.h @@ -0,0 +1,90 @@ +/* + *  Microblaze MMU emulation for qemu. + * + *  Copyright (c) 2009 Edgar E. Iglesias + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#define MMU_R_PID    0 +#define MMU_R_ZPR    1 +#define MMU_R_TLBX   2 +#define MMU_R_TLBLO  3 +#define MMU_R_TLBHI  4 +#define MMU_R_TLBSX  5 + +#define RAM_DATA     1 +#define RAM_TAG      0 + +/* Tag portion */ +#define TLB_EPN_MASK          0xFFFFFC00 /* Effective Page Number */ +#define TLB_PAGESZ_MASK       0x00000380 +#define TLB_PAGESZ(x)         (((x) & 0x7) << 7) +#define PAGESZ_1K             0 +#define PAGESZ_4K             1 +#define PAGESZ_16K            2 +#define PAGESZ_64K            3 +#define PAGESZ_256K           4 +#define PAGESZ_1M             5 +#define PAGESZ_4M             6 +#define PAGESZ_16M            7 +#define TLB_VALID             0x00000040 /* Entry is valid */ + +/* Data portion */ +#define TLB_RPN_MASK          0xFFFFFC00 /* Real Page Number */ +#define TLB_PERM_MASK         0x00000300 +#define TLB_EX                0x00000200 /* Instruction execution allowed */ +#define TLB_WR                0x00000100 /* Writes permitted */ +#define TLB_ZSEL_MASK         0x000000F0 +#define TLB_ZSEL(x)           (((x) & 0xF) << 4) +#define TLB_ATTR_MASK         0x0000000F +#define TLB_W                 0x00000008 /* Caching is write-through */ +#define TLB_I                 0x00000004 /* Caching is inhibited */ +#define TLB_M                 0x00000002 /* Memory is coherent */ +#define TLB_G                 0x00000001 /* Memory is guarded from prefetch */ + +#define TLB_ENTRIES    64 + +struct microblaze_mmu +{ +    /* Data and tag brams.  */ +    uint32_t rams[2][TLB_ENTRIES]; +    /* We keep a separate ram for the tids to avoid the 48 bit tag width.  */ +    uint8_t tids[TLB_ENTRIES]; +    /* Control flops.  */ +    uint32_t regs[8]; + +    int c_mmu; +    int c_mmu_tlb_access; +    int c_mmu_zones; +}; + +struct microblaze_mmu_lookup +{ +    uint32_t paddr; +    uint32_t vaddr; +    unsigned int size; +    unsigned int idx; +    int prot; +    enum { +        ERR_PROT, ERR_MISS, ERR_HIT +    } err; +}; + +unsigned int mmu_translate(struct microblaze_mmu *mmu, +                           struct microblaze_mmu_lookup *lu, +                           target_ulong vaddr, int rw, int mmu_idx); +uint32_t mmu_read(CPUMBState *env, uint32_t rn); +void mmu_write(CPUMBState *env, uint32_t rn, uint32_t v); +void mmu_init(struct microblaze_mmu *mmu); diff --git a/target-microblaze/op_helper.c b/target-microblaze/op_helper.c new file mode 100644 index 00000000..d2b36245 --- /dev/null +++ b/target-microblaze/op_helper.c @@ -0,0 +1,522 @@ +/* + *  Microblaze helper routines. + * + *  Copyright (c) 2009 Edgar E. Iglesias <edgar.iglesias@gmail.com>. + *  Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <assert.h> +#include "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "exec/cpu_ldst.h" + +#define D(x) + +#if !defined(CONFIG_USER_ONLY) + +/* Try to fill the TLB and return an exception if error. If retaddr is + * NULL, it means that the function was called in C code (i.e. not + * from generated code or from helper.c) + */ +void tlb_fill(CPUState *cs, target_ulong addr, int is_write, int mmu_idx, +              uintptr_t retaddr) +{ +    int ret; + +    ret = mb_cpu_handle_mmu_fault(cs, addr, is_write, mmu_idx); +    if (unlikely(ret)) { +        if (retaddr) { +            /* now we have a real cpu fault */ +            cpu_restore_state(cs, retaddr); +        } +        cpu_loop_exit(cs); +    } +} +#endif + +void helper_put(uint32_t id, uint32_t ctrl, uint32_t data) +{ +    int test = ctrl & STREAM_TEST; +    int atomic = ctrl & STREAM_ATOMIC; +    int control = ctrl & STREAM_CONTROL; +    int nonblock = ctrl & STREAM_NONBLOCK; +    int exception = ctrl & STREAM_EXCEPTION; + +    qemu_log("Unhandled stream put to stream-id=%d data=%x %s%s%s%s%s\n", +             id, data, +             test ? "t" : "", +             nonblock ? "n" : "", +             exception ? "e" : "", +             control ? "c" : "", +             atomic ? "a" : ""); +} + +uint32_t helper_get(uint32_t id, uint32_t ctrl) +{ +    int test = ctrl & STREAM_TEST; +    int atomic = ctrl & STREAM_ATOMIC; +    int control = ctrl & STREAM_CONTROL; +    int nonblock = ctrl & STREAM_NONBLOCK; +    int exception = ctrl & STREAM_EXCEPTION; + +    qemu_log("Unhandled stream get from stream-id=%d %s%s%s%s%s\n", +             id, +             test ? "t" : "", +             nonblock ? "n" : "", +             exception ? "e" : "", +             control ? "c" : "", +             atomic ? "a" : ""); +    return 0xdead0000 | id; +} + +void helper_raise_exception(CPUMBState *env, uint32_t index) +{ +    CPUState *cs = CPU(mb_env_get_cpu(env)); + +    cs->exception_index = index; +    cpu_loop_exit(cs); +} + +void helper_debug(CPUMBState *env) +{ +    int i; + +    qemu_log("PC=%8.8x\n", env->sregs[SR_PC]); +    qemu_log("rmsr=%x resr=%x rear=%x debug[%x] imm=%x iflags=%x\n", +             env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], +             env->debug, env->imm, env->iflags); +    qemu_log("btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", +             env->btaken, env->btarget, +             (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", +             (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", +             (env->sregs[SR_MSR] & MSR_EIP), +             (env->sregs[SR_MSR] & MSR_IE)); +    for (i = 0; i < 32; i++) { +        qemu_log("r%2.2d=%8.8x ", i, env->regs[i]); +        if ((i + 1) % 4 == 0) +            qemu_log("\n"); +    } +    qemu_log("\n\n"); +} + +static inline uint32_t compute_carry(uint32_t a, uint32_t b, uint32_t cin) +{ +    uint32_t cout = 0; + +    if ((b == ~0) && cin) +        cout = 1; +    else if ((~0 - a) < (b + cin)) +        cout = 1; +    return cout; +} + +uint32_t helper_cmp(uint32_t a, uint32_t b) +{ +    uint32_t t; + +    t = b + ~a + 1; +    if ((b & 0x80000000) ^ (a & 0x80000000)) +        t = (t & 0x7fffffff) | (b & 0x80000000); +    return t; +} + +uint32_t helper_cmpu(uint32_t a, uint32_t b) +{ +    uint32_t t; + +    t = b + ~a + 1; +    if ((b & 0x80000000) ^ (a & 0x80000000)) +        t = (t & 0x7fffffff) | (a & 0x80000000); +    return t; +} + +uint32_t helper_clz(uint32_t t0) +{ +    return clz32(t0); +} + +uint32_t helper_carry(uint32_t a, uint32_t b, uint32_t cf) +{ +    uint32_t ncf; +    ncf = compute_carry(a, b, cf); +    return ncf; +} + +static inline int div_prepare(CPUMBState *env, uint32_t a, uint32_t b) +{ +    if (b == 0) { +        env->sregs[SR_MSR] |= MSR_DZ; + +        if ((env->sregs[SR_MSR] & MSR_EE) +            && !(env->pvr.regs[2] & PVR2_DIV_ZERO_EXC_MASK)) { +            env->sregs[SR_ESR] = ESR_EC_DIVZERO; +            helper_raise_exception(env, EXCP_HW_EXCP); +        } +        return 0; +    } +    env->sregs[SR_MSR] &= ~MSR_DZ; +    return 1; +} + +uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b) +{ +    if (!div_prepare(env, a, b)) { +        return 0; +    } +    return (int32_t)a / (int32_t)b; +} + +uint32_t helper_divu(CPUMBState *env, uint32_t a, uint32_t b) +{ +    if (!div_prepare(env, a, b)) { +        return 0; +    } +    return a / b; +} + +/* raise FPU exception.  */ +static void raise_fpu_exception(CPUMBState *env) +{ +    env->sregs[SR_ESR] = ESR_EC_FPU; +    helper_raise_exception(env, EXCP_HW_EXCP); +} + +static void update_fpu_flags(CPUMBState *env, int flags) +{ +    int raise = 0; + +    if (flags & float_flag_invalid) { +        env->sregs[SR_FSR] |= FSR_IO; +        raise = 1; +    } +    if (flags & float_flag_divbyzero) { +        env->sregs[SR_FSR] |= FSR_DZ; +        raise = 1; +    } +    if (flags & float_flag_overflow) { +        env->sregs[SR_FSR] |= FSR_OF; +        raise = 1; +    } +    if (flags & float_flag_underflow) { +        env->sregs[SR_FSR] |= FSR_UF; +        raise = 1; +    } +    if (raise +        && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK) +        && (env->sregs[SR_MSR] & MSR_EE)) { +        raise_fpu_exception(env); +    } +} + +uint32_t helper_fadd(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fd, fa, fb; +    int flags; + +    set_float_exception_flags(0, &env->fp_status); +    fa.l = a; +    fb.l = b; +    fd.f = float32_add(fa.f, fb.f, &env->fp_status); + +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags); +    return fd.l; +} + +uint32_t helper_frsub(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fd, fa, fb; +    int flags; + +    set_float_exception_flags(0, &env->fp_status); +    fa.l = a; +    fb.l = b; +    fd.f = float32_sub(fb.f, fa.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags); +    return fd.l; +} + +uint32_t helper_fmul(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fd, fa, fb; +    int flags; + +    set_float_exception_flags(0, &env->fp_status); +    fa.l = a; +    fb.l = b; +    fd.f = float32_mul(fa.f, fb.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags); + +    return fd.l; +} + +uint32_t helper_fdiv(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fd, fa, fb; +    int flags; + +    set_float_exception_flags(0, &env->fp_status); +    fa.l = a; +    fb.l = b; +    fd.f = float32_div(fb.f, fa.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags); + +    return fd.l; +} + +uint32_t helper_fcmp_un(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fa, fb; +    uint32_t r = 0; + +    fa.l = a; +    fb.l = b; + +    if (float32_is_signaling_nan(fa.f) || float32_is_signaling_nan(fb.f)) { +        update_fpu_flags(env, float_flag_invalid); +        r = 1; +    } + +    if (float32_is_quiet_nan(fa.f) || float32_is_quiet_nan(fb.f)) { +        r = 1; +    } + +    return r; +} + +uint32_t helper_fcmp_lt(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fa, fb; +    int r; +    int flags; + +    set_float_exception_flags(0, &env->fp_status); +    fa.l = a; +    fb.l = b; +    r = float32_lt(fb.f, fa.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags & float_flag_invalid); + +    return r; +} + +uint32_t helper_fcmp_eq(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fa, fb; +    int flags; +    int r; + +    set_float_exception_flags(0, &env->fp_status); +    fa.l = a; +    fb.l = b; +    r = float32_eq_quiet(fa.f, fb.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags & float_flag_invalid); + +    return r; +} + +uint32_t helper_fcmp_le(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fa, fb; +    int flags; +    int r; + +    fa.l = a; +    fb.l = b; +    set_float_exception_flags(0, &env->fp_status); +    r = float32_le(fa.f, fb.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags & float_flag_invalid); + + +    return r; +} + +uint32_t helper_fcmp_gt(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fa, fb; +    int flags, r; + +    fa.l = a; +    fb.l = b; +    set_float_exception_flags(0, &env->fp_status); +    r = float32_lt(fa.f, fb.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags & float_flag_invalid); +    return r; +} + +uint32_t helper_fcmp_ne(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fa, fb; +    int flags, r; + +    fa.l = a; +    fb.l = b; +    set_float_exception_flags(0, &env->fp_status); +    r = !float32_eq_quiet(fa.f, fb.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags & float_flag_invalid); + +    return r; +} + +uint32_t helper_fcmp_ge(CPUMBState *env, uint32_t a, uint32_t b) +{ +    CPU_FloatU fa, fb; +    int flags, r; + +    fa.l = a; +    fb.l = b; +    set_float_exception_flags(0, &env->fp_status); +    r = !float32_lt(fa.f, fb.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags & float_flag_invalid); + +    return r; +} + +uint32_t helper_flt(CPUMBState *env, uint32_t a) +{ +    CPU_FloatU fd, fa; + +    fa.l = a; +    fd.f = int32_to_float32(fa.l, &env->fp_status); +    return fd.l; +} + +uint32_t helper_fint(CPUMBState *env, uint32_t a) +{ +    CPU_FloatU fa; +    uint32_t r; +    int flags; + +    set_float_exception_flags(0, &env->fp_status); +    fa.l = a; +    r = float32_to_int32(fa.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags); + +    return r; +} + +uint32_t helper_fsqrt(CPUMBState *env, uint32_t a) +{ +    CPU_FloatU fd, fa; +    int flags; + +    set_float_exception_flags(0, &env->fp_status); +    fa.l = a; +    fd.l = float32_sqrt(fa.f, &env->fp_status); +    flags = get_float_exception_flags(&env->fp_status); +    update_fpu_flags(env, flags); + +    return fd.l; +} + +uint32_t helper_pcmpbf(uint32_t a, uint32_t b) +{ +    unsigned int i; +    uint32_t mask = 0xff000000; + +    for (i = 0; i < 4; i++) { +        if ((a & mask) == (b & mask)) +            return i + 1; +        mask >>= 8; +    } +    return 0; +} + +void helper_memalign(CPUMBState *env, uint32_t addr, uint32_t dr, uint32_t wr, +                     uint32_t mask) +{ +    if (addr & mask) { +            qemu_log_mask(CPU_LOG_INT, +                          "unaligned access addr=%x mask=%x, wr=%d dr=r%d\n", +                          addr, mask, wr, dr); +            env->sregs[SR_EAR] = addr; +            env->sregs[SR_ESR] = ESR_EC_UNALIGNED_DATA | (wr << 10) \ +                                 | (dr & 31) << 5; +            if (mask == 3) { +                env->sregs[SR_ESR] |= 1 << 11; +            } +            if (!(env->sregs[SR_MSR] & MSR_EE)) { +                return; +            } +            helper_raise_exception(env, EXCP_HW_EXCP); +    } +} + +void helper_stackprot(CPUMBState *env, uint32_t addr) +{ +    if (addr < env->slr || addr > env->shr) { +        qemu_log("Stack protector violation at %x %x %x\n", +                 addr, env->slr, env->shr); +        env->sregs[SR_EAR] = addr; +        env->sregs[SR_ESR] = ESR_EC_STACKPROT; +        helper_raise_exception(env, EXCP_HW_EXCP); +    } +} + +#if !defined(CONFIG_USER_ONLY) +/* Writes/reads to the MMU's special regs end up here.  */ +uint32_t helper_mmu_read(CPUMBState *env, uint32_t rn) +{ +    return mmu_read(env, rn); +} + +void helper_mmu_write(CPUMBState *env, uint32_t rn, uint32_t v) +{ +    mmu_write(env, rn, v); +} + +void mb_cpu_unassigned_access(CPUState *cs, hwaddr addr, +                              bool is_write, bool is_exec, int is_asi, +                              unsigned size) +{ +    MicroBlazeCPU *cpu; +    CPUMBState *env; + +    qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n", +             addr, is_write ? 1 : 0, is_exec ? 1 : 0); +    if (cs == NULL) { +        return; +    } +    cpu = MICROBLAZE_CPU(cs); +    env = &cpu->env; +    if (!(env->sregs[SR_MSR] & MSR_EE)) { +        return; +    } + +    env->sregs[SR_EAR] = addr; +    if (is_exec) { +        if ((env->pvr.regs[2] & PVR2_IOPB_BUS_EXC_MASK)) { +            env->sregs[SR_ESR] = ESR_EC_INSN_BUS; +            helper_raise_exception(env, EXCP_HW_EXCP); +        } +    } else { +        if ((env->pvr.regs[2] & PVR2_DOPB_BUS_EXC_MASK)) { +            env->sregs[SR_ESR] = ESR_EC_DATA_BUS; +            helper_raise_exception(env, EXCP_HW_EXCP); +        } +    } +} +#endif diff --git a/target-microblaze/translate.c b/target-microblaze/translate.c new file mode 100644 index 00000000..f4e969b2 --- /dev/null +++ b/target-microblaze/translate.c @@ -0,0 +1,1957 @@ +/* + *  Xilinx MicroBlaze emulation for qemu: main translation routines. + * + *  Copyright (c) 2009 Edgar E. Iglesias. + *  Copyright (c) 2009-2012 PetaLogix Qld Pty Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "cpu.h" +#include "disas/disas.h" +#include "tcg-op.h" +#include "exec/helper-proto.h" +#include "microblaze-decode.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-gen.h" + +#include "trace-tcg.h" + + +#define SIM_COMPAT 0 +#define DISAS_GNU 1 +#define DISAS_MB 1 +#if DISAS_MB && !SIM_COMPAT +#  define LOG_DIS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) +#else +#  define LOG_DIS(...) do { } while (0) +#endif + +#define D(x) + +#define EXTRACT_FIELD(src, start, end) \ +            (((src) >> start) & ((1 << (end - start + 1)) - 1)) + +static TCGv env_debug; +static TCGv_ptr cpu_env; +static TCGv cpu_R[32]; +static TCGv cpu_SR[18]; +static TCGv env_imm; +static TCGv env_btaken; +static TCGv env_btarget; +static TCGv env_iflags; +static TCGv env_res_addr; +static TCGv env_res_val; + +#include "exec/gen-icount.h" + +/* This is the state at translation time.  */ +typedef struct DisasContext { +    MicroBlazeCPU *cpu; +    target_ulong pc; + +    /* Decoder.  */ +    int type_b; +    uint32_t ir; +    uint8_t opcode; +    uint8_t rd, ra, rb; +    uint16_t imm; + +    unsigned int cpustate_changed; +    unsigned int delayed_branch; +    unsigned int tb_flags, synced_flags; /* tb dependent flags.  */ +    unsigned int clear_imm; +    int is_jmp; + +#define JMP_NOJMP     0 +#define JMP_DIRECT    1 +#define JMP_DIRECT_CC 2 +#define JMP_INDIRECT  3 +    unsigned int jmp; +    uint32_t jmp_pc; + +    int abort_at_next_insn; +    int nr_nops; +    struct TranslationBlock *tb; +    int singlestep_enabled; +} DisasContext; + +static const char *regnames[] = +{ +    "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", +    "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", +    "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", +    "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", +}; + +static const char *special_regnames[] = +{ +    "rpc", "rmsr", "sr2", "sr3", "sr4", "sr5", "sr6", "sr7", +    "sr8", "sr9", "sr10", "sr11", "sr12", "sr13", "sr14", "sr15", +    "sr16", "sr17", "sr18" +}; + +static inline void t_sync_flags(DisasContext *dc) +{ +    /* Synch the tb dependent flags between translator and runtime.  */ +    if (dc->tb_flags != dc->synced_flags) { +        tcg_gen_movi_tl(env_iflags, dc->tb_flags); +        dc->synced_flags = dc->tb_flags; +    } +} + +static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) +{ +    TCGv_i32 tmp = tcg_const_i32(index); + +    t_sync_flags(dc); +    tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); +    gen_helper_raise_exception(cpu_env, tmp); +    tcg_temp_free_i32(tmp); +    dc->is_jmp = DISAS_UPDATE; +} + +static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) +{ +    TranslationBlock *tb; +    tb = dc->tb; +    if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) { +        tcg_gen_goto_tb(n); +        tcg_gen_movi_tl(cpu_SR[SR_PC], dest); +        tcg_gen_exit_tb((uintptr_t)tb + n); +    } else { +        tcg_gen_movi_tl(cpu_SR[SR_PC], dest); +        tcg_gen_exit_tb(0); +    } +} + +static void read_carry(DisasContext *dc, TCGv d) +{ +    tcg_gen_shri_tl(d, cpu_SR[SR_MSR], 31); +} + +/* + * write_carry sets the carry bits in MSR based on bit 0 of v. + * v[31:1] are ignored. + */ +static void write_carry(DisasContext *dc, TCGv v) +{ +    TCGv t0 = tcg_temp_new(); +    tcg_gen_shli_tl(t0, v, 31); +    tcg_gen_sari_tl(t0, t0, 31); +    tcg_gen_andi_tl(t0, t0, (MSR_C | MSR_CC)); +    tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], +                    ~(MSR_C | MSR_CC)); +    tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0); +    tcg_temp_free(t0); +} + +static void write_carryi(DisasContext *dc, bool carry) +{ +    TCGv t0 = tcg_temp_new(); +    tcg_gen_movi_tl(t0, carry); +    write_carry(dc, t0); +    tcg_temp_free(t0); +} + +/* True if ALU operand b is a small immediate that may deserve +   faster treatment.  */ +static inline int dec_alu_op_b_is_small_imm(DisasContext *dc) +{ +    /* Immediate insn without the imm prefix ?  */ +    return dc->type_b && !(dc->tb_flags & IMM_FLAG); +} + +static inline TCGv *dec_alu_op_b(DisasContext *dc) +{ +    if (dc->type_b) { +        if (dc->tb_flags & IMM_FLAG) +            tcg_gen_ori_tl(env_imm, env_imm, dc->imm); +        else +            tcg_gen_movi_tl(env_imm, (int32_t)((int16_t)dc->imm)); +        return &env_imm; +    } else +        return &cpu_R[dc->rb]; +} + +static void dec_add(DisasContext *dc) +{ +    unsigned int k, c; +    TCGv cf; + +    k = dc->opcode & 4; +    c = dc->opcode & 2; + +    LOG_DIS("add%s%s%s r%d r%d r%d\n", +            dc->type_b ? "i" : "", k ? "k" : "", c ? "c" : "", +            dc->rd, dc->ra, dc->rb); + +    /* Take care of the easy cases first.  */ +    if (k) { +        /* k - keep carry, no need to update MSR.  */ +        /* If rd == r0, it's a nop.  */ +        if (dc->rd) { +            tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + +            if (c) { +                /* c - Add carry into the result.  */ +                cf = tcg_temp_new(); + +                read_carry(dc, cf); +                tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); +                tcg_temp_free(cf); +            } +        } +        return; +    } + +    /* From now on, we can assume k is zero.  So we need to update MSR.  */ +    /* Extract carry.  */ +    cf = tcg_temp_new(); +    if (c) { +        read_carry(dc, cf); +    } else { +        tcg_gen_movi_tl(cf, 0); +    } + +    if (dc->rd) { +        TCGv ncf = tcg_temp_new(); +        gen_helper_carry(ncf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); +        tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +        tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); +        write_carry(dc, ncf); +        tcg_temp_free(ncf); +    } else { +        gen_helper_carry(cf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); +        write_carry(dc, cf); +    } +    tcg_temp_free(cf); +} + +static void dec_sub(DisasContext *dc) +{ +    unsigned int u, cmp, k, c; +    TCGv cf, na; + +    u = dc->imm & 2; +    k = dc->opcode & 4; +    c = dc->opcode & 2; +    cmp = (dc->imm & 1) && (!dc->type_b) && k; + +    if (cmp) { +        LOG_DIS("cmp%s r%d, r%d ir=%x\n", u ? "u" : "", dc->rd, dc->ra, dc->ir); +        if (dc->rd) { +            if (u) +                gen_helper_cmpu(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); +            else +                gen_helper_cmp(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); +        } +        return; +    } + +    LOG_DIS("sub%s%s r%d, r%d r%d\n", +             k ? "k" : "",  c ? "c" : "", dc->rd, dc->ra, dc->rb); + +    /* Take care of the easy cases first.  */ +    if (k) { +        /* k - keep carry, no need to update MSR.  */ +        /* If rd == r0, it's a nop.  */ +        if (dc->rd) { +            tcg_gen_sub_tl(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + +            if (c) { +                /* c - Add carry into the result.  */ +                cf = tcg_temp_new(); + +                read_carry(dc, cf); +                tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); +                tcg_temp_free(cf); +            } +        } +        return; +    } + +    /* From now on, we can assume k is zero.  So we need to update MSR.  */ +    /* Extract carry. And complement a into na.  */ +    cf = tcg_temp_new(); +    na = tcg_temp_new(); +    if (c) { +        read_carry(dc, cf); +    } else { +        tcg_gen_movi_tl(cf, 1); +    } + +    /* d = b + ~a + c. carry defaults to 1.  */ +    tcg_gen_not_tl(na, cpu_R[dc->ra]); + +    if (dc->rd) { +        TCGv ncf = tcg_temp_new(); +        gen_helper_carry(ncf, na, *(dec_alu_op_b(dc)), cf); +        tcg_gen_add_tl(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); +        tcg_gen_add_tl(cpu_R[dc->rd], cpu_R[dc->rd], cf); +        write_carry(dc, ncf); +        tcg_temp_free(ncf); +    } else { +        gen_helper_carry(cf, na, *(dec_alu_op_b(dc)), cf); +        write_carry(dc, cf); +    } +    tcg_temp_free(cf); +    tcg_temp_free(na); +} + +static void dec_pattern(DisasContext *dc) +{ +    unsigned int mode; +    TCGLabel *l1; + +    if ((dc->tb_flags & MSR_EE_FLAG) +          && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) +          && !((dc->cpu->env.pvr.regs[2] & PVR2_USE_PCMP_INSTR))) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +    } + +    mode = dc->opcode & 3; +    switch (mode) { +        case 0: +            /* pcmpbf.  */ +            LOG_DIS("pcmpbf r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); +            if (dc->rd) +                gen_helper_pcmpbf(cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); +            break; +        case 2: +            LOG_DIS("pcmpeq r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); +            if (dc->rd) { +                TCGv t0 = tcg_temp_local_new(); +                l1 = gen_new_label(); +                tcg_gen_movi_tl(t0, 1); +                tcg_gen_brcond_tl(TCG_COND_EQ, +                                  cpu_R[dc->ra], cpu_R[dc->rb], l1); +                tcg_gen_movi_tl(t0, 0); +                gen_set_label(l1); +                tcg_gen_mov_tl(cpu_R[dc->rd], t0); +                tcg_temp_free(t0); +            } +            break; +        case 3: +            LOG_DIS("pcmpne r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); +            l1 = gen_new_label(); +            if (dc->rd) { +                TCGv t0 = tcg_temp_local_new(); +                tcg_gen_movi_tl(t0, 1); +                tcg_gen_brcond_tl(TCG_COND_NE, +                                  cpu_R[dc->ra], cpu_R[dc->rb], l1); +                tcg_gen_movi_tl(t0, 0); +                gen_set_label(l1); +                tcg_gen_mov_tl(cpu_R[dc->rd], t0); +                tcg_temp_free(t0); +            } +            break; +        default: +            cpu_abort(CPU(dc->cpu), +                      "unsupported pattern insn opcode=%x\n", dc->opcode); +            break; +    } +} + +static void dec_and(DisasContext *dc) +{ +    unsigned int not; + +    if (!dc->type_b && (dc->imm & (1 << 10))) { +        dec_pattern(dc); +        return; +    } + +    not = dc->opcode & (1 << 1); +    LOG_DIS("and%s\n", not ? "n" : ""); + +    if (!dc->rd) +        return; + +    if (not) { +        tcg_gen_andc_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +    } else +        tcg_gen_and_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +} + +static void dec_or(DisasContext *dc) +{ +    if (!dc->type_b && (dc->imm & (1 << 10))) { +        dec_pattern(dc); +        return; +    } + +    LOG_DIS("or r%d r%d r%d imm=%x\n", dc->rd, dc->ra, dc->rb, dc->imm); +    if (dc->rd) +        tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +} + +static void dec_xor(DisasContext *dc) +{ +    if (!dc->type_b && (dc->imm & (1 << 10))) { +        dec_pattern(dc); +        return; +    } + +    LOG_DIS("xor r%d\n", dc->rd); +    if (dc->rd) +        tcg_gen_xor_tl(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +} + +static inline void msr_read(DisasContext *dc, TCGv d) +{ +    tcg_gen_mov_tl(d, cpu_SR[SR_MSR]); +} + +static inline void msr_write(DisasContext *dc, TCGv v) +{ +    TCGv t; + +    t = tcg_temp_new(); +    dc->cpustate_changed = 1; +    /* PVR bit is not writable.  */ +    tcg_gen_andi_tl(t, v, ~MSR_PVR); +    tcg_gen_andi_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); +    tcg_gen_or_tl(cpu_SR[SR_MSR], cpu_SR[SR_MSR], v); +    tcg_temp_free(t); +} + +static void dec_msr(DisasContext *dc) +{ +    CPUState *cs = CPU(dc->cpu); +    TCGv t0, t1; +    unsigned int sr, to, rn; +    int mem_index = cpu_mmu_index(&dc->cpu->env); + +    sr = dc->imm & ((1 << 14) - 1); +    to = dc->imm & (1 << 14); +    dc->type_b = 1; +    if (to) +        dc->cpustate_changed = 1; + +    /* msrclr and msrset.  */ +    if (!(dc->imm & (1 << 15))) { +        unsigned int clr = dc->ir & (1 << 16); + +        LOG_DIS("msr%s r%d imm=%x\n", clr ? "clr" : "set", +                dc->rd, dc->imm); + +        if (!(dc->cpu->env.pvr.regs[2] & PVR2_USE_MSR_INSTR)) { +            /* nop??? */ +            return; +        } + +        if ((dc->tb_flags & MSR_EE_FLAG) +            && mem_index == MMU_USER_IDX && (dc->imm != 4 && dc->imm != 0)) { +            tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); +            t_gen_raise_exception(dc, EXCP_HW_EXCP); +            return; +        } + +        if (dc->rd) +            msr_read(dc, cpu_R[dc->rd]); + +        t0 = tcg_temp_new(); +        t1 = tcg_temp_new(); +        msr_read(dc, t0); +        tcg_gen_mov_tl(t1, *(dec_alu_op_b(dc))); + +        if (clr) { +            tcg_gen_not_tl(t1, t1); +            tcg_gen_and_tl(t0, t0, t1); +        } else +            tcg_gen_or_tl(t0, t0, t1); +        msr_write(dc, t0); +        tcg_temp_free(t0); +        tcg_temp_free(t1); +	tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4); +        dc->is_jmp = DISAS_UPDATE; +        return; +    } + +    if (to) { +        if ((dc->tb_flags & MSR_EE_FLAG) +             && mem_index == MMU_USER_IDX) { +            tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); +            t_gen_raise_exception(dc, EXCP_HW_EXCP); +            return; +        } +    } + +#if !defined(CONFIG_USER_ONLY) +    /* Catch read/writes to the mmu block.  */ +    if ((sr & ~0xff) == 0x1000) { +        sr &= 7; +        LOG_DIS("m%ss sr%d r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); +        if (to) +            gen_helper_mmu_write(cpu_env, tcg_const_tl(sr), cpu_R[dc->ra]); +        else +            gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tcg_const_tl(sr)); +        return; +    } +#endif + +    if (to) { +        LOG_DIS("m%ss sr%x r%d imm=%x\n", to ? "t" : "f", sr, dc->ra, dc->imm); +        switch (sr) { +            case 0: +                break; +            case 1: +                msr_write(dc, cpu_R[dc->ra]); +                break; +            case 0x3: +                tcg_gen_mov_tl(cpu_SR[SR_EAR], cpu_R[dc->ra]); +                break; +            case 0x5: +                tcg_gen_mov_tl(cpu_SR[SR_ESR], cpu_R[dc->ra]); +                break; +            case 0x7: +                tcg_gen_andi_tl(cpu_SR[SR_FSR], cpu_R[dc->ra], 31); +                break; +            case 0x800: +                tcg_gen_st_tl(cpu_R[dc->ra], cpu_env, offsetof(CPUMBState, slr)); +                break; +            case 0x802: +                tcg_gen_st_tl(cpu_R[dc->ra], cpu_env, offsetof(CPUMBState, shr)); +                break; +            default: +                cpu_abort(CPU(dc->cpu), "unknown mts reg %x\n", sr); +                break; +        } +    } else { +        LOG_DIS("m%ss r%d sr%x imm=%x\n", to ? "t" : "f", dc->rd, sr, dc->imm); + +        switch (sr) { +            case 0: +                tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); +                break; +            case 1: +                msr_read(dc, cpu_R[dc->rd]); +                break; +            case 0x3: +                tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_EAR]); +                break; +            case 0x5: +                tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_ESR]); +                break; +             case 0x7: +                tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_FSR]); +                break; +            case 0xb: +                tcg_gen_mov_tl(cpu_R[dc->rd], cpu_SR[SR_BTR]); +                break; +            case 0x800: +                tcg_gen_ld_tl(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, slr)); +                break; +            case 0x802: +                tcg_gen_ld_tl(cpu_R[dc->rd], cpu_env, offsetof(CPUMBState, shr)); +                break; +            case 0x2000: +            case 0x2001: +            case 0x2002: +            case 0x2003: +            case 0x2004: +            case 0x2005: +            case 0x2006: +            case 0x2007: +            case 0x2008: +            case 0x2009: +            case 0x200a: +            case 0x200b: +            case 0x200c: +                rn = sr & 0xf; +                tcg_gen_ld_tl(cpu_R[dc->rd], +                              cpu_env, offsetof(CPUMBState, pvr.regs[rn])); +                break; +            default: +                cpu_abort(cs, "unknown mfs reg %x\n", sr); +                break; +        } +    } + +    if (dc->rd == 0) { +        tcg_gen_movi_tl(cpu_R[0], 0); +    } +} + +/* 64-bit signed mul, lower result in d and upper in d2.  */ +static void t_gen_muls(TCGv d, TCGv d2, TCGv a, TCGv b) +{ +    TCGv_i64 t0, t1; + +    t0 = tcg_temp_new_i64(); +    t1 = tcg_temp_new_i64(); + +    tcg_gen_ext_i32_i64(t0, a); +    tcg_gen_ext_i32_i64(t1, b); +    tcg_gen_mul_i64(t0, t0, t1); + +    tcg_gen_trunc_i64_i32(d, t0); +    tcg_gen_shri_i64(t0, t0, 32); +    tcg_gen_trunc_i64_i32(d2, t0); + +    tcg_temp_free_i64(t0); +    tcg_temp_free_i64(t1); +} + +/* 64-bit unsigned muls, lower result in d and upper in d2.  */ +static void t_gen_mulu(TCGv d, TCGv d2, TCGv a, TCGv b) +{ +    TCGv_i64 t0, t1; + +    t0 = tcg_temp_new_i64(); +    t1 = tcg_temp_new_i64(); + +    tcg_gen_extu_i32_i64(t0, a); +    tcg_gen_extu_i32_i64(t1, b); +    tcg_gen_mul_i64(t0, t0, t1); + +    tcg_gen_trunc_i64_i32(d, t0); +    tcg_gen_shri_i64(t0, t0, 32); +    tcg_gen_trunc_i64_i32(d2, t0); + +    tcg_temp_free_i64(t0); +    tcg_temp_free_i64(t1); +} + +/* Multiplier unit.  */ +static void dec_mul(DisasContext *dc) +{ +    TCGv d[2]; +    unsigned int subcode; + +    if ((dc->tb_flags & MSR_EE_FLAG) +         && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) +         && !(dc->cpu->env.pvr.regs[0] & PVR0_USE_HW_MUL_MASK)) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +        return; +    } + +    subcode = dc->imm & 3; +    d[0] = tcg_temp_new(); +    d[1] = tcg_temp_new(); + +    if (dc->type_b) { +        LOG_DIS("muli r%d r%d %x\n", dc->rd, dc->ra, dc->imm); +        t_gen_mulu(cpu_R[dc->rd], d[1], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +        goto done; +    } + +    /* mulh, mulhsu and mulhu are not available if C_USE_HW_MUL is < 2.  */ +    if (subcode >= 1 && subcode <= 3 +        && !((dc->cpu->env.pvr.regs[2] & PVR2_USE_MUL64_MASK))) { +        /* nop??? */ +    } + +    switch (subcode) { +        case 0: +            LOG_DIS("mul r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); +            t_gen_mulu(cpu_R[dc->rd], d[1], cpu_R[dc->ra], cpu_R[dc->rb]); +            break; +        case 1: +            LOG_DIS("mulh r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); +            t_gen_muls(d[0], cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); +            break; +        case 2: +            LOG_DIS("mulhsu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); +            t_gen_muls(d[0], cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); +            break; +        case 3: +            LOG_DIS("mulhu r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); +            t_gen_mulu(d[0], cpu_R[dc->rd], cpu_R[dc->ra], cpu_R[dc->rb]); +            break; +        default: +            cpu_abort(CPU(dc->cpu), "unknown MUL insn %x\n", subcode); +            break; +    } +done: +    tcg_temp_free(d[0]); +    tcg_temp_free(d[1]); +} + +/* Div unit.  */ +static void dec_div(DisasContext *dc) +{ +    unsigned int u; + +    u = dc->imm & 2;  +    LOG_DIS("div\n"); + +    if ((dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) +          && !((dc->cpu->env.pvr.regs[0] & PVR0_USE_DIV_MASK))) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +    } + +    if (u) +        gen_helper_divu(cpu_R[dc->rd], cpu_env, *(dec_alu_op_b(dc)), +                        cpu_R[dc->ra]); +    else +        gen_helper_divs(cpu_R[dc->rd], cpu_env, *(dec_alu_op_b(dc)), +                        cpu_R[dc->ra]); +    if (!dc->rd) +        tcg_gen_movi_tl(cpu_R[dc->rd], 0); +} + +static void dec_barrel(DisasContext *dc) +{ +    TCGv t0; +    unsigned int s, t; + +    if ((dc->tb_flags & MSR_EE_FLAG) +          && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) +          && !(dc->cpu->env.pvr.regs[0] & PVR0_USE_BARREL_MASK)) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +        return; +    } + +    s = dc->imm & (1 << 10); +    t = dc->imm & (1 << 9); + +    LOG_DIS("bs%s%s r%d r%d r%d\n", +            s ? "l" : "r", t ? "a" : "l", dc->rd, dc->ra, dc->rb); + +    t0 = tcg_temp_new(); + +    tcg_gen_mov_tl(t0, *(dec_alu_op_b(dc))); +    tcg_gen_andi_tl(t0, t0, 31); + +    if (s) +        tcg_gen_shl_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); +    else { +        if (t) +            tcg_gen_sar_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); +        else +            tcg_gen_shr_tl(cpu_R[dc->rd], cpu_R[dc->ra], t0); +    } +} + +static void dec_bit(DisasContext *dc) +{ +    CPUState *cs = CPU(dc->cpu); +    TCGv t0; +    unsigned int op; +    int mem_index = cpu_mmu_index(&dc->cpu->env); + +    op = dc->ir & ((1 << 9) - 1); +    switch (op) { +        case 0x21: +            /* src.  */ +            t0 = tcg_temp_new(); + +            LOG_DIS("src r%d r%d\n", dc->rd, dc->ra); +            tcg_gen_andi_tl(t0, cpu_SR[SR_MSR], MSR_CC); +            write_carry(dc, cpu_R[dc->ra]); +            if (dc->rd) { +                tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); +                tcg_gen_or_tl(cpu_R[dc->rd], cpu_R[dc->rd], t0); +            } +            tcg_temp_free(t0); +            break; + +        case 0x1: +        case 0x41: +            /* srl.  */ +            LOG_DIS("srl r%d r%d\n", dc->rd, dc->ra); + +            /* Update carry. Note that write carry only looks at the LSB.  */ +            write_carry(dc, cpu_R[dc->ra]); +            if (dc->rd) { +                if (op == 0x41) +                    tcg_gen_shri_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); +                else +                    tcg_gen_sari_tl(cpu_R[dc->rd], cpu_R[dc->ra], 1); +            } +            break; +        case 0x60: +            LOG_DIS("ext8s r%d r%d\n", dc->rd, dc->ra); +            tcg_gen_ext8s_i32(cpu_R[dc->rd], cpu_R[dc->ra]); +            break; +        case 0x61: +            LOG_DIS("ext16s r%d r%d\n", dc->rd, dc->ra); +            tcg_gen_ext16s_i32(cpu_R[dc->rd], cpu_R[dc->ra]); +            break; +        case 0x64: +        case 0x66: +        case 0x74: +        case 0x76: +            /* wdc.  */ +            LOG_DIS("wdc r%d\n", dc->ra); +            if ((dc->tb_flags & MSR_EE_FLAG) +                 && mem_index == MMU_USER_IDX) { +                tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); +                t_gen_raise_exception(dc, EXCP_HW_EXCP); +                return; +            } +            break; +        case 0x68: +            /* wic.  */ +            LOG_DIS("wic r%d\n", dc->ra); +            if ((dc->tb_flags & MSR_EE_FLAG) +                 && mem_index == MMU_USER_IDX) { +                tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); +                t_gen_raise_exception(dc, EXCP_HW_EXCP); +                return; +            } +            break; +        case 0xe0: +            if ((dc->tb_flags & MSR_EE_FLAG) +                && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) +                && !((dc->cpu->env.pvr.regs[2] & PVR2_USE_PCMP_INSTR))) { +                tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +                t_gen_raise_exception(dc, EXCP_HW_EXCP); +            } +            if (dc->cpu->env.pvr.regs[2] & PVR2_USE_PCMP_INSTR) { +                gen_helper_clz(cpu_R[dc->rd], cpu_R[dc->ra]); +            } +            break; +        case 0x1e0: +            /* swapb */ +            LOG_DIS("swapb r%d r%d\n", dc->rd, dc->ra); +            tcg_gen_bswap32_i32(cpu_R[dc->rd], cpu_R[dc->ra]); +            break; +        case 0x1e2: +            /*swaph */ +            LOG_DIS("swaph r%d r%d\n", dc->rd, dc->ra); +            tcg_gen_rotri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 16); +            break; +        default: +            cpu_abort(cs, "unknown bit oc=%x op=%x rd=%d ra=%d rb=%d\n", +                      dc->pc, op, dc->rd, dc->ra, dc->rb); +            break; +    } +} + +static inline void sync_jmpstate(DisasContext *dc) +{ +    if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { +        if (dc->jmp == JMP_DIRECT) { +            tcg_gen_movi_tl(env_btaken, 1); +        } +        dc->jmp = JMP_INDIRECT; +        tcg_gen_movi_tl(env_btarget, dc->jmp_pc); +    } +} + +static void dec_imm(DisasContext *dc) +{ +    LOG_DIS("imm %x\n", dc->imm << 16); +    tcg_gen_movi_tl(env_imm, (dc->imm << 16)); +    dc->tb_flags |= IMM_FLAG; +    dc->clear_imm = 0; +} + +static inline TCGv *compute_ldst_addr(DisasContext *dc, TCGv *t) +{ +    unsigned int extimm = dc->tb_flags & IMM_FLAG; +    /* Should be set to one if r1 is used by loadstores.  */ +    int stackprot = 0; + +    /* All load/stores use ra.  */ +    if (dc->ra == 1 && dc->cpu->cfg.stackprot) { +        stackprot = 1; +    } + +    /* Treat the common cases first.  */ +    if (!dc->type_b) { +        /* If any of the regs is r0, return a ptr to the other.  */ +        if (dc->ra == 0) { +            return &cpu_R[dc->rb]; +        } else if (dc->rb == 0) { +            return &cpu_R[dc->ra]; +        } + +        if (dc->rb == 1 && dc->cpu->cfg.stackprot) { +            stackprot = 1; +        } + +        *t = tcg_temp_new(); +        tcg_gen_add_tl(*t, cpu_R[dc->ra], cpu_R[dc->rb]); + +        if (stackprot) { +            gen_helper_stackprot(cpu_env, *t); +        } +        return t; +    } +    /* Immediate.  */ +    if (!extimm) { +        if (dc->imm == 0) { +            return &cpu_R[dc->ra]; +        } +        *t = tcg_temp_new(); +        tcg_gen_movi_tl(*t, (int32_t)((int16_t)dc->imm)); +        tcg_gen_add_tl(*t, cpu_R[dc->ra], *t); +    } else { +        *t = tcg_temp_new(); +        tcg_gen_add_tl(*t, cpu_R[dc->ra], *(dec_alu_op_b(dc))); +    } + +    if (stackprot) { +        gen_helper_stackprot(cpu_env, *t); +    } +    return t; +} + +static void dec_load(DisasContext *dc) +{ +    TCGv t, v, *addr; +    unsigned int size, rev = 0, ex = 0; +    TCGMemOp mop; + +    mop = dc->opcode & 3; +    size = 1 << mop; +    if (!dc->type_b) { +        rev = (dc->ir >> 9) & 1; +        ex = (dc->ir >> 10) & 1; +    } +    mop |= MO_TE; +    if (rev) { +        mop ^= MO_BSWAP; +    } + +    if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) +          && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +        return; +    } + +    LOG_DIS("l%d%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", +                                                        ex ? "x" : ""); + +    t_sync_flags(dc); +    addr = compute_ldst_addr(dc, &t); + +    /* +     * When doing reverse accesses we need to do two things. +     * +     * 1. Reverse the address wrt endianness. +     * 2. Byteswap the data lanes on the way back into the CPU core. +     */ +    if (rev && size != 4) { +        /* Endian reverse the address. t is addr.  */ +        switch (size) { +            case 1: +            { +                /* 00 -> 11 +                   01 -> 10 +                   10 -> 10 +                   11 -> 00 */ +                TCGv low = tcg_temp_new(); + +                /* Force addr into the temp.  */ +                if (addr != &t) { +                    t = tcg_temp_new(); +                    tcg_gen_mov_tl(t, *addr); +                    addr = &t; +                } + +                tcg_gen_andi_tl(low, t, 3); +                tcg_gen_sub_tl(low, tcg_const_tl(3), low); +                tcg_gen_andi_tl(t, t, ~3); +                tcg_gen_or_tl(t, t, low); +                tcg_gen_mov_tl(env_imm, t); +                tcg_temp_free(low); +                break; +            } + +            case 2: +                /* 00 -> 10 +                   10 -> 00.  */ +                /* Force addr into the temp.  */ +                if (addr != &t) { +                    t = tcg_temp_new(); +                    tcg_gen_xori_tl(t, *addr, 2); +                    addr = &t; +                } else { +                    tcg_gen_xori_tl(t, t, 2); +                } +                break; +            default: +                cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); +                break; +        } +    } + +    /* lwx does not throw unaligned access errors, so force alignment */ +    if (ex) { +        /* Force addr into the temp.  */ +        if (addr != &t) { +            t = tcg_temp_new(); +            tcg_gen_mov_tl(t, *addr); +            addr = &t; +        } +        tcg_gen_andi_tl(t, t, ~3); +    } + +    /* If we get a fault on a dslot, the jmpstate better be in sync.  */ +    sync_jmpstate(dc); + +    /* Verify alignment if needed.  */ +    /* +     * Microblaze gives MMU faults priority over faults due to +     * unaligned addresses. That's why we speculatively do the load +     * into v. If the load succeeds, we verify alignment of the +     * address and if that succeeds we write into the destination reg. +     */ +    v = tcg_temp_new(); +    tcg_gen_qemu_ld_tl(v, *addr, cpu_mmu_index(&dc->cpu->env), mop); + +    if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { +        tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); +        gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), +                            tcg_const_tl(0), tcg_const_tl(size - 1)); +    } + +    if (ex) { +        tcg_gen_mov_tl(env_res_addr, *addr); +        tcg_gen_mov_tl(env_res_val, v); +    } +    if (dc->rd) { +        tcg_gen_mov_tl(cpu_R[dc->rd], v); +    } +    tcg_temp_free(v); + +    if (ex) { /* lwx */ +        /* no support for for AXI exclusive so always clear C */ +        write_carryi(dc, 0); +    } + +    if (addr == &t) +        tcg_temp_free(t); +} + +static void dec_store(DisasContext *dc) +{ +    TCGv t, *addr, swx_addr; +    TCGLabel *swx_skip = NULL; +    unsigned int size, rev = 0, ex = 0; +    TCGMemOp mop; + +    mop = dc->opcode & 3; +    size = 1 << mop; +    if (!dc->type_b) { +        rev = (dc->ir >> 9) & 1; +        ex = (dc->ir >> 10) & 1; +    } +    mop |= MO_TE; +    if (rev) { +        mop ^= MO_BSWAP; +    } + +    if (size > 4 && (dc->tb_flags & MSR_EE_FLAG) +          && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +        return; +    } + +    LOG_DIS("s%d%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", +                                                        ex ? "x" : ""); +    t_sync_flags(dc); +    /* If we get a fault on a dslot, the jmpstate better be in sync.  */ +    sync_jmpstate(dc); +    addr = compute_ldst_addr(dc, &t); + +    swx_addr = tcg_temp_local_new(); +    if (ex) { /* swx */ +        TCGv tval; + +        /* Force addr into the swx_addr. */ +        tcg_gen_mov_tl(swx_addr, *addr); +        addr = &swx_addr; +        /* swx does not throw unaligned access errors, so force alignment */ +        tcg_gen_andi_tl(swx_addr, swx_addr, ~3); + +        write_carryi(dc, 1); +        swx_skip = gen_new_label(); +        tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, swx_addr, swx_skip); + +        /* Compare the value loaded at lwx with current contents of +           the reserved location. +           FIXME: This only works for system emulation where we can expect +           this compare and the following write to be atomic. For user +           emulation we need to add atomicity between threads.  */ +        tval = tcg_temp_new(); +        tcg_gen_qemu_ld_tl(tval, swx_addr, cpu_mmu_index(&dc->cpu->env), +                           MO_TEUL); +        tcg_gen_brcond_tl(TCG_COND_NE, env_res_val, tval, swx_skip); +        write_carryi(dc, 0); +        tcg_temp_free(tval); +    } + +    if (rev && size != 4) { +        /* Endian reverse the address. t is addr.  */ +        switch (size) { +            case 1: +            { +                /* 00 -> 11 +                   01 -> 10 +                   10 -> 10 +                   11 -> 00 */ +                TCGv low = tcg_temp_new(); + +                /* Force addr into the temp.  */ +                if (addr != &t) { +                    t = tcg_temp_new(); +                    tcg_gen_mov_tl(t, *addr); +                    addr = &t; +                } + +                tcg_gen_andi_tl(low, t, 3); +                tcg_gen_sub_tl(low, tcg_const_tl(3), low); +                tcg_gen_andi_tl(t, t, ~3); +                tcg_gen_or_tl(t, t, low); +                tcg_gen_mov_tl(env_imm, t); +                tcg_temp_free(low); +                break; +            } + +            case 2: +                /* 00 -> 10 +                   10 -> 00.  */ +                /* Force addr into the temp.  */ +                if (addr != &t) { +                    t = tcg_temp_new(); +                    tcg_gen_xori_tl(t, *addr, 2); +                    addr = &t; +                } else { +                    tcg_gen_xori_tl(t, t, 2); +                } +                break; +            default: +                cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); +                break; +        } +    } +    tcg_gen_qemu_st_tl(cpu_R[dc->rd], *addr, cpu_mmu_index(&dc->cpu->env), mop); + +    /* Verify alignment if needed.  */ +    if ((dc->cpu->env.pvr.regs[2] & PVR2_UNALIGNED_EXC_MASK) && size > 1) { +        tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); +        /* FIXME: if the alignment is wrong, we should restore the value +         *        in memory. One possible way to achieve this is to probe +         *        the MMU prior to the memaccess, thay way we could put +         *        the alignment checks in between the probe and the mem +         *        access. +         */ +        gen_helper_memalign(cpu_env, *addr, tcg_const_tl(dc->rd), +                            tcg_const_tl(1), tcg_const_tl(size - 1)); +    } + +    if (ex) { +        gen_set_label(swx_skip); +    } +    tcg_temp_free(swx_addr); + +    if (addr == &t) +        tcg_temp_free(t); +} + +static inline void eval_cc(DisasContext *dc, unsigned int cc, +                           TCGv d, TCGv a, TCGv b) +{ +    switch (cc) { +        case CC_EQ: +            tcg_gen_setcond_tl(TCG_COND_EQ, d, a, b); +            break; +        case CC_NE: +            tcg_gen_setcond_tl(TCG_COND_NE, d, a, b); +            break; +        case CC_LT: +            tcg_gen_setcond_tl(TCG_COND_LT, d, a, b); +            break; +        case CC_LE: +            tcg_gen_setcond_tl(TCG_COND_LE, d, a, b); +            break; +        case CC_GE: +            tcg_gen_setcond_tl(TCG_COND_GE, d, a, b); +            break; +        case CC_GT: +            tcg_gen_setcond_tl(TCG_COND_GT, d, a, b); +            break; +        default: +            cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); +            break; +    } +} + +static void eval_cond_jmp(DisasContext *dc, TCGv pc_true, TCGv pc_false) +{ +    TCGLabel *l1 = gen_new_label(); +    /* Conditional jmp.  */ +    tcg_gen_mov_tl(cpu_SR[SR_PC], pc_false); +    tcg_gen_brcondi_tl(TCG_COND_EQ, env_btaken, 0, l1); +    tcg_gen_mov_tl(cpu_SR[SR_PC], pc_true); +    gen_set_label(l1); +} + +static void dec_bcc(DisasContext *dc) +{ +    unsigned int cc; +    unsigned int dslot; + +    cc = EXTRACT_FIELD(dc->ir, 21, 23); +    dslot = dc->ir & (1 << 25); +    LOG_DIS("bcc%s r%d %x\n", dslot ? "d" : "", dc->ra, dc->imm); + +    dc->delayed_branch = 1; +    if (dslot) { +        dc->delayed_branch = 2; +        dc->tb_flags |= D_FLAG; +        tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), +                      cpu_env, offsetof(CPUMBState, bimm)); +    } + +    if (dec_alu_op_b_is_small_imm(dc)) { +        int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend.  */ + +        tcg_gen_movi_tl(env_btarget, dc->pc + offset); +        dc->jmp = JMP_DIRECT_CC; +        dc->jmp_pc = dc->pc + offset; +    } else { +        dc->jmp = JMP_INDIRECT; +        tcg_gen_movi_tl(env_btarget, dc->pc); +        tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); +    } +    eval_cc(dc, cc, env_btaken, cpu_R[dc->ra], tcg_const_tl(0)); +} + +static void dec_br(DisasContext *dc) +{ +    unsigned int dslot, link, abs, mbar; +    int mem_index = cpu_mmu_index(&dc->cpu->env); + +    dslot = dc->ir & (1 << 20); +    abs = dc->ir & (1 << 19); +    link = dc->ir & (1 << 18); + +    /* Memory barrier.  */ +    mbar = (dc->ir >> 16) & 31; +    if (mbar == 2 && dc->imm == 4) { +        /* mbar IMM & 16 decodes to sleep.  */ +        if (dc->rd & 16) { +            TCGv_i32 tmp_hlt = tcg_const_i32(EXCP_HLT); +            TCGv_i32 tmp_1 = tcg_const_i32(1); + +            LOG_DIS("sleep\n"); + +            t_sync_flags(dc); +            tcg_gen_st_i32(tmp_1, cpu_env, +                           -offsetof(MicroBlazeCPU, env) +                           +offsetof(CPUState, halted)); +            tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc + 4); +            gen_helper_raise_exception(cpu_env, tmp_hlt); +            tcg_temp_free_i32(tmp_hlt); +            tcg_temp_free_i32(tmp_1); +            return; +        } +        LOG_DIS("mbar %d\n", dc->rd); +        /* Break the TB.  */ +        dc->cpustate_changed = 1; +        return; +    } + +    LOG_DIS("br%s%s%s%s imm=%x\n", +             abs ? "a" : "", link ? "l" : "", +             dc->type_b ? "i" : "", dslot ? "d" : "", +             dc->imm); + +    dc->delayed_branch = 1; +    if (dslot) { +        dc->delayed_branch = 2; +        dc->tb_flags |= D_FLAG; +        tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), +                      cpu_env, offsetof(CPUMBState, bimm)); +    } +    if (link && dc->rd) +        tcg_gen_movi_tl(cpu_R[dc->rd], dc->pc); + +    dc->jmp = JMP_INDIRECT; +    if (abs) { +        tcg_gen_movi_tl(env_btaken, 1); +        tcg_gen_mov_tl(env_btarget, *(dec_alu_op_b(dc))); +        if (link && !dslot) { +            if (!(dc->tb_flags & IMM_FLAG) && (dc->imm == 8 || dc->imm == 0x18)) +                t_gen_raise_exception(dc, EXCP_BREAK); +            if (dc->imm == 0) { +                if ((dc->tb_flags & MSR_EE_FLAG) && mem_index == MMU_USER_IDX) { +                    tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); +                    t_gen_raise_exception(dc, EXCP_HW_EXCP); +                    return; +                } + +                t_gen_raise_exception(dc, EXCP_DEBUG); +            } +        } +    } else { +        if (dec_alu_op_b_is_small_imm(dc)) { +            dc->jmp = JMP_DIRECT; +            dc->jmp_pc = dc->pc + (int32_t)((int16_t)dc->imm); +        } else { +            tcg_gen_movi_tl(env_btaken, 1); +            tcg_gen_movi_tl(env_btarget, dc->pc); +            tcg_gen_add_tl(env_btarget, env_btarget, *(dec_alu_op_b(dc))); +        } +    } +} + +static inline void do_rti(DisasContext *dc) +{ +    TCGv t0, t1; +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); +    tcg_gen_shri_tl(t0, cpu_SR[SR_MSR], 1); +    tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_IE); +    tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + +    tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); +    tcg_gen_or_tl(t1, t1, t0); +    msr_write(dc, t1); +    tcg_temp_free(t1); +    tcg_temp_free(t0); +    dc->tb_flags &= ~DRTI_FLAG; +} + +static inline void do_rtb(DisasContext *dc) +{ +    TCGv t0, t1; +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); +    tcg_gen_andi_tl(t1, cpu_SR[SR_MSR], ~MSR_BIP); +    tcg_gen_shri_tl(t0, t1, 1); +    tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + +    tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); +    tcg_gen_or_tl(t1, t1, t0); +    msr_write(dc, t1); +    tcg_temp_free(t1); +    tcg_temp_free(t0); +    dc->tb_flags &= ~DRTB_FLAG; +} + +static inline void do_rte(DisasContext *dc) +{ +    TCGv t0, t1; +    t0 = tcg_temp_new(); +    t1 = tcg_temp_new(); + +    tcg_gen_ori_tl(t1, cpu_SR[SR_MSR], MSR_EE); +    tcg_gen_andi_tl(t1, t1, ~MSR_EIP); +    tcg_gen_shri_tl(t0, t1, 1); +    tcg_gen_andi_tl(t0, t0, (MSR_VM | MSR_UM)); + +    tcg_gen_andi_tl(t1, t1, ~(MSR_VM | MSR_UM)); +    tcg_gen_or_tl(t1, t1, t0); +    msr_write(dc, t1); +    tcg_temp_free(t1); +    tcg_temp_free(t0); +    dc->tb_flags &= ~DRTE_FLAG; +} + +static void dec_rts(DisasContext *dc) +{ +    unsigned int b_bit, i_bit, e_bit; +    int mem_index = cpu_mmu_index(&dc->cpu->env); + +    i_bit = dc->ir & (1 << 21); +    b_bit = dc->ir & (1 << 22); +    e_bit = dc->ir & (1 << 23); + +    dc->delayed_branch = 2; +    dc->tb_flags |= D_FLAG; +    tcg_gen_st_tl(tcg_const_tl(dc->type_b && (dc->tb_flags & IMM_FLAG)), +                  cpu_env, offsetof(CPUMBState, bimm)); + +    if (i_bit) { +        LOG_DIS("rtid ir=%x\n", dc->ir); +        if ((dc->tb_flags & MSR_EE_FLAG) +             && mem_index == MMU_USER_IDX) { +            tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); +            t_gen_raise_exception(dc, EXCP_HW_EXCP); +        } +        dc->tb_flags |= DRTI_FLAG; +    } else if (b_bit) { +        LOG_DIS("rtbd ir=%x\n", dc->ir); +        if ((dc->tb_flags & MSR_EE_FLAG) +             && mem_index == MMU_USER_IDX) { +            tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); +            t_gen_raise_exception(dc, EXCP_HW_EXCP); +        } +        dc->tb_flags |= DRTB_FLAG; +    } else if (e_bit) { +        LOG_DIS("rted ir=%x\n", dc->ir); +        if ((dc->tb_flags & MSR_EE_FLAG) +             && mem_index == MMU_USER_IDX) { +            tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); +            t_gen_raise_exception(dc, EXCP_HW_EXCP); +        } +        dc->tb_flags |= DRTE_FLAG; +    } else +        LOG_DIS("rts ir=%x\n", dc->ir); + +    dc->jmp = JMP_INDIRECT; +    tcg_gen_movi_tl(env_btaken, 1); +    tcg_gen_add_tl(env_btarget, cpu_R[dc->ra], *(dec_alu_op_b(dc))); +} + +static int dec_check_fpuv2(DisasContext *dc) +{ +    if ((dc->cpu->cfg.use_fpu != 2) && (dc->tb_flags & MSR_EE_FLAG)) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_FPU); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +    } +    return (dc->cpu->cfg.use_fpu == 2) ? 0 : PVR2_USE_FPU2_MASK; +} + +static void dec_fpu(DisasContext *dc) +{ +    unsigned int fpu_insn; + +    if ((dc->tb_flags & MSR_EE_FLAG) +          && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) +          && (dc->cpu->cfg.use_fpu != 1)) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +        return; +    } + +    fpu_insn = (dc->ir >> 7) & 7; + +    switch (fpu_insn) { +        case 0: +            gen_helper_fadd(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], +                            cpu_R[dc->rb]); +            break; + +        case 1: +            gen_helper_frsub(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], +                             cpu_R[dc->rb]); +            break; + +        case 2: +            gen_helper_fmul(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], +                            cpu_R[dc->rb]); +            break; + +        case 3: +            gen_helper_fdiv(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], +                            cpu_R[dc->rb]); +            break; + +        case 4: +            switch ((dc->ir >> 4) & 7) { +                case 0: +                    gen_helper_fcmp_un(cpu_R[dc->rd], cpu_env, +                                       cpu_R[dc->ra], cpu_R[dc->rb]); +                    break; +                case 1: +                    gen_helper_fcmp_lt(cpu_R[dc->rd], cpu_env, +                                       cpu_R[dc->ra], cpu_R[dc->rb]); +                    break; +                case 2: +                    gen_helper_fcmp_eq(cpu_R[dc->rd], cpu_env, +                                       cpu_R[dc->ra], cpu_R[dc->rb]); +                    break; +                case 3: +                    gen_helper_fcmp_le(cpu_R[dc->rd], cpu_env, +                                       cpu_R[dc->ra], cpu_R[dc->rb]); +                    break; +                case 4: +                    gen_helper_fcmp_gt(cpu_R[dc->rd], cpu_env, +                                       cpu_R[dc->ra], cpu_R[dc->rb]); +                    break; +                case 5: +                    gen_helper_fcmp_ne(cpu_R[dc->rd], cpu_env, +                                       cpu_R[dc->ra], cpu_R[dc->rb]); +                    break; +                case 6: +                    gen_helper_fcmp_ge(cpu_R[dc->rd], cpu_env, +                                       cpu_R[dc->ra], cpu_R[dc->rb]); +                    break; +                default: +                    qemu_log_mask(LOG_UNIMP, +                                  "unimplemented fcmp fpu_insn=%x pc=%x" +                                  " opc=%x\n", +                                  fpu_insn, dc->pc, dc->opcode); +                    dc->abort_at_next_insn = 1; +                    break; +            } +            break; + +        case 5: +            if (!dec_check_fpuv2(dc)) { +                return; +            } +            gen_helper_flt(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra]); +            break; + +        case 6: +            if (!dec_check_fpuv2(dc)) { +                return; +            } +            gen_helper_fint(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra]); +            break; + +        case 7: +            if (!dec_check_fpuv2(dc)) { +                return; +            } +            gen_helper_fsqrt(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra]); +            break; + +        default: +            qemu_log_mask(LOG_UNIMP, "unimplemented FPU insn fpu_insn=%x pc=%x" +                          " opc=%x\n", +                          fpu_insn, dc->pc, dc->opcode); +            dc->abort_at_next_insn = 1; +            break; +    } +} + +static void dec_null(DisasContext *dc) +{ +    if ((dc->tb_flags & MSR_EE_FLAG) +          && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK)) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +        return; +    } +    qemu_log ("unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode); +    dc->abort_at_next_insn = 1; +} + +/* Insns connected to FSL or AXI stream attached devices.  */ +static void dec_stream(DisasContext *dc) +{ +    int mem_index = cpu_mmu_index(&dc->cpu->env); +    TCGv_i32 t_id, t_ctrl; +    int ctrl; + +    LOG_DIS("%s%s imm=%x\n", dc->rd ? "get" : "put", +            dc->type_b ? "" : "d", dc->imm); + +    if ((dc->tb_flags & MSR_EE_FLAG) && (mem_index == MMU_USER_IDX)) { +        tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); +        t_gen_raise_exception(dc, EXCP_HW_EXCP); +        return; +    } + +    t_id = tcg_temp_new(); +    if (dc->type_b) { +        tcg_gen_movi_tl(t_id, dc->imm & 0xf); +        ctrl = dc->imm >> 10; +    } else { +        tcg_gen_andi_tl(t_id, cpu_R[dc->rb], 0xf); +        ctrl = dc->imm >> 5; +    } + +    t_ctrl = tcg_const_tl(ctrl); + +    if (dc->rd == 0) { +        gen_helper_put(t_id, t_ctrl, cpu_R[dc->ra]); +    } else { +        gen_helper_get(cpu_R[dc->rd], t_id, t_ctrl); +    } +    tcg_temp_free(t_id); +    tcg_temp_free(t_ctrl); +} + +static struct decoder_info { +    struct { +        uint32_t bits; +        uint32_t mask; +    }; +    void (*dec)(DisasContext *dc); +} decinfo[] = { +    {DEC_ADD, dec_add}, +    {DEC_SUB, dec_sub}, +    {DEC_AND, dec_and}, +    {DEC_XOR, dec_xor}, +    {DEC_OR, dec_or}, +    {DEC_BIT, dec_bit}, +    {DEC_BARREL, dec_barrel}, +    {DEC_LD, dec_load}, +    {DEC_ST, dec_store}, +    {DEC_IMM, dec_imm}, +    {DEC_BR, dec_br}, +    {DEC_BCC, dec_bcc}, +    {DEC_RTS, dec_rts}, +    {DEC_FPU, dec_fpu}, +    {DEC_MUL, dec_mul}, +    {DEC_DIV, dec_div}, +    {DEC_MSR, dec_msr}, +    {DEC_STREAM, dec_stream}, +    {{0, 0}, dec_null} +}; + +static inline void decode(DisasContext *dc, uint32_t ir) +{ +    int i; + +    if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP | CPU_LOG_TB_OP_OPT))) { +        tcg_gen_debug_insn_start(dc->pc); +    } + +    dc->ir = ir; +    LOG_DIS("%8.8x\t", dc->ir); + +    if (dc->ir) +        dc->nr_nops = 0; +    else { +        if ((dc->tb_flags & MSR_EE_FLAG) +              && (dc->cpu->env.pvr.regs[2] & PVR2_ILL_OPCODE_EXC_MASK) +              && (dc->cpu->env.pvr.regs[2] & PVR2_OPCODE_0x0_ILL_MASK)) { +            tcg_gen_movi_tl(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); +            t_gen_raise_exception(dc, EXCP_HW_EXCP); +            return; +        } + +        LOG_DIS("nr_nops=%d\t", dc->nr_nops); +        dc->nr_nops++; +        if (dc->nr_nops > 4) { +            cpu_abort(CPU(dc->cpu), "fetching nop sequence\n"); +        } +    } +    /* bit 2 seems to indicate insn type.  */ +    dc->type_b = ir & (1 << 29); + +    dc->opcode = EXTRACT_FIELD(ir, 26, 31); +    dc->rd = EXTRACT_FIELD(ir, 21, 25); +    dc->ra = EXTRACT_FIELD(ir, 16, 20); +    dc->rb = EXTRACT_FIELD(ir, 11, 15); +    dc->imm = EXTRACT_FIELD(ir, 0, 15); + +    /* Large switch for all insns.  */ +    for (i = 0; i < ARRAY_SIZE(decinfo); i++) { +        if ((dc->opcode & decinfo[i].mask) == decinfo[i].bits) { +            decinfo[i].dec(dc); +            break; +        } +    } +} + +static void check_breakpoint(CPUMBState *env, DisasContext *dc) +{ +    CPUState *cs = CPU(mb_env_get_cpu(env)); +    CPUBreakpoint *bp; + +    if (unlikely(!QTAILQ_EMPTY(&cs->breakpoints))) { +        QTAILQ_FOREACH(bp, &cs->breakpoints, entry) { +            if (bp->pc == dc->pc) { +                t_gen_raise_exception(dc, EXCP_DEBUG); +                dc->is_jmp = DISAS_UPDATE; +             } +        } +    } +} + +/* generate intermediate code for basic block 'tb'.  */ +static inline void +gen_intermediate_code_internal(MicroBlazeCPU *cpu, TranslationBlock *tb, +                               bool search_pc) +{ +    CPUState *cs = CPU(cpu); +    CPUMBState *env = &cpu->env; +    uint32_t pc_start; +    int j, lj; +    struct DisasContext ctx; +    struct DisasContext *dc = &ctx; +    uint32_t next_page_start, org_flags; +    target_ulong npc; +    int num_insns; +    int max_insns; + +    pc_start = tb->pc; +    dc->cpu = cpu; +    dc->tb = tb; +    org_flags = dc->synced_flags = dc->tb_flags = tb->flags; + +    dc->is_jmp = DISAS_NEXT; +    dc->jmp = 0; +    dc->delayed_branch = !!(dc->tb_flags & D_FLAG); +    if (dc->delayed_branch) { +        dc->jmp = JMP_INDIRECT; +    } +    dc->pc = pc_start; +    dc->singlestep_enabled = cs->singlestep_enabled; +    dc->cpustate_changed = 0; +    dc->abort_at_next_insn = 0; +    dc->nr_nops = 0; + +    if (pc_start & 3) { +        cpu_abort(cs, "Microblaze: unaligned PC=%x\n", pc_start); +    } + +    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { +#if !SIM_COMPAT +        qemu_log("--------------\n"); +        log_cpu_state(CPU(cpu), 0); +#endif +    } + +    next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE; +    lj = -1; +    num_insns = 0; +    max_insns = tb->cflags & CF_COUNT_MASK; +    if (max_insns == 0) +        max_insns = CF_COUNT_MASK; + +    gen_tb_start(tb); +    do +    { +#if SIM_COMPAT +        if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { +            tcg_gen_movi_tl(cpu_SR[SR_PC], dc->pc); +            gen_helper_debug(); +        } +#endif +        check_breakpoint(env, dc); + +        if (search_pc) { +            j = tcg_op_buf_count(); +            if (lj < j) { +                lj++; +                while (lj < j) +                    tcg_ctx.gen_opc_instr_start[lj++] = 0; +            } +            tcg_ctx.gen_opc_pc[lj] = dc->pc; +            tcg_ctx.gen_opc_instr_start[lj] = 1; +                        tcg_ctx.gen_opc_icount[lj] = num_insns; +        } + +        /* Pretty disas.  */ +        LOG_DIS("%8.8x:\t", dc->pc); + +        if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) +            gen_io_start(); + +        dc->clear_imm = 1; +        decode(dc, cpu_ldl_code(env, dc->pc)); +        if (dc->clear_imm) +            dc->tb_flags &= ~IMM_FLAG; +        dc->pc += 4; +        num_insns++; + +        if (dc->delayed_branch) { +            dc->delayed_branch--; +            if (!dc->delayed_branch) { +                if (dc->tb_flags & DRTI_FLAG) +                    do_rti(dc); +                 if (dc->tb_flags & DRTB_FLAG) +                    do_rtb(dc); +                if (dc->tb_flags & DRTE_FLAG) +                    do_rte(dc); +                /* Clear the delay slot flag.  */ +                dc->tb_flags &= ~D_FLAG; +                /* If it is a direct jump, try direct chaining.  */ +                if (dc->jmp == JMP_INDIRECT) { +                    eval_cond_jmp(dc, env_btarget, tcg_const_tl(dc->pc)); +                    dc->is_jmp = DISAS_JUMP; +                } else if (dc->jmp == JMP_DIRECT) { +                    t_sync_flags(dc); +                    gen_goto_tb(dc, 0, dc->jmp_pc); +                    dc->is_jmp = DISAS_TB_JUMP; +                } else if (dc->jmp == JMP_DIRECT_CC) { +                    TCGLabel *l1 = gen_new_label(); +                    t_sync_flags(dc); +                    /* Conditional jmp.  */ +                    tcg_gen_brcondi_tl(TCG_COND_NE, env_btaken, 0, l1); +                    gen_goto_tb(dc, 1, dc->pc); +                    gen_set_label(l1); +                    gen_goto_tb(dc, 0, dc->jmp_pc); + +                    dc->is_jmp = DISAS_TB_JUMP; +                } +                break; +            } +        } +        if (cs->singlestep_enabled) { +            break; +        } +    } while (!dc->is_jmp && !dc->cpustate_changed +             && !tcg_op_buf_full() +             && !singlestep +             && (dc->pc < next_page_start) +             && num_insns < max_insns); + +    npc = dc->pc; +    if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { +        if (dc->tb_flags & D_FLAG) { +            dc->is_jmp = DISAS_UPDATE; +            tcg_gen_movi_tl(cpu_SR[SR_PC], npc); +            sync_jmpstate(dc); +        } else +            npc = dc->jmp_pc; +    } + +    if (tb->cflags & CF_LAST_IO) +        gen_io_end(); +    /* Force an update if the per-tb cpu state has changed.  */ +    if (dc->is_jmp == DISAS_NEXT +        && (dc->cpustate_changed || org_flags != dc->tb_flags)) { +        dc->is_jmp = DISAS_UPDATE; +        tcg_gen_movi_tl(cpu_SR[SR_PC], npc); +    } +    t_sync_flags(dc); + +    if (unlikely(cs->singlestep_enabled)) { +        TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); + +        if (dc->is_jmp != DISAS_JUMP) { +            tcg_gen_movi_tl(cpu_SR[SR_PC], npc); +        } +        gen_helper_raise_exception(cpu_env, tmp); +        tcg_temp_free_i32(tmp); +    } else { +        switch(dc->is_jmp) { +            case DISAS_NEXT: +                gen_goto_tb(dc, 1, npc); +                break; +            default: +            case DISAS_JUMP: +            case DISAS_UPDATE: +                /* indicate that the hash table must be used +                   to find the next TB */ +                tcg_gen_exit_tb(0); +                break; +            case DISAS_TB_JUMP: +                /* nothing more to generate */ +                break; +        } +    } +    gen_tb_end(tb, num_insns); + +    if (search_pc) { +        j = tcg_op_buf_count(); +        lj++; +        while (lj <= j) +            tcg_ctx.gen_opc_instr_start[lj++] = 0; +    } else { +        tb->size = dc->pc - pc_start; +                tb->icount = num_insns; +    } + +#ifdef DEBUG_DISAS +#if !SIM_COMPAT +    if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { +        qemu_log("\n"); +#if DISAS_GNU +        log_target_disas(cs, pc_start, dc->pc - pc_start, 0); +#endif +        qemu_log("\nisize=%d osize=%d\n", +                 dc->pc - pc_start, tcg_op_buf_count()); +    } +#endif +#endif +    assert(!dc->abort_at_next_insn); +} + +void gen_intermediate_code (CPUMBState *env, struct TranslationBlock *tb) +{ +    gen_intermediate_code_internal(mb_env_get_cpu(env), tb, false); +} + +void gen_intermediate_code_pc (CPUMBState *env, struct TranslationBlock *tb) +{ +    gen_intermediate_code_internal(mb_env_get_cpu(env), tb, true); +} + +void mb_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, +                       int flags) +{ +    MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); +    CPUMBState *env = &cpu->env; +    int i; + +    if (!env || !f) +        return; + +    cpu_fprintf(f, "IN: PC=%x %s\n", +                env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC])); +    cpu_fprintf(f, "rmsr=%x resr=%x rear=%x debug=%x imm=%x iflags=%x fsr=%x\n", +             env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], +             env->debug, env->imm, env->iflags, env->sregs[SR_FSR]); +    cpu_fprintf(f, "btaken=%d btarget=%x mode=%s(saved=%s) eip=%d ie=%d\n", +             env->btaken, env->btarget, +             (env->sregs[SR_MSR] & MSR_UM) ? "user" : "kernel", +             (env->sregs[SR_MSR] & MSR_UMS) ? "user" : "kernel", +             (env->sregs[SR_MSR] & MSR_EIP), +             (env->sregs[SR_MSR] & MSR_IE)); + +    for (i = 0; i < 32; i++) { +        cpu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); +        if ((i + 1) % 4 == 0) +            cpu_fprintf(f, "\n"); +        } +    cpu_fprintf(f, "\n\n"); +} + +MicroBlazeCPU *cpu_mb_init(const char *cpu_model) +{ +    MicroBlazeCPU *cpu; + +    cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU)); + +    object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + +    return cpu; +} + +void mb_tcg_init(void) +{ +    int i; + +    cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + +    env_debug = tcg_global_mem_new(TCG_AREG0,  +                    offsetof(CPUMBState, debug), +                    "debug0"); +    env_iflags = tcg_global_mem_new(TCG_AREG0,  +                    offsetof(CPUMBState, iflags), +                    "iflags"); +    env_imm = tcg_global_mem_new(TCG_AREG0,  +                    offsetof(CPUMBState, imm), +                    "imm"); +    env_btarget = tcg_global_mem_new(TCG_AREG0, +                     offsetof(CPUMBState, btarget), +                     "btarget"); +    env_btaken = tcg_global_mem_new(TCG_AREG0, +                     offsetof(CPUMBState, btaken), +                     "btaken"); +    env_res_addr = tcg_global_mem_new(TCG_AREG0, +                     offsetof(CPUMBState, res_addr), +                     "res_addr"); +    env_res_val = tcg_global_mem_new(TCG_AREG0, +                     offsetof(CPUMBState, res_val), +                     "res_val"); +    for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { +        cpu_R[i] = tcg_global_mem_new(TCG_AREG0, +                          offsetof(CPUMBState, regs[i]), +                          regnames[i]); +    } +    for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) { +        cpu_SR[i] = tcg_global_mem_new(TCG_AREG0, +                          offsetof(CPUMBState, sregs[i]), +                          special_regnames[i]); +    } +} + +void restore_state_to_opc(CPUMBState *env, TranslationBlock *tb, int pc_pos) +{ +    env->sregs[SR_PC] = tcg_ctx.gen_opc_pc[pc_pos]; +}  | 
