diff options
author | Timothy Pearson <tpearson@raptorengineering.com> | 2019-05-11 15:12:49 -0500 |
---|---|---|
committer | Timothy Pearson <tpearson@raptorengineering.com> | 2019-05-11 15:12:49 -0500 |
commit | 9e80202352dd49bdd9e67b8b906d86f058431505 (patch) | |
tree | 5673c17aad6e3833da8c4ff21b5a11f666ec9fbe /src/target-microblaze | |
download | hqemu-master.zip hqemu-master.tar.gz |
Diffstat (limited to 'src/target-microblaze')
-rw-r--r-- | src/target-microblaze/Makefile.objs | 3 | ||||
-rw-r--r-- | src/target-microblaze/cpu-qom.h | 94 | ||||
-rw-r--r-- | src/target-microblaze/cpu.c | 289 | ||||
-rw-r--r-- | src/target-microblaze/cpu.h | 342 | ||||
-rw-r--r-- | src/target-microblaze/gdbstub.c | 56 | ||||
-rw-r--r-- | src/target-microblaze/helper.c | 304 | ||||
-rw-r--r-- | src/target-microblaze/helper.h | 37 | ||||
-rw-r--r-- | src/target-microblaze/microblaze-decode.h | 55 | ||||
-rw-r--r-- | src/target-microblaze/mmu.c | 301 | ||||
-rw-r--r-- | src/target-microblaze/mmu.h | 90 | ||||
-rw-r--r-- | src/target-microblaze/op_helper.c | 519 | ||||
-rw-r--r-- | src/target-microblaze/translate.c | 1908 |
12 files changed, 3998 insertions, 0 deletions
diff --git a/src/target-microblaze/Makefile.objs b/src/target-microblaze/Makefile.objs new file mode 100644 index 0000000..f3d7b44 --- /dev/null +++ b/src/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/src/target-microblaze/cpu-qom.h b/src/target-microblaze/cpu-qom.h new file mode 100644 index 0000000..34f6273 --- /dev/null +++ b/src/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/src/target-microblaze/cpu.c b/src/target-microblaze/cpu.c new file mode 100644 index 0000000..52959e1 --- /dev/null +++ b/src/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; + + env->sregs[SR_PC] = cpu->cfg.base_vectors; + +#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); + + 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/src/target-microblaze/cpu.h b/src/target-microblaze/cpu.h new file mode 100644 index 0000000..6b212ab --- /dev/null +++ b/src/target-microblaze/cpu.h @@ -0,0 +1,342 @@ +/* + * 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 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_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, bool ifetch) +{ + /* 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/src/target-microblaze/gdbstub.c b/src/target-microblaze/gdbstub.c new file mode 100644 index 0000000..a70e2ee --- /dev/null +++ b/src/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/src/target-microblaze/helper.c b/src/target-microblaze/helper.c new file mode 100644 index 0000000..8257b0e --- /dev/null +++ b/src/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/src/target-microblaze/helper.h b/src/target-microblaze/helper.h new file mode 100644 index 0000000..bd13826 --- /dev/null +++ b/src/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/src/target-microblaze/microblaze-decode.h b/src/target-microblaze/microblaze-decode.h new file mode 100644 index 0000000..401319e --- /dev/null +++ b/src/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/src/target-microblaze/mmu.c b/src/target-microblaze/mmu.c new file mode 100644 index 0000000..2ef1dc2 --- /dev/null +++ b/src/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, false)); + 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/src/target-microblaze/mmu.h b/src/target-microblaze/mmu.h new file mode 100644 index 0000000..3b7a998 --- /dev/null +++ b/src/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/src/target-microblaze/op_helper.c b/src/target-microblaze/op_helper.c new file mode 100644 index 0000000..d324347 --- /dev/null +++ b/src/target-microblaze/op_helper.c @@ -0,0 +1,519 @@ +/* + * 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 "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) +{ + return compute_carry(a, b, cf); +} + +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/src/target-microblaze/translate.c b/src/target-microblaze/translate.c new file mode 100644 index 0000000..154b9d6 --- /dev/null +++ b/src/target-microblaze/translate.c @@ -0,0 +1,1908 @@ +/* + * 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; + + 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) { + tcg_gen_setcond_tl(TCG_COND_EQ, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + } + break; + case 3: + LOG_DIS("pcmpne r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); + if (dc->rd) { + tcg_gen_setcond_tl(TCG_COND_NE, cpu_R[dc->rd], + cpu_R[dc->ra], cpu_R[dc->rb]); + } + 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, false); + + 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_extrl_i64_i32(d, t0); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_extrl_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_extrl_i64_i32(d, t0); + tcg_gen_shri_i64(t0, t0, 32); + tcg_gen_extrl_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, false); + + 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, false), 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 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, false), + 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, false), 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, false); + + 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, false); + + 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, false); + 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; + + 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; + } + } +} + +/* generate intermediate code for basic block 'tb'. */ +void gen_intermediate_code(CPUMBState *env, struct TranslationBlock *tb) +{ + MicroBlazeCPU *cpu = mb_env_get_cpu(env); + CPUState *cs = CPU(cpu); + uint32_t pc_start; + 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; + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + + gen_tb_start(tb); + do + { + tcg_gen_insn_start(dc->pc); + num_insns++; + +#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 + + if (unlikely(cpu_breakpoint_test(cs, dc->pc, BP_ANY))) { + t_gen_raise_exception(dc, EXCP_DEBUG); + dc->is_jmp = DISAS_UPDATE; + /* The address covered by the breakpoint must be included in + [tb->pc, tb->pc + tb->size) in order to for it to be + properly cleared -- thus we increment the PC here so that + the logic setting tb->size below does the right thing. */ + dc->pc += 4; + break; + } + + /* Pretty disas. */ + LOG_DIS("%8.8x:\t", dc->pc); + + if (num_insns == 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; + + 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); + + 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 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, + target_ulong *data) +{ + env->sregs[SR_PC] = data[0]; +} |