diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-09-02 13:56:55 +0100 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-09-02 13:56:56 +0100 |
commit | 7068d5ef399b4682d9ad77164d700fcca3c77485 (patch) | |
tree | e9a8e967e6cf64620ba5cceee27414b0bfa19181 /target | |
parent | 887adde81d1f1f3897f1688d37ec6851b4fdad86 (diff) | |
parent | 19f27b6c2493472fe2790cf08d7b0140d57bdad5 (diff) |
Merge remote-tracking branch 'remotes/rth/tags/pull-mb-20200901' into staging
Convert microblaze to generic translator loop
Convert microblaze to decodetree
Fix mb_cpu_transaction_failed
Other misc cleanups
# gpg: Signature made Tue 01 Sep 2020 16:17:19 BST
# gpg: using RSA key 7A481E78868B4DB6A85A05C064DF38E8AF7E215F
# gpg: issuer "richard.henderson@linaro.org"
# gpg: Good signature from "Richard Henderson <richard.henderson@linaro.org>" [full]
# Primary key fingerprint: 7A48 1E78 868B 4DB6 A85A 05C0 64DF 38E8 AF7E 215F
* remotes/rth/tags/pull-mb-20200901: (76 commits)
target/microblaze: Reduce linux-user address space to 32-bit
target/microblaze: Add flags markup to some helpers
target/microblaze: Remove cpu_R[0]
target/microblaze: Remove last of old decoder
target/microblaze: Convert dec_stream to decodetree
target/microblaze: Convert dec_msr to decodetree
target/microblaze: Convert msrclr, msrset to decodetree
target/microblaze: Tidy do_rti, do_rtb, do_rte
target/microblaze: Convert dec_rts to decodetree
target/microblaze: Convert dec_bcc to decodetree
target/microblaze: Convert dec_br to decodetree
target/microblaze: Reorganize branching
target/microblaze: Convert mbar to decodetree
target/microblaze: Convert brk and brki to decodetree
target/microblaze: Tidy mb_cpu_dump_state
target/microblaze: Replace delayed_branch with tb_flags_to_set
target/microblaze: Replace clear_imm with tb_flags_to_set
target/microblaze: Use cc->do_unaligned_access
tcg: Add tcg_get_insn_start_param
target/microblaze: Store "current" iflags in insn_start
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'target')
-rw-r--r-- | target/microblaze/cpu-param.h | 15 | ||||
-rw-r--r-- | target/microblaze/cpu.c | 9 | ||||
-rw-r--r-- | target/microblaze/cpu.h | 67 | ||||
-rw-r--r-- | target/microblaze/gdbstub.c | 189 | ||||
-rw-r--r-- | target/microblaze/helper.c | 164 | ||||
-rw-r--r-- | target/microblaze/helper.h | 49 | ||||
-rw-r--r-- | target/microblaze/insns.decode | 256 | ||||
-rw-r--r-- | target/microblaze/meson.build | 3 | ||||
-rw-r--r-- | target/microblaze/microblaze-decode.h | 59 | ||||
-rw-r--r-- | target/microblaze/mmu.c | 4 | ||||
-rw-r--r-- | target/microblaze/op_helper.c | 194 | ||||
-rw-r--r-- | target/microblaze/translate.c | 2901 |
12 files changed, 2069 insertions, 1841 deletions
diff --git a/target/microblaze/cpu-param.h b/target/microblaze/cpu-param.h index 4abbc62d50..4d8297fa94 100644 --- a/target/microblaze/cpu-param.h +++ b/target/microblaze/cpu-param.h @@ -8,9 +8,24 @@ #ifndef MICROBLAZE_CPU_PARAM_H #define MICROBLAZE_CPU_PARAM_H 1 +/* + * While system mode can address up to 64 bits of address space, + * this is done via the lea/sea instructions, which are system-only + * (as they also bypass the mmu). + * + * We can improve the user-only experience by only exposing 32 bits + * of address space. + */ +#ifdef CONFIG_USER_ONLY +#define TARGET_LONG_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#else #define TARGET_LONG_BITS 64 #define TARGET_PHYS_ADDR_SPACE_BITS 64 #define TARGET_VIRT_ADDR_SPACE_BITS 64 +#endif + /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 #define NB_MMU_MODES 3 diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 51e5c85b10..67017ecc33 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -79,7 +79,7 @@ static void mb_cpu_set_pc(CPUState *cs, vaddr value) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - cpu->env.sregs[SR_PC] = value; + cpu->env.pc = value; } static bool mb_cpu_has_work(CPUState *cs) @@ -117,13 +117,13 @@ static void mb_cpu_reset(DeviceState *dev) /* Disable stack protector. */ env->shr = ~0; - env->sregs[SR_PC] = cpu->cfg.base_vectors; + env->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; + mb_cpu_write_msr(env, MSR_EE | MSR_IE | MSR_VM | MSR_UM); #else - env->sregs[SR_MSR] = 0; + mb_cpu_write_msr(env, 0); mmu_init(&env->mmu); env->mmu.c_mmu = 3; env->mmu.c_mmu_tlb_access = 3; @@ -317,6 +317,7 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) cc->class_by_name = mb_cpu_class_by_name; cc->has_work = mb_cpu_has_work; cc->do_interrupt = mb_cpu_do_interrupt; + cc->do_unaligned_access = mb_cpu_do_unaligned_access; cc->cpu_exec_interrupt = mb_cpu_exec_interrupt; cc->dump_state = mb_cpu_dump_state; cc->set_pc = mb_cpu_set_pc; diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index a31134b65c..d11b6fa995 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -31,7 +31,7 @@ typedef struct CPUMBState CPUMBState; #define EXCP_MMU 1 #define EXCP_IRQ 2 -#define EXCP_BREAK 3 +#define EXCP_SYSCALL 3 /* user-only */ #define EXCP_HW_BREAK 4 #define EXCP_HW_EXCP 5 @@ -79,10 +79,13 @@ typedef struct CPUMBState CPUMBState; /* Exception State Register (ESR) Fields */ #define ESR_DIZ (1<<11) /* Zone Protection */ +#define ESR_W (1<<11) /* Unaligned word access */ #define ESR_S (1<<10) /* Store instruction */ #define ESR_ESS_FSL_OFFSET 5 +#define ESR_ESS_MASK (0x7f << 5) + #define ESR_EC_FSL 0 #define ESR_EC_UNALIGNED_DATA 1 #define ESR_EC_ILLEGAL_OP 2 @@ -228,15 +231,22 @@ typedef struct CPUMBState CPUMBState; #define STREAM_CONTROL (1 << 3) #define STREAM_NONBLOCK (1 << 4) +#define TARGET_INSN_START_EXTRA_WORDS 1 + struct CPUMBState { - uint32_t debug; - uint32_t btaken; - uint64_t btarget; - uint32_t bimm; + uint32_t bvalue; /* TCG temporary, only valid during a TB */ + uint32_t btarget; /* Full resolved branch destination */ uint32_t imm; uint32_t regs[32]; - uint64_t sregs[14]; + uint32_t pc; + uint32_t msr; /* All bits of MSR except MSR[C] and MSR[CC] */ + uint32_t msr_c; /* MSR[C], in low bit; other bits must be 0 */ + target_ulong ear; + uint32_t esr; + uint32_t fsr; + uint32_t btr; + uint32_t edr; float_status fp_status; /* Stack protectors. Yes, it's a hw feature. */ uint32_t slr, shr; @@ -247,14 +257,22 @@ struct CPUMBState { uint32_t res_val; /* Internal flags. */ -#define IMM_FLAG 4 -#define MSR_EE_FLAG (1 << 8) +#define IMM_FLAG (1 << 0) +#define BIMM_FLAG (1 << 1) +#define ESR_ESS_FLAG (1 << 2) /* indicates ESR_ESS_MASK is present */ +/* MSR_EE (1 << 8) -- these 3 are not in iflags but tb_flags */ +/* MSR_UM (1 << 11) */ +/* MSR_VM (1 << 13) */ +/* ESR_ESS_MASK [11:5] -- unwind into iflags for unaligned excp */ #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) +#define MSR_TB_MASK (MSR_UM | MSR_VM | MSR_EE) + uint32_t iflags; #if !defined(CONFIG_USER_ONLY) @@ -317,11 +335,30 @@ struct MicroBlazeCPU { void mb_cpu_do_interrupt(CPUState *cs); bool mb_cpu_exec_interrupt(CPUState *cs, int int_req); +void mb_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr); void mb_cpu_dump_state(CPUState *cpu, FILE *f, int flags); hwaddr mb_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); int mb_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int mb_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +static inline uint32_t mb_cpu_read_msr(const CPUMBState *env) +{ + /* Replicate MSR[C] to MSR[CC]. */ + return env->msr | (env->msr_c * (MSR_C | MSR_CC)); +} + +static inline void mb_cpu_write_msr(CPUMBState *env, uint32_t val) +{ + env->msr_c = (val >> 2) & 1; + /* + * Clear both MSR[C] and MSR[CC] from the saved copy. + * MSR_PVR is not writable and is always clear. + */ + env->msr = val & ~(MSR_C | MSR_CC | MSR_PVR); +} + void mb_tcg_init(void); /* you can call this signal handler from your SIGBUS and SIGSEGV signal handlers to inform the virtual CPU of exceptions. non zero @@ -348,13 +385,15 @@ typedef MicroBlazeCPU ArchCPU; #include "exec/cpu-all.h" +/* Ensure there is no overlap between the two masks. */ +QEMU_BUILD_BUG_ON(MSR_TB_MASK & IFLAGS_TB_MASK); + static inline void cpu_get_tb_cpu_state(CPUMBState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *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)); + *pc = env->pc; + *flags = (env->iflags & IFLAGS_TB_MASK) | (env->msr & MSR_TB_MASK); + *cs_base = (*flags & IMM_FLAG ? env->imm : 0); } #if !defined(CONFIG_USER_ONLY) @@ -369,11 +408,11 @@ static inline int cpu_mmu_index(CPUMBState *env, bool ifetch) MicroBlazeCPU *cpu = env_archcpu(env); /* Are we in nommu mode?. */ - if (!(env->sregs[SR_MSR] & MSR_VM) || !cpu->cfg.use_mmu) { + if (!(env->msr & MSR_VM) || !cpu->cfg.use_mmu) { return MMU_NOMMU_IDX; } - if (env->sregs[SR_MSR] & MSR_UM) { + if (env->msr & MSR_UM) { return MMU_USER_IDX; } return MMU_KERNEL_IDX; diff --git a/target/microblaze/gdbstub.c b/target/microblaze/gdbstub.c index 73e8973597..08d6a0e807 100644 --- a/target/microblaze/gdbstub.c +++ b/target/microblaze/gdbstub.c @@ -21,58 +21,80 @@ #include "cpu.h" #include "exec/gdbstub.h" +/* + * GDB expects SREGs in the following order: + * PC, MSR, EAR, ESR, FSR, BTR, EDR, PID, ZPR, TLBX, TLBSX, TLBLO, TLBHI. + * + * PID, ZPR, TLBx, TLBsx, TLBLO, and TLBHI aren't modeled, so we don't + * map them to anything and return a value of 0 instead. + */ + +enum { + GDB_PC = 32 + 0, + GDB_MSR = 32 + 1, + GDB_EAR = 32 + 2, + GDB_ESR = 32 + 3, + GDB_FSR = 32 + 4, + GDB_BTR = 32 + 5, + GDB_PVR0 = 32 + 6, + GDB_PVR11 = 32 + 17, + GDB_EDR = 32 + 18, + GDB_SLR = 32 + 25, + GDB_SHR = 32 + 26, +}; + int mb_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + CPUClass *cc = CPU_GET_CLASS(cs); CPUMBState *env = &cpu->env; - /* - * GDB expects SREGs in the following order: - * PC, MSR, EAR, ESR, FSR, BTR, EDR, PID, ZPR, TLBX, TLBSX, TLBLO, TLBHI. - * They aren't stored in this order, so make a map. - * PID, ZPR, TLBx, TLBsx, TLBLO, and TLBHI aren't modeled, so we don't - * map them to anything and return a value of 0 instead. - */ - static const uint8_t sreg_map[6] = { - SR_PC, - SR_MSR, - SR_EAR, - SR_ESR, - SR_FSR, - SR_BTR - }; + uint32_t val; - /* - * GDB expects registers to be reported in this order: - * R0-R31 - * PC-BTR - * PVR0-PVR11 - * EDR-TLBHI - * SLR-SHR - */ - if (n < 32) { - return gdb_get_reg32(mem_buf, env->regs[n]); - } else { - n -= 32; - switch (n) { - case 0 ... 5: - return gdb_get_reg32(mem_buf, env->sregs[sreg_map[n]]); + if (n > cc->gdb_num_core_regs) { + return 0; + } + + switch (n) { + case 1 ... 31: + val = env->regs[n]; + break; + case GDB_PC: + val = env->pc; + break; + case GDB_MSR: + val = mb_cpu_read_msr(env); + break; + case GDB_EAR: + val = env->ear; + break; + case GDB_ESR: + val = env->esr; + break; + case GDB_FSR: + val = env->fsr; + break; + case GDB_BTR: + val = env->btr; + break; + case GDB_PVR0 ... GDB_PVR11: /* PVR12 is intentionally skipped */ - case 6 ... 17: - n -= 6; - return gdb_get_reg32(mem_buf, env->pvr.regs[n]); - case 18: - return gdb_get_reg32(mem_buf, env->sregs[SR_EDR]); + val = env->pvr.regs[n - GDB_PVR0]; + break; + case GDB_EDR: + val = env->edr; + break; + case GDB_SLR: + val = env->slr; + break; + case GDB_SHR: + val = env->shr; + break; + default: /* Other SRegs aren't modeled, so report a value of 0 */ - case 19 ... 24: - return gdb_get_reg32(mem_buf, 0); - case 25: - return gdb_get_reg32(mem_buf, env->slr); - case 26: - return gdb_get_reg32(mem_buf, env->shr); - default: - return 0; - } + val = 0; + break; } + return gdb_get_reg32(mem_buf, val); } int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) @@ -82,60 +104,47 @@ int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) CPUMBState *env = &cpu->env; uint32_t tmp; - /* - * GDB expects SREGs in the following order: - * PC, MSR, EAR, ESR, FSR, BTR, EDR, PID, ZPR, TLBX, TLBSX, TLBLO, TLBHI. - * They aren't stored in this order, so make a map. - * PID, ZPR, TLBx, TLBsx, TLBLO, and TLBHI aren't modeled, so we don't - * map them to anything. - */ - static const uint8_t sreg_map[6] = { - SR_PC, - SR_MSR, - SR_EAR, - SR_ESR, - SR_FSR, - SR_BTR - }; - if (n > cc->gdb_num_core_regs) { return 0; } tmp = ldl_p(mem_buf); - /* - * GDB expects registers to be reported in this order: - * R0-R31 - * PC-BTR - * PVR0-PVR11 - * EDR-TLBHI - * SLR-SHR - */ - if (n < 32) { + switch (n) { + case 1 ... 31: env->regs[n] = tmp; - } else { - n -= 32; - switch (n) { - case 0 ... 5: - env->sregs[sreg_map[n]] = tmp; - break; + break; + case GDB_PC: + env->pc = tmp; + break; + case GDB_MSR: + mb_cpu_write_msr(env, tmp); + break; + case GDB_EAR: + env->ear = tmp; + break; + case GDB_ESR: + env->esr = tmp; + break; + case GDB_FSR: + env->fsr = tmp; + break; + case GDB_BTR: + env->btr = tmp; + break; + case GDB_PVR0 ... GDB_PVR11: /* PVR12 is intentionally skipped */ - case 6 ... 17: - n -= 6; - env->pvr.regs[n] = tmp; - break; - /* Only EDR is modeled in these indeces, so ignore the rest */ - case 18: - env->sregs[SR_EDR] = tmp; - break; - case 25: - env->slr = tmp; - break; - case 26: - env->shr = tmp; - break; - } + env->pvr.regs[n - GDB_PVR0] = tmp; + break; + case GDB_EDR: + env->edr = tmp; + break; + case GDB_SLR: + env->slr = tmp; + break; + case GDB_SHR: + env->shr = tmp; + break; } return 4; } diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index ab2ceeb055..48547385b0 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -24,8 +24,6 @@ #include "qemu/host-utils.h" #include "exec/log.h" -#define D(x) - #if defined(CONFIG_USER_ONLY) void mb_cpu_do_interrupt(CPUState *cs) @@ -35,7 +33,7 @@ void mb_cpu_do_interrupt(CPUState *cs) cs->exception_index = -1; env->res_addr = RES_ADDR_NONE; - env->regs[14] = env->sregs[SR_PC]; + env->regs[14] = env->pc; } bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, @@ -85,15 +83,15 @@ bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, qemu_log_mask(CPU_LOG_MMU, "mmu=%d miss v=%" VADDR_PRIx "\n", mmu_idx, address); - env->sregs[SR_EAR] = address; + env->ear = address; switch (lu.err) { case ERR_PROT: - env->sregs[SR_ESR] = access_type == MMU_INST_FETCH ? 17 : 16; - env->sregs[SR_ESR] |= (access_type == MMU_DATA_STORE) << 10; + env->esr = access_type == MMU_INST_FETCH ? 17 : 16; + env->esr |= (access_type == MMU_DATA_STORE) << 10; break; case ERR_MISS: - env->sregs[SR_ESR] = access_type == MMU_INST_FETCH ? 19 : 18; - env->sregs[SR_ESR] |= (access_type == MMU_DATA_STORE) << 10; + env->esr = access_type == MMU_INST_FETCH ? 19 : 18; + env->esr |= (access_type == MMU_DATA_STORE) << 10; break; default: abort(); @@ -112,12 +110,11 @@ void mb_cpu_do_interrupt(CPUState *cs) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; - uint32_t t; + uint32_t t, msr = mb_cpu_read_msr(env); /* 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: @@ -126,80 +123,79 @@ void mb_cpu_do_interrupt(CPUState *cs) return; } - env->regs[17] = env->sregs[SR_PC] + 4; - env->sregs[SR_ESR] &= ~(1 << 12); + env->regs[17] = env->pc + 4; + env->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; + env->esr |= 1 << 12 ; + env->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; + t = (msr & (MSR_VM | MSR_UM)) << 1; + msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); + msr |= t; /* Exception in progress. */ - env->sregs[SR_MSR] |= MSR_EIP; + msr |= MSR_EIP; + mb_cpu_write_msr(env, msr); qemu_log_mask(CPU_LOG_INT, - "hw exception at pc=%" PRIx64 " ear=%" PRIx64 " " - "esr=%" PRIx64 " iflags=%x\n", - env->sregs[SR_PC], env->sregs[SR_EAR], - env->sregs[SR_ESR], env->iflags); + "hw exception at pc=%x ear=%" PRIx64 " " + "esr=%x iflags=%x\n", + env->pc, env->ear, + env->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; + env->pc = cpu->cfg.base_vectors + 0x20; break; case EXCP_MMU: - env->regs[17] = env->sregs[SR_PC]; + env->regs[17] = env->pc; + + qemu_log_mask(CPU_LOG_INT, + "MMU exception at pc=%x iflags=%x ear=%" PRIx64 "\n", + env->pc, env->iflags, env->ear); - env->sregs[SR_ESR] &= ~(1 << 12); + env->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; + env->esr |= 1 << 12 ; + env->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=%" PRIx64 " " - "iflags=%x\n", - env->sregs[SR_PC], env->iflags); + if (env->iflags & BIMM_FLAG) { 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; + t = (msr & (MSR_VM | MSR_UM)) << 1; + msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); + msr |= t; /* Exception in progress. */ - env->sregs[SR_MSR] |= MSR_EIP; + msr |= MSR_EIP; + mb_cpu_write_msr(env, msr); qemu_log_mask(CPU_LOG_INT, - "exception at pc=%" PRIx64 " ear=%" PRIx64 " " - "iflags=%x\n", - env->sregs[SR_PC], env->sregs[SR_EAR], env->iflags); + "exception at pc=%x ear=%" PRIx64 " iflags=%x\n", + env->pc, env->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; + env->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(!(msr & (MSR_EIP | MSR_BIP))); + assert(msr & MSR_IE); assert(!(env->iflags & D_FLAG)); - t = (env->sregs[SR_MSR] & (MSR_VM | MSR_UM)) << 1; + t = (msr & (MSR_VM | MSR_UM)) << 1; #if 0 #include "disas/disas.h" @@ -209,53 +205,45 @@ void mb_cpu_do_interrupt(CPUState *cs) { const char *sym; - sym = lookup_symbol(env->sregs[SR_PC]); + sym = lookup_symbol(env->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); + qemu_log("interrupt at pc=%x msr=%x %x iflags=%x sym=%s\n", + env->pc, msr, t, env->iflags, sym); log_cpu_state(cs, 0); } } #endif qemu_log_mask(CPU_LOG_INT, - "interrupt at pc=%" PRIx64 " msr=%" PRIx64 " %x " - "iflags=%x\n", - env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); + "interrupt at pc=%x msr=%x %x iflags=%x\n", + env->pc, msr, t, env->iflags); - env->sregs[SR_MSR] &= ~(MSR_VMS | MSR_UMS | MSR_VM \ - | MSR_UM | MSR_IE); - env->sregs[SR_MSR] |= t; + msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM | MSR_IE); + msr |= t; + mb_cpu_write_msr(env, msr); - env->regs[14] = env->sregs[SR_PC]; - env->sregs[SR_PC] = cpu->cfg.base_vectors + 0x10; + env->regs[14] = env->pc; + env->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; + t = (msr & (MSR_VM | MSR_UM)) << 1; qemu_log_mask(CPU_LOG_INT, - "break at pc=%" PRIx64 " msr=%" PRIx64 " %x " - "iflags=%x\n", - env->sregs[SR_PC], env->sregs[SR_MSR], t, env->iflags); + "break at pc=%x msr=%x %x iflags=%x\n", + env->pc, 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; + msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); + msr |= t; + msr |= MSR_BIP; + env->regs[16] = env->pc; + env->pc = cpu->cfg.base_vectors + 0x18; + mb_cpu_write_msr(env, msr); break; default: cpu_abort(cs, "unhandled exception type=%d\n", @@ -293,8 +281,8 @@ bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) 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->msr & MSR_IE) + && !(env->msr & (MSR_EIP | MSR_BIP)) && !(env->iflags & (D_FLAG | IMM_FLAG))) { cs->exception_index = EXCP_IRQ; mb_cpu_do_interrupt(cs); @@ -302,3 +290,31 @@ bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } return false; } + +void mb_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + uint32_t esr, iflags; + + /* Recover the pc and iflags from the corresponding insn_start. */ + cpu_restore_state(cs, retaddr, true); + iflags = cpu->env.iflags; + + qemu_log_mask(CPU_LOG_INT, + "Unaligned access addr=" TARGET_FMT_lx " pc=%x iflags=%x\n", + (target_ulong)addr, cpu->env.pc, iflags); + + esr = ESR_EC_UNALIGNED_DATA; + if (likely(iflags & ESR_ESS_FLAG)) { + esr |= iflags & ESR_ESS_MASK; + } else { + qemu_log_mask(LOG_UNIMP, "Unaligned access without ESR_ESS_FLAG\n"); + } + + cpu->env.ear = addr; + cpu->env.esr = esr; + cs->exception_index = EXCP_HW_EXCP; + cpu_loop_exit(cs); +} diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index 2f8bdea22b..f740835fcb 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -1,36 +1,31 @@ -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_2(raise_exception, TCG_CALL_NO_WG, noreturn, env, i32) -DEF_HELPER_3(divs, i32, env, i32, i32) -DEF_HELPER_3(divu, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(divs, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(divu, TCG_CALL_NO_WG, 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_FLAGS_3(fadd, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(frsub, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(fmul, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(fdiv, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_2(flt, TCG_CALL_NO_WG, i32, env, i32) +DEF_HELPER_FLAGS_2(fint, TCG_CALL_NO_WG, i32, env, i32) +DEF_HELPER_FLAGS_2(fsqrt, TCG_CALL_NO_WG, 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_3(fcmp_un, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(fcmp_lt, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(fcmp_eq, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(fcmp_le, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(fcmp_gt, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(fcmp_ne, TCG_CALL_NO_WG, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(fcmp_ge, TCG_CALL_NO_WG, i32, env, i32, i32) DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_NO_RWG_SE, i32, i32, i32) #if !defined(CONFIG_USER_ONLY) -DEF_HELPER_3(mmu_read, i32, env, i32, i32) -DEF_HELPER_4(mmu_write, void, env, i32, i32, i32) +DEF_HELPER_FLAGS_3(mmu_read, TCG_CALL_NO_RWG, i32, env, i32, i32) +DEF_HELPER_FLAGS_4(mmu_write, TCG_CALL_NO_RWG, void, env, i32, i32, i32) #endif -DEF_HELPER_5(memalign, void, env, tl, i32, i32, i32) -DEF_HELPER_2(stackprot, void, env, tl) +DEF_HELPER_FLAGS_2(stackprot, TCG_CALL_NO_WG, void, env, tl) -DEF_HELPER_2(get, i32, i32, i32) -DEF_HELPER_3(put, void, i32, i32, i32) +DEF_HELPER_FLAGS_2(get, TCG_CALL_NO_RWG, i32, i32, i32) +DEF_HELPER_FLAGS_3(put, TCG_CALL_NO_RWG, void, i32, i32, i32) diff --git a/target/microblaze/insns.decode b/target/microblaze/insns.decode new file mode 100644 index 0000000000..fb0f0e6838 --- /dev/null +++ b/target/microblaze/insns.decode @@ -0,0 +1,256 @@ +# +# MicroBlaze instruction decode definitions. +# +# Copyright (c) 2020 Richard Henderson <rth@twiddle.net> +# +# 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/>. +# + +&typea0 rd ra +&typea rd ra rb +&typea_br rd rb +&typea_bc ra rb +&typeb rd ra imm +&typeb_br rd imm +&typeb_bc ra imm +&type_msr rd imm + +# Include any IMM prefix in the value reported. +%extimm 0:s16 !function=typeb_imm + +@typea ...... rd:5 ra:5 rb:5 ... .... .... &typea +@typeb ...... rd:5 ra:5 ................ &typeb imm=%extimm + +# Officially typea, but with rb==0, which is not used. +@typea0 ...... rd:5 ra:5 ................ &typea0 + +# Officially typea, but with ra as opcode. +@typea_br ...... rd:5 ..... rb:5 ........... &typea_br + +# Officially typea, but with rd as opcode. +@typea_bc ...... ..... ra:5 rb:5 ........... &typea_bc + +# Officially typeb, but any immediate extension is unused. +@typeb_bs ...... rd:5 ra:5 ..... ...... imm:5 &typeb + +# Officially typeb, but with ra as opcode. +@typeb_br ...... rd:5 ..... ................ &typeb_br imm=%extimm + +# Officially typeb, but with rd as opcode. +@typeb_bc ...... ..... ra:5 ................ &typeb_bc imm=%extimm + +# For convenience, extract the two imm_w/imm_s fields, then pack +# them back together as "imm". Doing this makes it easiest to +# match the required zero at bit 5. +%ieimm 6:5 0:5 +@typeb_ie ...... rd:5 ra:5 ..... ..... . ..... &typeb imm=%ieimm + +@type_msr ...... rd:5 ...... imm:15 &type_msr + +### + +{ + zero 000000 00000 00000 00000 000 0000 0000 + add 000000 ..... ..... ..... 000 0000 0000 @typea +} +addc 000010 ..... ..... ..... 000 0000 0000 @typea +addk 000100 ..... ..... ..... 000 0000 0000 @typea +addkc 000110 ..... ..... ..... 000 0000 0000 @typea + +addi 001000 ..... ..... ................ @typeb +addic 001010 ..... ..... ................ @typeb +addik 001100 ..... ..... ................ @typeb +addikc 001110 ..... ..... ................ @typeb + +and 100001 ..... ..... ..... 000 0000 0000 @typea +andi 101001 ..... ..... ................ @typeb + +andn 100011 ..... ..... ..... 000 0000 0000 @typea +andni 101011 ..... ..... ................ @typeb + +beq 100111 00000 ..... ..... 000 0000 0000 @typea_bc +bge 100111 00101 ..... ..... 000 0000 0000 @typea_bc +bgt 100111 00100 ..... ..... 000 0000 0000 @typea_bc +ble 100111 00011 ..... ..... 000 0000 0000 @typea_bc +blt 100111 00010 ..... ..... 000 0000 0000 @typea_bc +bne 100111 00001 ..... ..... 000 0000 0000 @typea_bc + +beqd 100111 10000 ..... ..... 000 0000 0000 @typea_bc +bged 100111 10101 ..... ..... 000 0000 0000 @typea_bc +bgtd 100111 10100 ..... ..... 000 0000 0000 @typea_bc +bled 100111 10011 ..... ..... 000 0000 0000 @typea_bc +bltd 100111 10010 ..... ..... 000 0000 0000 @typea_bc +bned 100111 10001 ..... ..... 000 0000 0000 @typea_bc + +beqi 101111 00000 ..... ................ @typeb_bc +bgei 101111 00101 ..... ................ @typeb_bc +bgti 101111 00100 ..... ................ @typeb_bc +blei 101111 00011 ..... ................ @typeb_bc +blti 101111 00010 ..... ................ @typeb_bc +bnei 101111 00001 ..... ................ @typeb_bc + +beqid 101111 10000 ..... ................ @typeb_bc +bgeid 101111 10101 ..... ................ @typeb_bc +bgtid 101111 10100 ..... ................ @typeb_bc +bleid 101111 10011 ..... ................ @typeb_bc +bltid 101111 10010 ..... ................ @typeb_bc +bneid 101111 10001 ..... ................ @typeb_bc + +br 100110 ..... 00000 ..... 000 0000 0000 @typea_br +bra 100110 ..... 01000 ..... 000 0000 0000 @typea_br +brd 100110 ..... 10000 ..... 000 0000 0000 @typea_br +brad 100110 ..... 11000 ..... 000 0000 0000 @typea_br +brld 100110 ..... 10100 ..... 000 0000 0000 @typea_br +brald 100110 ..... 11100 ..... 000 0000 0000 @typea_br + +bri 101110 ..... 00000 ................ @typeb_br +brai 101110 ..... 01000 ................ @typeb_br +brid 101110 ..... 10000 ................ @typeb_br +braid 101110 ..... 11000 ................ @typeb_br +brlid 101110 ..... 10100 ................ @typeb_br +bralid 101110 ..... 11100 ................ @typeb_br + +brk 100110 ..... 01100 ..... 000 0000 0000 @typea_br +brki 101110 ..... 01100 ................ @typeb_br + +bsrl 010001 ..... ..... ..... 000 0000 0000 @typea +bsra 010001 ..... ..... ..... 010 0000 0000 @typea +bsll 010001 ..... ..... ..... 100 0000 0000 @typea + +bsrli 011001 ..... ..... 00000 000000 ..... @typeb_bs +bsrai 011001 ..... ..... 00000 010000 ..... @typeb_bs +bslli 011001 ..... ..... 00000 100000 ..... @typeb_bs + +bsefi 011001 ..... ..... 01000 .....0 ..... @typeb_ie +bsifi 011001 ..... ..... 10000 .....0 ..... @typeb_ie + +clz 100100 ..... ..... 00000 000 1110 0000 @typea0 + +cmp 000101 ..... ..... ..... 000 0000 0001 @typea +cmpu 000101 ..... ..... ..... 000 0000 0011 @typea + +fadd 010110 ..... ..... ..... 0000 000 0000 @typea +frsub 010110 ..... ..... ..... 0001 000 0000 @typea +fmul 010110 ..... ..... ..... 0010 000 0000 @typea +fdiv 010110 ..... ..... ..... 0011 000 0000 @typea +fcmp_un 010110 ..... ..... ..... 0100 000 0000 @typea +fcmp_lt 010110 ..... ..... ..... 0100 001 0000 @typea +fcmp_eq 010110 ..... ..... ..... 0100 010 0000 @typea +fcmp_le 010110 ..... ..... ..... 0100 011 0000 @typea +fcmp_gt 010110 ..... ..... ..... 0100 100 0000 @typea +fcmp_ne 010110 ..... ..... ..... 0100 101 0000 @typea +fcmp_ge 010110 ..... ..... ..... 0100 110 0000 @typea + +# Note that flt and fint, unlike fsqrt, are documented as having the RB +# operand which is unused. So allow the field to be non-zero but discard +# the value and treat as 2-operand insns. +flt 010110 ..... ..... ----- 0101 000 0000 @typea0 +fint 010110 ..... ..... ----- 0110 000 0000 @typea0 +fsqrt 010110 ..... ..... 00000 0111 000 0000 @typea0 + +get 011011 rd:5 00000 0 ctrl:5 000000 imm:4 +getd 010011 rd:5 00000 rb:5 0 ctrl:5 00000 + +idiv 010010 ..... ..... ..... 000 0000 0000 @typea +idivu 010010 ..... ..... ..... 000 0000 0010 @typea + +imm 101100 00000 00000 imm:16 + +lbu 110000 ..... ..... ..... 0000 000 0000 @typea +lbur 110000 ..... ..... ..... 0100 000 0000 @typea +lbuea 110000 ..... ..... ..... 0001 000 0000 @typea +lbui 111000 ..... ..... ................ @typeb + +lhu 110001 ..... ..... ..... 0000 000 0000 @typea +lhur 110001 ..... ..... ..... 0100 000 0000 @typea +lhuea 110001 ..... ..... ..... 0001 000 0000 @typea +lhui 111001 ..... ..... ................ @typeb + +lw 110010 ..... ..... ..... 0000 000 0000 @typea +lwr 110010 ..... ..... ..... 0100 000 0000 @typea +lwea 110010 ..... ..... ..... 0001 000 0000 @typea +lwx 110010 ..... ..... ..... 1000 000 0000 @typea +lwi 111010 ..... ..... ................ @typeb + +mbar 101110 imm:5 00010 0000 0000 0000 0100 + +mfs 100101 rd:5 0 e:1 000 10 rs:14 +mts 100101 0 e:1 000 ra:5 11 rs:14 + +msrclr 100101 ..... 100010 ............... @type_msr +msrset 100101 ..... 100000 ............... @type_msr + +mul 010000 ..... ..... ..... 000 0000 0000 @typea +mulh 010000 ..... ..... ..... 000 0000 0001 @typea +mulhu 010000 ..... ..... ..... 000 0000 0011 @typea +mulhsu 010000 ..... ..... ..... 000 0000 0010 @typea +muli 011000 ..... ..... ................ @typeb + +or 100000 ..... ..... ..... 000 0000 0000 @typea +ori 101000 ..... ..... ................ @typeb + +pcmpbf 100000 ..... ..... ..... 100 0000 0000 @typea +pcmpeq 100010 ..... ..... ..... 100 0000 0000 @typea +pcmpne 100011 ..... ..... ..... 100 0000 0000 @typea + +put 011011 00000 ra:5 1 ctrl:5 000000 imm:4 +putd 010011 00000 ra:5 rb:5 1 ctrl:5 00000 + +rsub 000001 ..... ..... ..... 000 0000 0000 @typea +rsubc 000011 ..... ..... ..... 000 0000 0000 @typea +rsubk 000101 ..... ..... ..... 000 0000 0000 @typea +rsubkc 000111 ..... ..... ..... 000 0000 0000 @typea + +rsubi 001001 ..... ..... ................ @typeb +rsubic 001011 ..... ..... ................ @typeb +rsubik 001101 ..... ..... ................ @typeb +rsubikc 001111 ..... ..... ................ @typeb + +rtbd 101101 10010 ..... ................ @typeb_bc +rtid 101101 10001 ..... ................ @typeb_bc +rted 101101 10100 ..... ................ @typeb_bc +rtsd 101101 10000 ..... ................ @typeb_bc + +sb 110100 ..... ..... ..... 0000 000 0000 @typea +sbr 110100 ..... ..... ..... 0100 000 0000 @typea +sbea 110100 ..... ..... ..... 0001 000 0000 @typea +sbi 111100 ..... ..... ................ @typeb + +sh 110101 ..... ..... ..... 0000 000 0000 @typea +shr 110101 ..... ..... ..... 0100 000 0000 @typea +shea 110101 ..... ..... ..... 0001 000 0000 @typea +shi 111101 ..... ..... ................ @typeb + +sw 110110 ..... ..... ..... 0000 000 0000 @typea +swr 110110 ..... ..... ..... 0100 000 0000 @typea +swea 110110 ..... ..... ..... 0001 000 0000 @typea +swx 110110 ..... ..... ..... 1000 000 0000 @typea +swi 111110 ..... ..... ................ @typeb + +sext8 100100 ..... ..... 00000 000 0110 0000 @typea0 +sext16 100100 ..... ..... 00000 000 0110 0001 @typea0 + +sra 100100 ..... ..... 00000 000 0000 0001 @typea0 +src 100100 ..... ..... 00000 000 0010 0001 @typea0 +srl 100100 ..... ..... 00000 000 0100 0001 @typea0 + +swapb 100100 ..... ..... 00000 001 1110 0000 @typea0 +swaph 100100 ..... ..... 00000 001 1110 0010 @typea0 + +# Cache operations have no effect in qemu: discard the arguments. +wdic 100100 00000 ----- ----- -00 -11- 01-0 # wdc +wdic 100100 00000 ----- ----- 000 0110 1000 # wic + +xor 100010 ..... ..... ..... 000 0000 0000 @typea +xori 101010 ..... ..... ................ @typeb diff --git a/target/microblaze/meson.build b/target/microblaze/meson.build index b8fe4afe61..639c3f73a8 100644 --- a/target/microblaze/meson.build +++ b/target/microblaze/meson.build @@ -1,4 +1,7 @@ +gen = decodetree.process('insns.decode') + microblaze_ss = ss.source_set() +microblaze_ss.add(gen) microblaze_ss.add(files( 'cpu.c', 'gdbstub.c', diff --git a/target/microblaze/microblaze-decode.h b/target/microblaze/microblaze-decode.h deleted file mode 100644 index 17b2f29fff..0000000000 --- a/target/microblaze/microblaze-decode.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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/>. - */ - -#ifndef TARGET_MICROBLAZE_MICROBLAZE_DECODE_H -#define TARGET_MICROBLAZE_MICROBLAZE_DECODE_H - -/* 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)} - -#endif diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 6763421ba2..6e583d78d9 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -250,8 +250,8 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) if (rn == MMU_R_TLBHI) { if (i < 3 && !(v & TLB_VALID) && qemu_loglevel_mask(~0)) qemu_log_mask(LOG_GUEST_ERROR, - "invalidating index %x at pc=%" PRIx64 "\n", - i, env->sregs[SR_PC]); + "invalidating index %x at pc=%x\n", + i, env->pc); env->mmu.tids[i] = env->mmu.regs[MMU_R_PID] & 0xff; mmu_flush_idx(env, i); } diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index f3b17a95b3..4614e99db3 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -26,8 +26,6 @@ #include "exec/cpu_ldst.h" #include "fpu/softfloat.h" -#define D(x) - void helper_put(uint32_t id, uint32_t ctrl, uint32_t data) { int test = ctrl & STREAM_TEST; @@ -71,85 +69,27 @@ void helper_raise_exception(CPUMBState *env, uint32_t index) cpu_loop_exit(cs); } -void helper_debug(CPUMBState *env) -{ - int i; - - qemu_log("PC=%" PRIx64 "\n", env->sregs[SR_PC]); - qemu_log("rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " " - "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=%" PRIx64 " 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", - (bool)(env->sregs[SR_MSR] & MSR_EIP), - (bool)(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) +static bool check_divz(CPUMBState *env, uint32_t a, uint32_t b, uintptr_t ra) { - uint32_t t; + if (unlikely(b == 0)) { + env->msr |= MSR_DZ; - t = b + ~a + 1; - if ((b & 0x80000000) ^ (a & 0x80000000)) - t = (t & 0x7fffffff) | (a & 0x80000000); - return t; -} + if ((env->msr & MSR_EE) && + env_archcpu(env)->cfg.div_zero_exception) { + CPUState *cs = env_cpu(env); -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) -{ - MicroBlazeCPU *cpu = env_archcpu(env); - - if (b == 0) { - env->sregs[SR_MSR] |= MSR_DZ; - - if ((env->sregs[SR_MSR] & MSR_EE) && cpu->cfg.div_zero_exception) { - env->sregs[SR_ESR] = ESR_EC_DIVZERO; - helper_raise_exception(env, EXCP_HW_EXCP); + env->esr = ESR_EC_DIVZERO; + cs->exception_index = EXCP_HW_EXCP; + cpu_loop_exit_restore(cs, ra); } - return 0; + return false; } - env->sregs[SR_MSR] &= ~MSR_DZ; - return 1; + return true; } uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b) { - if (!div_prepare(env, a, b)) { + if (!check_divz(env, a, b, GETPC())) { return 0; } return (int32_t)a / (int32_t)b; @@ -157,43 +97,46 @@ uint32_t helper_divs(CPUMBState *env, uint32_t a, uint32_t b) uint32_t helper_divu(CPUMBState *env, uint32_t a, uint32_t b) { - if (!div_prepare(env, a, b)) { + if (!check_divz(env, a, b, GETPC())) { return 0; } return a / b; } /* raise FPU exception. */ -static void raise_fpu_exception(CPUMBState *env) +static void raise_fpu_exception(CPUMBState *env, uintptr_t ra) { - env->sregs[SR_ESR] = ESR_EC_FPU; - helper_raise_exception(env, EXCP_HW_EXCP); + CPUState *cs = env_cpu(env); + + env->esr = ESR_EC_FPU; + cs->exception_index = EXCP_HW_EXCP; + cpu_loop_exit_restore(cs, ra); } -static void update_fpu_flags(CPUMBState *env, int flags) +static void update_fpu_flags(CPUMBState *env, int flags, uintptr_t ra) { int raise = 0; if (flags & float_flag_invalid) { - env->sregs[SR_FSR] |= FSR_IO; + env->fsr |= FSR_IO; raise = 1; } if (flags & float_flag_divbyzero) { - env->sregs[SR_FSR] |= FSR_DZ; + env->fsr |= FSR_DZ; raise = 1; } if (flags & float_flag_overflow) { - env->sregs[SR_FSR] |= FSR_OF; + env->fsr |= FSR_OF; raise = 1; } if (flags & float_flag_underflow) { - env->sregs[SR_FSR] |= FSR_UF; + env->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); + && (env->msr & MSR_EE)) { + raise_fpu_exception(env, ra); } } @@ -208,7 +151,7 @@ uint32_t helper_fadd(CPUMBState *env, uint32_t a, uint32_t 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); + update_fpu_flags(env, flags, GETPC()); return fd.l; } @@ -222,7 +165,7 @@ uint32_t helper_frsub(CPUMBState *env, uint32_t a, uint32_t b) 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); + update_fpu_flags(env, flags, GETPC()); return fd.l; } @@ -236,7 +179,7 @@ uint32_t helper_fmul(CPUMBState *env, uint32_t a, uint32_t b) 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); + update_fpu_flags(env, flags, GETPC()); return fd.l; } @@ -251,7 +194,7 @@ uint32_t helper_fdiv(CPUMBState *env, uint32_t a, uint32_t b) 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); + update_fpu_flags(env, flags, GETPC()); return fd.l; } @@ -266,7 +209,7 @@ uint32_t helper_fcmp_un(CPUMBState *env, uint32_t a, uint32_t b) if (float32_is_signaling_nan(fa.f, &env->fp_status) || float32_is_signaling_nan(fb.f, &env->fp_status)) { - update_fpu_flags(env, float_flag_invalid); + update_fpu_flags(env, float_flag_invalid, GETPC()); r = 1; } @@ -289,7 +232,7 @@ uint32_t helper_fcmp_lt(CPUMBState *env, uint32_t a, uint32_t b) 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); + update_fpu_flags(env, flags & float_flag_invalid, GETPC()); return r; } @@ -305,7 +248,7 @@ uint32_t helper_fcmp_eq(CPUMBState *env, uint32_t a, uint32_t b) 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); + update_fpu_flags(env, flags & float_flag_invalid, GETPC()); return r; } @@ -321,7 +264,7 @@ uint32_t helper_fcmp_le(CPUMBState *env, uint32_t a, uint32_t 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); + update_fpu_flags(env, flags & float_flag_invalid, GETPC()); return r; @@ -337,7 +280,7 @@ uint32_t helper_fcmp_gt(CPUMBState *env, uint32_t a, uint32_t 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); + update_fpu_flags(env, flags & float_flag_invalid, GETPC()); return r; } @@ -351,7 +294,7 @@ uint32_t helper_fcmp_ne(CPUMBState *env, uint32_t a, uint32_t 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); + update_fpu_flags(env, flags & float_flag_invalid, GETPC()); return r; } @@ -366,7 +309,7 @@ uint32_t helper_fcmp_ge(CPUMBState *env, uint32_t a, uint32_t 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); + update_fpu_flags(env, flags & float_flag_invalid, GETPC()); return r; } @@ -390,7 +333,7 @@ uint32_t helper_fint(CPUMBState *env, uint32_t a) 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); + update_fpu_flags(env, flags, GETPC()); return r; } @@ -404,7 +347,7 @@ uint32_t helper_fsqrt(CPUMBState *env, uint32_t a) 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); + update_fpu_flags(env, flags, GETPC()); return fd.l; } @@ -422,37 +365,19 @@ uint32_t helper_pcmpbf(uint32_t a, uint32_t b) return 0; } -void helper_memalign(CPUMBState *env, target_ulong addr, - uint32_t dr, uint32_t wr, - uint32_t mask) -{ - if (addr & mask) { - qemu_log_mask(CPU_LOG_INT, - "unaligned access addr=" TARGET_FMT_lx - " 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, target_ulong addr) { if (addr < env->slr || addr > env->shr) { + CPUState *cs = env_cpu(env); + qemu_log_mask(CPU_LOG_INT, "Stack protector violation at " TARGET_FMT_lx " %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); + + env->ear = addr; + env->esr = ESR_EC_STACKPROT; + cs->exception_index = EXCP_HW_EXCP; + cpu_loop_exit_restore(cs, GETPC()); } } @@ -473,32 +398,33 @@ void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, int mmu_idx, MemTxAttrs attrs, MemTxResult response, uintptr_t retaddr) { - MicroBlazeCPU *cpu; - CPUMBState *env; + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + CPUMBState *env = &cpu->env; + qemu_log_mask(CPU_LOG_INT, "Transaction failed: vaddr 0x%" VADDR_PRIx " physaddr 0x" TARGET_FMT_plx " size %d access type %s\n", addr, physaddr, size, access_type == MMU_INST_FETCH ? "INST_FETCH" : (access_type == MMU_DATA_LOAD ? "DATA_LOAD" : "DATA_STORE")); - cpu = MICROBLAZE_CPU(cs); - env = &cpu->env; - cpu_restore_state(cs, retaddr, true); - if (!(env->sregs[SR_MSR] & MSR_EE)) { + if (!(env->msr & MSR_EE)) { return; } - env->sregs[SR_EAR] = addr; if (access_type == MMU_INST_FETCH) { - 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); + if (!cpu->cfg.iopb_bus_exception) { + return; } + env->esr = ESR_EC_INSN_BUS; } 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); + if (!cpu->cfg.dopb_bus_exception) { + return; } + env->esr = ESR_EC_DATA_BUS; } + + env->ear = addr; + cs->exception_index = EXCP_HW_EXCP; + cpu_loop_exit_restore(cs, retaddr); } #endif diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index a96cb21d96..a377818b5e 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -24,7 +24,6 @@ #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" -#include "microblaze-decode.h" #include "exec/cpu_ldst.h" #include "exec/helper-gen.h" #include "exec/translator.h" @@ -33,106 +32,99 @@ #include "trace-tcg.h" #include "exec/log.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)) /* is_jmp field values */ #define DISAS_JUMP DISAS_TARGET_0 /* only pc was modified dynamically */ #define DISAS_UPDATE DISAS_TARGET_1 /* cpu state was modified dynamically */ -#define DISAS_TB_JUMP DISAS_TARGET_2 /* only pc was modified statically */ -static TCGv_i32 env_debug; static TCGv_i32 cpu_R[32]; -static TCGv_i64 cpu_SR[14]; -static TCGv_i32 env_imm; -static TCGv_i32 env_btaken; -static TCGv_i64 env_btarget; -static TCGv_i32 env_iflags; -static TCGv env_res_addr; -static TCGv_i32 env_res_val; +static TCGv_i32 cpu_pc; +static TCGv_i32 cpu_msr; +static TCGv_i32 cpu_msr_c; +static TCGv_i32 cpu_imm; +static TCGv_i32 cpu_bvalue; +static TCGv_i32 cpu_btarget; +static TCGv_i32 cpu_iflags; +static TCGv cpu_res_addr; +static TCGv_i32 cpu_res_val; #include "exec/gen-icount.h" /* This is the state at translation time. */ typedef struct DisasContext { + DisasContextBase base; MicroBlazeCPU *cpu; - uint32_t pc; - /* Decoder. */ - int type_b; - uint32_t ir; - uint8_t opcode; - uint8_t rd, ra, rb; - uint16_t imm; + /* TCG op of the current insn_start. */ + TCGOp *insn_start; + + TCGv_i32 r0; + bool r0_set; + /* Decoder. */ + uint32_t ext_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; - struct TranslationBlock *tb; - int singlestep_enabled; + unsigned int tb_flags; + unsigned int tb_flags_to_set; + int mem_index; + + /* Condition under which to jump, including NEVER and ALWAYS. */ + TCGCond jmp_cond; + + /* Immediate branch-taken destination, or -1 for indirect. */ + uint32_t jmp_dest; } DisasContext; -static const char *regnames[] = +static int typeb_imm(DisasContext *dc, int x) { - "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", -}; + if (dc->tb_flags & IMM_FLAG) { + return deposit32(dc->ext_imm, 0, 16, x); + } + return x; +} -static const char *special_regnames[] = -{ - "rpc", "rmsr", "sr2", "rear", "sr4", "resr", "sr6", "rfsr", - "sr8", "sr9", "sr10", "rbtr", "sr12", "redr" -}; +/* Include the auto-generated decoder. */ +#include "decode-insns.c.inc" -static inline void t_sync_flags(DisasContext *dc) +static 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_i32(env_iflags, dc->tb_flags); - dc->synced_flags = dc->tb_flags; + if ((dc->tb_flags ^ dc->base.tb->flags) & ~MSR_TB_MASK) { + tcg_gen_movi_i32(cpu_iflags, dc->tb_flags & ~MSR_TB_MASK); } } -static inline void t_gen_raise_exception(DisasContext *dc, uint32_t index) +static void gen_raise_exception(DisasContext *dc, uint32_t index) { TCGv_i32 tmp = tcg_const_i32(index); - t_sync_flags(dc); - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); gen_helper_raise_exception(cpu_env, tmp); tcg_temp_free_i32(tmp); - dc->is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_NORETURN; +} + +static void gen_raise_exception_sync(DisasContext *dc, uint32_t index) +{ + t_sync_flags(dc); + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); + gen_raise_exception(dc, index); +} + +static void gen_raise_hw_excp(DisasContext *dc, uint32_t esr_ec) +{ + TCGv_i32 tmp = tcg_const_i32(esr_ec); + tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUMBState, esr)); + tcg_temp_free_i32(tmp); + + gen_raise_exception_sync(dc, EXCP_HW_EXCP); } static inline bool use_goto_tb(DisasContext *dc, target_ulong dest) { #ifndef CONFIG_USER_ONLY - return (dc->tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); + return (dc->base.pc_first & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK); #else return true; #endif @@ -140,42 +132,20 @@ static inline bool use_goto_tb(DisasContext *dc, target_ulong dest) static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) { - if (use_goto_tb(dc, dest)) { + if (dc->base.singlestep_enabled) { + TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); + tcg_gen_movi_i32(cpu_pc, dest); + gen_helper_raise_exception(cpu_env, tmp); + tcg_temp_free_i32(tmp); + } else if (use_goto_tb(dc, dest)) { tcg_gen_goto_tb(n); - tcg_gen_movi_i64(cpu_SR[SR_PC], dest); - tcg_gen_exit_tb(dc->tb, n); + tcg_gen_movi_i32(cpu_pc, dest); + tcg_gen_exit_tb(dc->base.tb, n); } else { - tcg_gen_movi_i64(cpu_SR[SR_PC], dest); + tcg_gen_movi_i32(cpu_pc, dest); tcg_gen_exit_tb(NULL, 0); } -} - -static void read_carry(DisasContext *dc, TCGv_i32 d) -{ - tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); - tcg_gen_shri_i32(d, d, 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_i32 v) -{ - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(t0, v); - /* Deposit bit 0 into MSR_C and the alias MSR_CC. */ - tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 2, 1); - tcg_gen_deposit_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t0, 31, 1); - tcg_temp_free_i64(t0); -} - -static void write_carryi(DisasContext *dc, bool carry) -{ - TCGv_i32 t0 = tcg_temp_new_i32(); - tcg_gen_movi_i32(t0, carry); - write_carry(dc, t0); - tcg_temp_free_i32(t0); + dc->base.is_jmp = DISAS_NORETURN; } /* @@ -184,10 +154,9 @@ static void write_carryi(DisasContext *dc, bool carry) */ static bool trap_illegal(DisasContext *dc, bool cond) { - if (cond && (dc->tb_flags & MSR_EE_FLAG) + if (cond && (dc->tb_flags & MSR_EE) && dc->cpu->cfg.illegal_opcode_exception) { - tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_ILLEGAL_OP); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + gen_raise_hw_excp(dc, ESR_EC_ILLEGAL_OP); } return cond; } @@ -198,759 +167,717 @@ static bool trap_illegal(DisasContext *dc, bool cond) */ static bool trap_userspace(DisasContext *dc, bool cond) { - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - bool cond_user = cond && mem_index == MMU_USER_IDX; + bool cond_user = cond && dc->mem_index == MMU_USER_IDX; - if (cond_user && (dc->tb_flags & MSR_EE_FLAG)) { - tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_PRIVINSN); - t_gen_raise_exception(dc, EXCP_HW_EXCP); + if (cond_user && (dc->tb_flags & MSR_EE)) { + gen_raise_hw_excp(dc, ESR_EC_PRIVINSN); } return cond_user; } -/* 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) +static TCGv_i32 reg_for_read(DisasContext *dc, int reg) { - /* Immediate insn without the imm prefix ? */ - return dc->type_b && !(dc->tb_flags & IMM_FLAG); + if (likely(reg != 0)) { + return cpu_R[reg]; + } + if (!dc->r0_set) { + if (dc->r0 == NULL) { + dc->r0 = tcg_temp_new_i32(); + } + tcg_gen_movi_i32(dc->r0, 0); + dc->r0_set = true; + } + return dc->r0; } -static inline TCGv_i32 *dec_alu_op_b(DisasContext *dc) +static TCGv_i32 reg_for_write(DisasContext *dc, int reg) { - if (dc->type_b) { - if (dc->tb_flags & IMM_FLAG) - tcg_gen_ori_i32(env_imm, env_imm, dc->imm); - else - tcg_gen_movi_i32(env_imm, (int32_t)((int16_t)dc->imm)); - return &env_imm; - } else - return &cpu_R[dc->rb]; + if (likely(reg != 0)) { + return cpu_R[reg]; + } + if (dc->r0 == NULL) { + dc->r0 = tcg_temp_new_i32(); + } + return dc->r0; } -static void dec_add(DisasContext *dc) +static bool do_typea(DisasContext *dc, arg_typea *arg, bool side_effects, + void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32)) { - unsigned int k, c; - TCGv_i32 cf; + TCGv_i32 rd, ra, rb; - 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); + if (arg->rd == 0 && !side_effects) { + return true; + } - /* 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_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + rd = reg_for_write(dc, arg->rd); + ra = reg_for_read(dc, arg->ra); + rb = reg_for_read(dc, arg->rb); + fn(rd, ra, rb); + return true; +} - if (c) { - /* c - Add carry into the result. */ - cf = tcg_temp_new_i32(); +static bool do_typea0(DisasContext *dc, arg_typea0 *arg, bool side_effects, + void (*fn)(TCGv_i32, TCGv_i32)) +{ + TCGv_i32 rd, ra; - read_carry(dc, cf); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free_i32(cf); - } - } - return; + if (arg->rd == 0 && !side_effects) { + return true; } - /* From now on, we can assume k is zero. So we need to update MSR. */ - /* Extract carry. */ - cf = tcg_temp_new_i32(); - if (c) { - read_carry(dc, cf); - } else { - tcg_gen_movi_i32(cf, 0); + rd = reg_for_write(dc, arg->rd); + ra = reg_for_read(dc, arg->ra); + fn(rd, ra); + return true; +} + +static bool do_typeb_imm(DisasContext *dc, arg_typeb *arg, bool side_effects, + void (*fni)(TCGv_i32, TCGv_i32, int32_t)) +{ + TCGv_i32 rd, ra; + + if (arg->rd == 0 && !side_effects) { + return true; } - if (dc->rd) { - TCGv_i32 ncf = tcg_temp_new_i32(); - gen_helper_carry(ncf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); - write_carry(dc, ncf); - tcg_temp_free_i32(ncf); - } else { - gen_helper_carry(cf, cpu_R[dc->ra], *(dec_alu_op_b(dc)), cf); - write_carry(dc, cf); + rd = reg_for_write(dc, arg->rd); + ra = reg_for_read(dc, arg->ra); + fni(rd, ra, arg->imm); + return true; +} + +static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects, + void (*fn)(TCGv_i32, TCGv_i32, TCGv_i32)) +{ + TCGv_i32 rd, ra, imm; + + if (arg->rd == 0 && !side_effects) { + return true; } - tcg_temp_free_i32(cf); + + rd = reg_for_write(dc, arg->rd); + ra = reg_for_read(dc, arg->ra); + imm = tcg_const_i32(arg->imm); + + fn(rd, ra, imm); + + tcg_temp_free_i32(imm); + return true; } -static void dec_sub(DisasContext *dc) +#define DO_TYPEA(NAME, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typea *a) \ + { return do_typea(dc, a, SE, FN); } + +#define DO_TYPEA_CFG(NAME, CFG, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typea *a) \ + { return dc->cpu->cfg.CFG && do_typea(dc, a, SE, FN); } + +#define DO_TYPEA0(NAME, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typea0 *a) \ + { return do_typea0(dc, a, SE, FN); } + +#define DO_TYPEA0_CFG(NAME, CFG, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typea0 *a) \ + { return dc->cpu->cfg.CFG && do_typea0(dc, a, SE, FN); } + +#define DO_TYPEBI(NAME, SE, FNI) \ + static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \ + { return do_typeb_imm(dc, a, SE, FNI); } + +#define DO_TYPEBI_CFG(NAME, CFG, SE, FNI) \ + static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \ + { return dc->cpu->cfg.CFG && do_typeb_imm(dc, a, SE, FNI); } + +#define DO_TYPEBV(NAME, SE, FN) \ + static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \ + { return do_typeb_val(dc, a, SE, FN); } + +#define ENV_WRAPPER2(NAME, HELPER) \ + static void NAME(TCGv_i32 out, TCGv_i32 ina) \ + { HELPER(out, cpu_env, ina); } + +#define ENV_WRAPPER3(NAME, HELPER) \ + static void NAME(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) \ + { HELPER(out, cpu_env, ina, inb); } + +/* No input carry, but output carry. */ +static void gen_add(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - unsigned int u, cmp, k, c; - TCGv_i32 cf, na; + TCGv_i32 zero = tcg_const_i32(0); - u = dc->imm & 2; - k = dc->opcode & 4; - c = dc->opcode & 2; - cmp = (dc->imm & 1) && (!dc->type_b) && k; + tcg_gen_add2_i32(out, cpu_msr_c, ina, zero, inb, zero); - 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; - } + tcg_temp_free_i32(zero); +} - LOG_DIS("sub%s%s r%d, r%d r%d\n", - k ? "k" : "", c ? "c" : "", dc->rd, dc->ra, dc->rb); +/* Input and output carry. */ +static void gen_addc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 tmp = tcg_temp_new_i32(); - /* 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_i32(cpu_R[dc->rd], *(dec_alu_op_b(dc)), cpu_R[dc->ra]); + tcg_gen_add2_i32(tmp, cpu_msr_c, ina, zero, cpu_msr_c, zero); + tcg_gen_add2_i32(out, cpu_msr_c, tmp, cpu_msr_c, inb, zero); - if (c) { - /* c - Add carry into the result. */ - cf = tcg_temp_new_i32(); + tcg_temp_free_i32(tmp); + tcg_temp_free_i32(zero); +} - read_carry(dc, cf); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); - tcg_temp_free_i32(cf); - } - } - return; - } +/* Input carry, but no output carry. */ +static void gen_addkc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + tcg_gen_add_i32(out, ina, inb); + tcg_gen_add_i32(out, out, cpu_msr_c); +} - /* 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_i32(); - na = tcg_temp_new_i32(); - if (c) { - read_carry(dc, cf); - } else { - tcg_gen_movi_i32(cf, 1); - } +DO_TYPEA(add, true, gen_add) +DO_TYPEA(addc, true, gen_addc) +DO_TYPEA(addk, false, tcg_gen_add_i32) +DO_TYPEA(addkc, true, gen_addkc) - /* d = b + ~a + c. carry defaults to 1. */ - tcg_gen_not_i32(na, cpu_R[dc->ra]); +DO_TYPEBV(addi, true, gen_add) +DO_TYPEBV(addic, true, gen_addc) +DO_TYPEBI(addik, false, tcg_gen_addi_i32) +DO_TYPEBV(addikc, true, gen_addkc) - if (dc->rd) { - TCGv_i32 ncf = tcg_temp_new_i32(); - gen_helper_carry(ncf, na, *(dec_alu_op_b(dc)), cf); - tcg_gen_add_i32(cpu_R[dc->rd], na, *(dec_alu_op_b(dc))); - tcg_gen_add_i32(cpu_R[dc->rd], cpu_R[dc->rd], cf); - write_carry(dc, ncf); - tcg_temp_free_i32(ncf); - } else { - gen_helper_carry(cf, na, *(dec_alu_op_b(dc)), cf); - write_carry(dc, cf); - } - tcg_temp_free_i32(cf); - tcg_temp_free_i32(na); +static void gen_andni(TCGv_i32 out, TCGv_i32 ina, int32_t imm) +{ + tcg_gen_andi_i32(out, ina, ~imm); } -static void dec_pattern(DisasContext *dc) +DO_TYPEA(and, false, tcg_gen_and_i32) +DO_TYPEBI(andi, false, tcg_gen_andi_i32) +DO_TYPEA(andn, false, tcg_gen_andc_i32) +DO_TYPEBI(andni, false, gen_andni) + +static void gen_bsra(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - unsigned int mode; + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, inb, 31); + tcg_gen_sar_i32(out, ina, tmp); + tcg_temp_free_i32(tmp); +} - if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { - return; - } +static void gen_bsrl(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, inb, 31); + tcg_gen_shr_i32(out, ina, tmp); + tcg_temp_free_i32(tmp); +} - 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_i32(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_i32(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 gen_bsll(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, inb, 31); + tcg_gen_shl_i32(out, ina, tmp); + tcg_temp_free_i32(tmp); } -static void dec_and(DisasContext *dc) +static void gen_bsefi(TCGv_i32 out, TCGv_i32 ina, int32_t imm) { - unsigned int not; + /* Note that decodetree has extracted and reassembled imm_w/imm_s. */ + int imm_w = extract32(imm, 5, 5); + int imm_s = extract32(imm, 0, 5); + + if (imm_w + imm_s > 32 || imm_w == 0) { + /* These inputs have an undefined behavior. */ + qemu_log_mask(LOG_GUEST_ERROR, "bsefi: Bad input w=%d s=%d\n", + imm_w, imm_s); + } else { + tcg_gen_extract_i32(out, ina, imm_s, imm_w); + } +} - if (!dc->type_b && (dc->imm & (1 << 10))) { - dec_pattern(dc); - return; +static void gen_bsifi(TCGv_i32 out, TCGv_i32 ina, int32_t imm) +{ + /* Note that decodetree has extracted and reassembled imm_w/imm_s. */ + int imm_w = extract32(imm, 5, 5); + int imm_s = extract32(imm, 0, 5); + int width = imm_w - imm_s + 1; + + if (imm_w < imm_s) { + /* These inputs have an undefined behavior. */ + qemu_log_mask(LOG_GUEST_ERROR, "bsifi: Bad input w=%d s=%d\n", + imm_w, imm_s); + } else { + tcg_gen_deposit_i32(out, out, ina, imm_s, width); } +} - not = dc->opcode & (1 << 1); - LOG_DIS("and%s\n", not ? "n" : ""); +DO_TYPEA_CFG(bsra, use_barrel, false, gen_bsra) +DO_TYPEA_CFG(bsrl, use_barrel, false, gen_bsrl) +DO_TYPEA_CFG(bsll, use_barrel, false, gen_bsll) - if (!dc->rd) - return; +DO_TYPEBI_CFG(bsrai, use_barrel, false, tcg_gen_sari_i32) +DO_TYPEBI_CFG(bsrli, use_barrel, false, tcg_gen_shri_i32) +DO_TYPEBI_CFG(bslli, use_barrel, false, tcg_gen_shli_i32) + +DO_TYPEBI_CFG(bsefi, use_barrel, false, gen_bsefi) +DO_TYPEBI_CFG(bsifi, use_barrel, false, gen_bsifi) - if (not) { - tcg_gen_andc_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - } else - tcg_gen_and_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); +static void gen_clz(TCGv_i32 out, TCGv_i32 ina) +{ + tcg_gen_clzi_i32(out, ina, 32); } -static void dec_or(DisasContext *dc) +DO_TYPEA0_CFG(clz, use_pcmp_instr, false, gen_clz) + +static void gen_cmp(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - if (!dc->type_b && (dc->imm & (1 << 10))) { - dec_pattern(dc); - return; - } + TCGv_i32 lt = tcg_temp_new_i32(); - 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_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_setcond_i32(TCG_COND_LT, lt, inb, ina); + tcg_gen_sub_i32(out, inb, ina); + tcg_gen_deposit_i32(out, out, lt, 31, 1); + tcg_temp_free_i32(lt); } -static void dec_xor(DisasContext *dc) +static void gen_cmpu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - if (!dc->type_b && (dc->imm & (1 << 10))) { - dec_pattern(dc); - return; - } + TCGv_i32 lt = tcg_temp_new_i32(); - LOG_DIS("xor r%d\n", dc->rd); - if (dc->rd) - tcg_gen_xor_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); + tcg_gen_setcond_i32(TCG_COND_LTU, lt, inb, ina); + tcg_gen_sub_i32(out, inb, ina); + tcg_gen_deposit_i32(out, out, lt, 31, 1); + tcg_temp_free_i32(lt); } -static inline void msr_read(DisasContext *dc, TCGv_i32 d) +DO_TYPEA(cmp, false, gen_cmp) +DO_TYPEA(cmpu, false, gen_cmpu) + +ENV_WRAPPER3(gen_fadd, gen_helper_fadd) +ENV_WRAPPER3(gen_frsub, gen_helper_frsub) +ENV_WRAPPER3(gen_fmul, gen_helper_fmul) +ENV_WRAPPER3(gen_fdiv, gen_helper_fdiv) +ENV_WRAPPER3(gen_fcmp_un, gen_helper_fcmp_un) +ENV_WRAPPER3(gen_fcmp_lt, gen_helper_fcmp_lt) +ENV_WRAPPER3(gen_fcmp_eq, gen_helper_fcmp_eq) +ENV_WRAPPER3(gen_fcmp_le, gen_helper_fcmp_le) +ENV_WRAPPER3(gen_fcmp_gt, gen_helper_fcmp_gt) +ENV_WRAPPER3(gen_fcmp_ne, gen_helper_fcmp_ne) +ENV_WRAPPER3(gen_fcmp_ge, gen_helper_fcmp_ge) + +DO_TYPEA_CFG(fadd, use_fpu, true, gen_fadd) +DO_TYPEA_CFG(frsub, use_fpu, true, gen_frsub) +DO_TYPEA_CFG(fmul, use_fpu, true, gen_fmul) +DO_TYPEA_CFG(fdiv, use_fpu, true, gen_fdiv) +DO_TYPEA_CFG(fcmp_un, use_fpu, true, gen_fcmp_un) +DO_TYPEA_CFG(fcmp_lt, use_fpu, true, gen_fcmp_lt) +DO_TYPEA_CFG(fcmp_eq, use_fpu, true, gen_fcmp_eq) +DO_TYPEA_CFG(fcmp_le, use_fpu, true, gen_fcmp_le) +DO_TYPEA_CFG(fcmp_gt, use_fpu, true, gen_fcmp_gt) +DO_TYPEA_CFG(fcmp_ne, use_fpu, true, gen_fcmp_ne) +DO_TYPEA_CFG(fcmp_ge, use_fpu, true, gen_fcmp_ge) + +ENV_WRAPPER2(gen_flt, gen_helper_flt) +ENV_WRAPPER2(gen_fint, gen_helper_fint) +ENV_WRAPPER2(gen_fsqrt, gen_helper_fsqrt) + +DO_TYPEA0_CFG(flt, use_fpu >= 2, true, gen_flt) +DO_TYPEA0_CFG(fint, use_fpu >= 2, true, gen_fint) +DO_TYPEA0_CFG(fsqrt, use_fpu >= 2, true, gen_fsqrt) + +/* Does not use ENV_WRAPPER3, because arguments are swapped as well. */ +static void gen_idiv(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - tcg_gen_extrl_i64_i32(d, cpu_SR[SR_MSR]); + gen_helper_divs(out, cpu_env, inb, ina); } -static inline void msr_write(DisasContext *dc, TCGv_i32 v) +static void gen_idivu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - TCGv_i64 t; + gen_helper_divu(out, cpu_env, inb, ina); +} - t = tcg_temp_new_i64(); - dc->cpustate_changed = 1; - /* PVR bit is not writable. */ - tcg_gen_extu_i32_i64(t, v); - tcg_gen_andi_i64(t, t, ~MSR_PVR); - tcg_gen_andi_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], MSR_PVR); - tcg_gen_or_i64(cpu_SR[SR_MSR], cpu_SR[SR_MSR], t); - tcg_temp_free_i64(t); -} - -static void dec_msr(DisasContext *dc) -{ - CPUState *cs = CPU(dc->cpu); - TCGv_i32 t0, t1; - unsigned int sr, rn; - bool to, clrset, extended = false; - - sr = extract32(dc->imm, 0, 14); - to = extract32(dc->imm, 14, 1); - clrset = extract32(dc->imm, 15, 1) == 0; - dc->type_b = 1; - if (to) { - dc->cpustate_changed = 1; - } +DO_TYPEA_CFG(idiv, use_div, true, gen_idiv) +DO_TYPEA_CFG(idivu, use_div, true, gen_idivu) - /* Extended MSRs are only available if addr_size > 32. */ - if (dc->cpu->cfg.addr_size > 32) { - /* The E-bit is encoded differently for To/From MSR. */ - static const unsigned int e_bit[] = { 19, 24 }; +static bool trans_imm(DisasContext *dc, arg_imm *arg) +{ + dc->ext_imm = arg->imm << 16; + tcg_gen_movi_i32(cpu_imm, dc->ext_imm); + dc->tb_flags_to_set = IMM_FLAG; + return true; +} - extended = extract32(dc->imm, e_bit[to], 1); - } +static void gen_mulh(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_muls2_i32(tmp, out, ina, inb); + tcg_temp_free_i32(tmp); +} - /* msrclr and msrset. */ - if (clrset) { - bool clr = extract32(dc->ir, 16, 1); +static void gen_mulhu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_mulu2_i32(tmp, out, ina, inb); + tcg_temp_free_i32(tmp); +} - LOG_DIS("msr%s r%d imm=%x\n", clr ? "clr" : "set", - dc->rd, dc->imm); +static void gen_mulhsu(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_mulsu2_i32(tmp, out, ina, inb); + tcg_temp_free_i32(tmp); +} - if (!dc->cpu->cfg.use_msr_instr) { - /* nop??? */ - return; - } +DO_TYPEA_CFG(mul, use_hw_mul, false, tcg_gen_mul_i32) +DO_TYPEA_CFG(mulh, use_hw_mul >= 2, false, gen_mulh) +DO_TYPEA_CFG(mulhu, use_hw_mul >= 2, false, gen_mulhu) +DO_TYPEA_CFG(mulhsu, use_hw_mul >= 2, false, gen_mulhsu) +DO_TYPEBI_CFG(muli, use_hw_mul, false, tcg_gen_muli_i32) - if (trap_userspace(dc, dc->imm != 4 && dc->imm != 0)) { - return; - } +DO_TYPEA(or, false, tcg_gen_or_i32) +DO_TYPEBI(ori, false, tcg_gen_ori_i32) - if (dc->rd) - msr_read(dc, cpu_R[dc->rd]); - - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - msr_read(dc, t0); - tcg_gen_mov_i32(t1, *(dec_alu_op_b(dc))); - - if (clr) { - tcg_gen_not_i32(t1, t1); - tcg_gen_and_i32(t0, t0, t1); - } else - tcg_gen_or_i32(t0, t0, t1); - msr_write(dc, t0); - tcg_temp_free_i32(t0); - tcg_temp_free_i32(t1); - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc + 4); - dc->is_jmp = DISAS_UPDATE; - return; - } +static void gen_pcmpeq(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + tcg_gen_setcond_i32(TCG_COND_EQ, out, ina, inb); +} - if (trap_userspace(dc, to)) { - return; - } +static void gen_pcmpne(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + tcg_gen_setcond_i32(TCG_COND_NE, out, ina, inb); +} -#if !defined(CONFIG_USER_ONLY) - /* Catch read/writes to the mmu block. */ - if ((sr & ~0xff) == 0x1000) { - TCGv_i32 tmp_ext = tcg_const_i32(extended); - TCGv_i32 tmp_sr; +DO_TYPEA_CFG(pcmpbf, use_pcmp_instr, false, gen_helper_pcmpbf) +DO_TYPEA_CFG(pcmpeq, use_pcmp_instr, false, gen_pcmpeq) +DO_TYPEA_CFG(pcmpne, use_pcmp_instr, false, gen_pcmpne) - sr &= 7; - tmp_sr = tcg_const_i32(sr); - 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, tmp_ext, tmp_sr, cpu_R[dc->ra]); - } else { - gen_helper_mmu_read(cpu_R[dc->rd], cpu_env, tmp_ext, tmp_sr); - } - tcg_temp_free_i32(tmp_sr); - tcg_temp_free_i32(tmp_ext); - return; - } -#endif +/* No input carry, but output carry. */ +static void gen_rsub(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_msr_c, inb, ina); + tcg_gen_sub_i32(out, inb, ina); +} - 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 SR_EAR: - case SR_ESR: - case SR_FSR: - tcg_gen_extu_i32_i64(cpu_SR[sr], cpu_R[dc->ra]); - break; - case 0x800: - tcg_gen_st_i32(cpu_R[dc->ra], - cpu_env, offsetof(CPUMBState, slr)); - break; - case 0x802: - tcg_gen_st_i32(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_i32(cpu_R[dc->rd], dc->pc); - break; - case 1: - msr_read(dc, cpu_R[dc->rd]); - break; - case SR_EAR: - if (extended) { - tcg_gen_extrh_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); - break; - } - case SR_ESR: - case SR_FSR: - case SR_BTR: - case SR_EDR: - tcg_gen_extrl_i64_i32(cpu_R[dc->rd], cpu_SR[sr]); - break; - case 0x800: - tcg_gen_ld_i32(cpu_R[dc->rd], - cpu_env, offsetof(CPUMBState, slr)); - break; - case 0x802: - tcg_gen_ld_i32(cpu_R[dc->rd], - cpu_env, offsetof(CPUMBState, shr)); - break; - case 0x2000 ... 0x200c: - rn = sr & 0xf; - tcg_gen_ld_i32(cpu_R[dc->rd], - cpu_env, offsetof(CPUMBState, pvr.regs[rn])); - break; - default: - cpu_abort(cs, "unknown mfs reg %x\n", sr); - break; - } - } +/* Input and output carry. */ +static void gen_rsubc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 tmp = tcg_temp_new_i32(); - if (dc->rd == 0) { - tcg_gen_movi_i32(cpu_R[0], 0); - } + tcg_gen_not_i32(tmp, ina); + tcg_gen_add2_i32(tmp, cpu_msr_c, tmp, zero, cpu_msr_c, zero); + tcg_gen_add2_i32(out, cpu_msr_c, tmp, cpu_msr_c, inb, zero); + + tcg_temp_free_i32(zero); + tcg_temp_free_i32(tmp); } -/* Multiplier unit. */ -static void dec_mul(DisasContext *dc) +/* No input or output carry. */ +static void gen_rsubk(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - TCGv_i32 tmp; - unsigned int subcode; + tcg_gen_sub_i32(out, inb, ina); +} - if (trap_illegal(dc, !dc->cpu->cfg.use_hw_mul)) { - return; - } +/* Input carry, no output carry. */ +static void gen_rsubkc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) +{ + TCGv_i32 nota = tcg_temp_new_i32(); - subcode = dc->imm & 3; + tcg_gen_not_i32(nota, ina); + tcg_gen_add_i32(out, inb, nota); + tcg_gen_add_i32(out, out, cpu_msr_c); - if (dc->type_b) { - LOG_DIS("muli r%d r%d %x\n", dc->rd, dc->ra, dc->imm); - tcg_gen_mul_i32(cpu_R[dc->rd], cpu_R[dc->ra], *(dec_alu_op_b(dc))); - return; - } + tcg_temp_free_i32(nota); +} - /* mulh, mulhsu and mulhu are not available if C_USE_HW_MUL is < 2. */ - if (subcode >= 1 && subcode <= 3 && dc->cpu->cfg.use_hw_mul < 2) { - /* nop??? */ - } +DO_TYPEA(rsub, true, gen_rsub) +DO_TYPEA(rsubc, true, gen_rsubc) +DO_TYPEA(rsubk, false, gen_rsubk) +DO_TYPEA(rsubkc, true, gen_rsubkc) + +DO_TYPEBV(rsubi, true, gen_rsub) +DO_TYPEBV(rsubic, true, gen_rsubc) +DO_TYPEBV(rsubik, false, gen_rsubk) +DO_TYPEBV(rsubikc, true, gen_rsubkc) + +DO_TYPEA0(sext8, false, tcg_gen_ext8s_i32) +DO_TYPEA0(sext16, false, tcg_gen_ext16s_i32) + +static void gen_sra(TCGv_i32 out, TCGv_i32 ina) +{ + tcg_gen_andi_i32(cpu_msr_c, ina, 1); + tcg_gen_sari_i32(out, ina, 1); +} + +static void gen_src(TCGv_i32 out, TCGv_i32 ina) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_mov_i32(tmp, cpu_msr_c); + tcg_gen_andi_i32(cpu_msr_c, ina, 1); + tcg_gen_extract2_i32(out, ina, tmp, 1); - tmp = tcg_temp_new_i32(); - switch (subcode) { - case 0: - LOG_DIS("mul r%d r%d r%d\n", dc->rd, dc->ra, dc->rb); - tcg_gen_mul_i32(cpu_R[dc->rd], 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); - tcg_gen_muls2_i32(tmp, 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); - tcg_gen_mulsu2_i32(tmp, 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); - tcg_gen_mulu2_i32(tmp, 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; - } tcg_temp_free_i32(tmp); } -/* Div unit. */ -static void dec_div(DisasContext *dc) +static void gen_srl(TCGv_i32 out, TCGv_i32 ina) { - unsigned int u; + tcg_gen_andi_i32(cpu_msr_c, ina, 1); + tcg_gen_shri_i32(out, ina, 1); +} - u = dc->imm & 2; - LOG_DIS("div\n"); +DO_TYPEA0(sra, false, gen_sra) +DO_TYPEA0(src, false, gen_src) +DO_TYPEA0(srl, false, gen_srl) - if (trap_illegal(dc, !dc->cpu->cfg.use_div)) { - return; - } +static void gen_swaph(TCGv_i32 out, TCGv_i32 ina) +{ + tcg_gen_rotri_i32(out, ina, 16); +} + +DO_TYPEA0(swapb, false, tcg_gen_bswap32_i32) +DO_TYPEA0(swaph, false, gen_swaph) - 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_i32(cpu_R[dc->rd], 0); +static bool trans_wdic(DisasContext *dc, arg_wdic *a) +{ + /* Cache operations are nops: only check for supervisor mode. */ + trap_userspace(dc, true); + return true; } -static void dec_barrel(DisasContext *dc) +DO_TYPEA(xor, false, tcg_gen_xor_i32) +DO_TYPEBI(xori, false, tcg_gen_xori_i32) + +static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) { - TCGv_i32 t0; - unsigned int imm_w, imm_s; - bool s, t, e = false, i = false; + TCGv ret = tcg_temp_new(); - if (trap_illegal(dc, !dc->cpu->cfg.use_barrel)) { - return; + /* If any of the regs is r0, set t to the value of the other reg. */ + if (ra && rb) { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_add_i32(tmp, cpu_R[ra], cpu_R[rb]); + tcg_gen_extu_i32_tl(ret, tmp); + tcg_temp_free_i32(tmp); + } else if (ra) { + tcg_gen_extu_i32_tl(ret, cpu_R[ra]); + } else if (rb) { + tcg_gen_extu_i32_tl(ret, cpu_R[rb]); + } else { + tcg_gen_movi_tl(ret, 0); } - if (dc->type_b) { - /* Insert and extract are only available in immediate mode. */ - i = extract32(dc->imm, 15, 1); - e = extract32(dc->imm, 14, 1); + if ((ra == 1 || rb == 1) && dc->cpu->cfg.stackprot) { + gen_helper_stackprot(cpu_env, ret); } - s = extract32(dc->imm, 10, 1); - t = extract32(dc->imm, 9, 1); - imm_w = extract32(dc->imm, 6, 5); - imm_s = extract32(dc->imm, 0, 5); + return ret; +} - LOG_DIS("bs%s%s%s r%d r%d r%d\n", - e ? "e" : "", - s ? "l" : "r", t ? "a" : "l", dc->rd, dc->ra, dc->rb); +static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) +{ + TCGv ret = tcg_temp_new(); - if (e) { - if (imm_w + imm_s > 32 || imm_w == 0) { - /* These inputs have an undefined behavior. */ - qemu_log_mask(LOG_GUEST_ERROR, "bsefi: Bad input w=%d s=%d\n", - imm_w, imm_s); - } else { - tcg_gen_extract_i32(cpu_R[dc->rd], cpu_R[dc->ra], imm_s, imm_w); - } - } else if (i) { - int width = imm_w - imm_s + 1; + /* If any of the regs is r0, set t to the value of the other reg. */ + if (ra) { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_addi_i32(tmp, cpu_R[ra], imm); + tcg_gen_extu_i32_tl(ret, tmp); + tcg_temp_free_i32(tmp); + } else { + tcg_gen_movi_tl(ret, (uint32_t)imm); + } - if (imm_w < imm_s) { - /* These inputs have an undefined behavior. */ - qemu_log_mask(LOG_GUEST_ERROR, "bsifi: Bad input w=%d s=%d\n", - imm_w, imm_s); + if (ra == 1 && dc->cpu->cfg.stackprot) { + gen_helper_stackprot(cpu_env, ret); + } + return ret; +} + +#ifndef CONFIG_USER_ONLY +static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) +{ + int addr_size = dc->cpu->cfg.addr_size; + TCGv ret = tcg_temp_new(); + + if (addr_size == 32 || ra == 0) { + if (rb) { + tcg_gen_extu_i32_tl(ret, cpu_R[rb]); } else { - tcg_gen_deposit_i32(cpu_R[dc->rd], cpu_R[dc->rd], cpu_R[dc->ra], - imm_s, width); + tcg_gen_movi_tl(ret, 0); } } else { - t0 = tcg_temp_new_i32(); - - tcg_gen_mov_i32(t0, *(dec_alu_op_b(dc))); - tcg_gen_andi_i32(t0, t0, 31); - - if (s) { - tcg_gen_shl_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); + if (rb) { + tcg_gen_concat_i32_i64(ret, cpu_R[rb], cpu_R[ra]); } else { - if (t) { - tcg_gen_sar_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); - } else { - tcg_gen_shr_i32(cpu_R[dc->rd], cpu_R[dc->ra], t0); - } + tcg_gen_extu_i32_tl(ret, cpu_R[ra]); + tcg_gen_shli_tl(ret, ret, 32); + } + if (addr_size < 64) { + /* Mask off out of range bits. */ + tcg_gen_andi_i64(ret, ret, MAKE_64BIT_MASK(0, addr_size)); } - tcg_temp_free_i32(t0); } + return ret; } +#endif -static void dec_bit(DisasContext *dc) +static void record_unaligned_ess(DisasContext *dc, int rd, + MemOp size, bool store) { - CPUState *cs = CPU(dc->cpu); - TCGv_i32 t0; - unsigned int op; + uint32_t iflags = tcg_get_insn_start_param(dc->insn_start, 1); - op = dc->ir & ((1 << 9) - 1); - switch (op) { - case 0x21: - /* src. */ - t0 = tcg_temp_new_i32(); + iflags |= ESR_ESS_FLAG; + iflags |= rd << 5; + iflags |= store * ESR_S; + iflags |= (size == MO_32) * ESR_W; - LOG_DIS("src r%d r%d\n", dc->rd, dc->ra); - tcg_gen_extrl_i64_i32(t0, cpu_SR[SR_MSR]); - tcg_gen_andi_i32(t0, t0, MSR_CC); - write_carry(dc, cpu_R[dc->ra]); - if (dc->rd) { - tcg_gen_shri_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); - tcg_gen_or_i32(cpu_R[dc->rd], cpu_R[dc->rd], t0); - } - tcg_temp_free_i32(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_i32(cpu_R[dc->rd], cpu_R[dc->ra], 1); - else - tcg_gen_sari_i32(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); - trap_userspace(dc, true); - break; - case 0x68: - /* wic. */ - LOG_DIS("wic r%d\n", dc->ra); - trap_userspace(dc, true); - break; - case 0xe0: - if (trap_illegal(dc, !dc->cpu->cfg.use_pcmp_instr)) { - return; - } - if (dc->cpu->cfg.use_pcmp_instr) { - tcg_gen_clzi_i32(cpu_R[dc->rd], cpu_R[dc->ra], 32); - } - 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; - } + tcg_set_insn_start_param(dc->insn_start, 1, iflags); } -static inline void sync_jmpstate(DisasContext *dc) +static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, + int mem_index, bool rev) { - if (dc->jmp == JMP_DIRECT || dc->jmp == JMP_DIRECT_CC) { - if (dc->jmp == JMP_DIRECT) { - tcg_gen_movi_i32(env_btaken, 1); + MemOp size = mop & MO_SIZE; + + /* + * 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) { + if (size > MO_8) { + mop ^= MO_BSWAP; + } + if (size < MO_32) { + tcg_gen_xori_tl(addr, addr, 3 - size); } - dc->jmp = JMP_INDIRECT; - tcg_gen_movi_i64(env_btarget, dc->jmp_pc); } + + if (size > MO_8 && + (dc->tb_flags & MSR_EE) && + dc->cpu->cfg.unaligned_exceptions) { + record_unaligned_ess(dc, rd, size, false); + mop |= MO_ALIGN; + } + + tcg_gen_qemu_ld_i32(reg_for_write(dc, rd), addr, mem_index, mop); + + tcg_temp_free(addr); + return true; } -static void dec_imm(DisasContext *dc) +static bool trans_lbu(DisasContext *dc, arg_typea *arg) { - LOG_DIS("imm %x\n", dc->imm << 16); - tcg_gen_movi_i32(env_imm, (dc->imm << 16)); - dc->tb_flags |= IMM_FLAG; - dc->clear_imm = 0; + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_UB, dc->mem_index, false); } -static inline void compute_ldst_addr(DisasContext *dc, bool ea, TCGv t) +static bool trans_lbur(DisasContext *dc, arg_typea *arg) { - bool extimm = dc->tb_flags & IMM_FLAG; - /* Should be set to true if r1 is used by loadstores. */ - bool stackprot = false; - TCGv_i32 t32; + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_UB, dc->mem_index, true); +} - /* All load/stores use ra. */ - if (dc->ra == 1 && dc->cpu->cfg.stackprot) { - stackprot = true; +static bool trans_lbuea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_UB, MMU_NOMMU_IDX, false); +#endif +} - /* Treat the common cases first. */ - if (!dc->type_b) { - if (ea) { - int addr_size = dc->cpu->cfg.addr_size; +static bool trans_lbui(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_load(dc, arg->rd, addr, MO_UB, dc->mem_index, false); +} - if (addr_size == 32) { - tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); - return; - } +static bool trans_lhu(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); +} - tcg_gen_concat_i32_i64(t, cpu_R[dc->rb], cpu_R[dc->ra]); - if (addr_size < 64) { - /* Mask off out of range bits. */ - tcg_gen_andi_i64(t, t, MAKE_64BIT_MASK(0, addr_size)); - } - return; - } +static bool trans_lhur(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, true); +} - /* If any of the regs is r0, set t to the value of the other reg. */ - if (dc->ra == 0) { - tcg_gen_extu_i32_tl(t, cpu_R[dc->rb]); - return; - } else if (dc->rb == 0) { - tcg_gen_extu_i32_tl(t, cpu_R[dc->ra]); - return; - } +static bool trans_lhuea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; + } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUW, MMU_NOMMU_IDX, false); +#endif +} - if (dc->rb == 1 && dc->cpu->cfg.stackprot) { - stackprot = true; - } +static bool trans_lhui(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_load(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); +} - t32 = tcg_temp_new_i32(); - tcg_gen_add_i32(t32, cpu_R[dc->ra], cpu_R[dc->rb]); - tcg_gen_extu_i32_tl(t, t32); - tcg_temp_free_i32(t32); +static bool trans_lw(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); +} - if (stackprot) { - gen_helper_stackprot(cpu_env, t); - } - return; - } - /* Immediate. */ - t32 = tcg_temp_new_i32(); - if (!extimm) { - tcg_gen_addi_i32(t32, cpu_R[dc->ra], (int16_t)dc->imm); - } else { - tcg_gen_add_i32(t32, cpu_R[dc->ra], *(dec_alu_op_b(dc))); - } - tcg_gen_extu_i32_tl(t, t32); - tcg_temp_free_i32(t32); +static bool trans_lwr(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, true); +} - if (stackprot) { - gen_helper_stackprot(cpu_env, t); +static bool trans_lwea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } - return; +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_load(dc, arg->rd, addr, MO_TEUL, MMU_NOMMU_IDX, false); +#endif } -static void dec_load(DisasContext *dc) +static bool trans_lwi(DisasContext *dc, arg_typeb *arg) { - TCGv_i32 v; - TCGv addr; - unsigned int size; - bool rev = false, ex = false, ea = false; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - MemOp mop; + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_load(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); +} - mop = dc->opcode & 3; - size = 1 << mop; - if (!dc->type_b) { - ea = extract32(dc->ir, 7, 1); - rev = extract32(dc->ir, 9, 1); - ex = extract32(dc->ir, 10, 1); - } - mop |= MO_TE; - if (rev) { - mop ^= MO_BSWAP; - } +static bool trans_lwx(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); - if (trap_illegal(dc, size > 4)) { - return; - } + /* lwx does not throw unaligned access errors, so force alignment */ + tcg_gen_andi_tl(addr, addr, ~3); - if (trap_userspace(dc, ea)) { - return; + tcg_gen_qemu_ld_i32(cpu_res_val, addr, dc->mem_index, MO_TEUL); + tcg_gen_mov_tl(cpu_res_addr, addr); + tcg_temp_free(addr); + + if (arg->rd) { + tcg_gen_mov_i32(cpu_R[arg->rd], cpu_res_val); } - LOG_DIS("l%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : "", - ea ? "ea" : ""); + /* No support for AXI exclusive so always clear C */ + tcg_gen_movi_i32(cpu_msr_c, 0); + return true; +} - t_sync_flags(dc); - addr = tcg_temp_new(); - compute_ldst_addr(dc, ea, addr); - /* Extended addressing bypasses the MMU. */ - mem_index = ea ? MMU_NOMMU_IDX : mem_index; +static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, + int mem_index, bool rev) +{ + MemOp size = mop & MO_SIZE; /* * When doing reverse accesses we need to do two things. @@ -958,925 +885,1025 @@ static void dec_load(DisasContext *dc) * 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: - { - tcg_gen_xori_tl(addr, addr, 3); - break; - } - - case 2: - /* 00 -> 10 - 10 -> 00. */ - tcg_gen_xori_tl(addr, addr, 2); - break; - default: - cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); - break; + if (rev) { + if (size > MO_8) { + mop ^= MO_BSWAP; + } + if (size < MO_32) { + tcg_gen_xori_tl(addr, addr, 3 - size); } } - /* lwx does not throw unaligned access errors, so force alignment */ - if (ex) { - tcg_gen_andi_tl(addr, addr, ~3); + if (size > MO_8 && + (dc->tb_flags & MSR_EE) && + dc->cpu->cfg.unaligned_exceptions) { + record_unaligned_ess(dc, rd, size, true); + mop |= MO_ALIGN; } - /* If we get a fault on a dslot, the jmpstate better be in sync. */ - sync_jmpstate(dc); + tcg_gen_qemu_st_i32(reg_for_read(dc, rd), addr, mem_index, mop); - /* 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_i32(); - tcg_gen_qemu_ld_i32(v, addr, mem_index, mop); + tcg_temp_free(addr); + return true; +} - if (dc->cpu->cfg.unaligned_exceptions && size > 1) { - TCGv_i32 t0 = tcg_const_i32(0); - TCGv_i32 treg = tcg_const_i32(dc->rd); - TCGv_i32 tsize = tcg_const_i32(size - 1); +static bool trans_sb(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_UB, dc->mem_index, false); +} - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); - gen_helper_memalign(cpu_env, addr, treg, t0, tsize); +static bool trans_sbr(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_UB, dc->mem_index, true); +} - tcg_temp_free_i32(t0); - tcg_temp_free_i32(treg); - tcg_temp_free_i32(tsize); +static bool trans_sbea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_UB, MMU_NOMMU_IDX, false); +#endif +} - if (ex) { - tcg_gen_mov_tl(env_res_addr, addr); - tcg_gen_mov_i32(env_res_val, v); - } - if (dc->rd) { - tcg_gen_mov_i32(cpu_R[dc->rd], v); - } - tcg_temp_free_i32(v); +static bool trans_sbi(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_store(dc, arg->rd, addr, MO_UB, dc->mem_index, false); +} - if (ex) { /* lwx */ - /* no support for AXI exclusive so always clear C */ - write_carryi(dc, 0); +static bool trans_sh(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); +} + +static bool trans_shr(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, true); +} + +static bool trans_shea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUW, MMU_NOMMU_IDX, false); +#endif +} - tcg_temp_free(addr); +static bool trans_shi(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_store(dc, arg->rd, addr, MO_TEUW, dc->mem_index, false); } -static void dec_store(DisasContext *dc) +static bool trans_sw(DisasContext *dc, arg_typea *arg) { - TCGv addr; - TCGLabel *swx_skip = NULL; - unsigned int size; - bool rev = false, ex = false, ea = false; - int mem_index = cpu_mmu_index(&dc->cpu->env, false); - MemOp mop; + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); +} - mop = dc->opcode & 3; - size = 1 << mop; - if (!dc->type_b) { - ea = extract32(dc->ir, 7, 1); - rev = extract32(dc->ir, 9, 1); - ex = extract32(dc->ir, 10, 1); - } - mop |= MO_TE; - if (rev) { - mop ^= MO_BSWAP; - } +static bool trans_swr(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, true); +} - if (trap_illegal(dc, size > 4)) { - return; +static bool trans_swea(DisasContext *dc, arg_typea *arg) +{ + if (trap_userspace(dc, true)) { + return true; } +#ifdef CONFIG_USER_ONLY + return true; +#else + TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + return do_store(dc, arg->rd, addr, MO_TEUL, MMU_NOMMU_IDX, false); +#endif +} + +static bool trans_swi(DisasContext *dc, arg_typeb *arg) +{ + TCGv addr = compute_ldst_addr_typeb(dc, arg->ra, arg->imm); + return do_store(dc, arg->rd, addr, MO_TEUL, dc->mem_index, false); +} - trap_userspace(dc, ea); +static bool trans_swx(DisasContext *dc, arg_typea *arg) +{ + TCGv addr = compute_ldst_addr_typea(dc, arg->ra, arg->rb); + TCGLabel *swx_done = gen_new_label(); + TCGLabel *swx_fail = gen_new_label(); + TCGv_i32 tval; - LOG_DIS("s%d%s%s%s%s\n", size, dc->type_b ? "i" : "", rev ? "r" : "", - ex ? "x" : "", - ea ? "ea" : ""); - t_sync_flags(dc); - /* If we get a fault on a dslot, the jmpstate better be in sync. */ - sync_jmpstate(dc); - /* SWX needs a temp_local. */ - addr = ex ? tcg_temp_local_new() : tcg_temp_new(); - compute_ldst_addr(dc, ea, addr); - /* Extended addressing bypasses the MMU. */ - mem_index = ea ? MMU_NOMMU_IDX : mem_index; + /* swx does not throw unaligned access errors, so force alignment */ + tcg_gen_andi_tl(addr, addr, ~3); - if (ex) { /* swx */ - TCGv_i32 tval; + /* + * Compare the address vs the one we used during lwx. + * On mismatch, the operation fails. On match, addr dies at the + * branch, but we know we can use the equal version in the global. + * In either case, addr is no longer needed. + */ + tcg_gen_brcond_tl(TCG_COND_NE, cpu_res_addr, addr, swx_fail); + tcg_temp_free(addr); - /* swx does not throw unaligned access errors, so force alignment */ - tcg_gen_andi_tl(addr, addr, ~3); + /* + * Compare the value loaded during lwx with current contents of + * the reserved location. + */ + tval = tcg_temp_new_i32(); - write_carryi(dc, 1); - swx_skip = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_NE, env_res_addr, addr, swx_skip); + tcg_gen_atomic_cmpxchg_i32(tval, cpu_res_addr, cpu_res_val, + reg_for_write(dc, arg->rd), + dc->mem_index, MO_TEUL); - /* - * Compare the value loaded at lwx with current contents of - * the reserved location. - */ - tval = tcg_temp_new_i32(); + tcg_gen_brcond_i32(TCG_COND_NE, cpu_res_val, tval, swx_fail); + tcg_temp_free_i32(tval); - tcg_gen_atomic_cmpxchg_i32(tval, addr, env_res_val, - cpu_R[dc->rd], mem_index, - mop); + /* Success */ + tcg_gen_movi_i32(cpu_msr_c, 0); + tcg_gen_br(swx_done); - tcg_gen_brcond_i32(TCG_COND_NE, env_res_val, tval, swx_skip); - write_carryi(dc, 0); - tcg_temp_free_i32(tval); - } + /* Failure */ + gen_set_label(swx_fail); + tcg_gen_movi_i32(cpu_msr_c, 1); - if (rev && size != 4) { - /* Endian reverse the address. t is addr. */ - switch (size) { - case 1: - { - tcg_gen_xori_tl(addr, addr, 3); - break; - } + gen_set_label(swx_done); - case 2: - /* 00 -> 10 - 10 -> 00. */ - /* Force addr into the temp. */ - tcg_gen_xori_tl(addr, addr, 2); - break; - default: - cpu_abort(CPU(dc->cpu), "Invalid reverse size\n"); - break; - } - } + /* + * Prevent the saved address from working again without another ldx. + * Akin to the pseudocode setting reservation = 0. + */ + tcg_gen_movi_tl(cpu_res_addr, -1); + return true; +} - if (!ex) { - tcg_gen_qemu_st_i32(cpu_R[dc->rd], addr, mem_index, mop); +static void setup_dslot(DisasContext *dc, bool type_b) +{ + dc->tb_flags_to_set |= D_FLAG; + if (type_b && (dc->tb_flags & IMM_FLAG)) { + dc->tb_flags_to_set |= BIMM_FLAG; } +} - /* Verify alignment if needed. */ - if (dc->cpu->cfg.unaligned_exceptions && size > 1) { - TCGv_i32 t1 = tcg_const_i32(1); - TCGv_i32 treg = tcg_const_i32(dc->rd); - TCGv_i32 tsize = tcg_const_i32(size - 1); - - tcg_gen_movi_i64(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, treg, t1, tsize); +static bool do_branch(DisasContext *dc, int dest_rb, int dest_imm, + bool delay, bool abs, int link) +{ + uint32_t add_pc; - tcg_temp_free_i32(t1); - tcg_temp_free_i32(treg); - tcg_temp_free_i32(tsize); + if (delay) { + setup_dslot(dc, dest_rb < 0); } - if (ex) { - gen_set_label(swx_skip); + if (link) { + tcg_gen_movi_i32(cpu_R[link], dc->base.pc_next); } - tcg_temp_free(addr); + /* Store the branch taken destination into btarget. */ + add_pc = abs ? 0 : dc->base.pc_next; + if (dest_rb > 0) { + dc->jmp_dest = -1; + tcg_gen_addi_i32(cpu_btarget, cpu_R[dest_rb], add_pc); + } else { + dc->jmp_dest = add_pc + dest_imm; + tcg_gen_movi_i32(cpu_btarget, dc->jmp_dest); + } + dc->jmp_cond = TCG_COND_ALWAYS; + return true; } -static inline void eval_cc(DisasContext *dc, unsigned int cc, - TCGv_i32 d, TCGv_i32 a) +#define DO_BR(NAME, NAMEI, DELAY, ABS, LINK) \ + static bool trans_##NAME(DisasContext *dc, arg_typea_br *arg) \ + { return do_branch(dc, arg->rb, 0, DELAY, ABS, LINK ? arg->rd : 0); } \ + static bool trans_##NAMEI(DisasContext *dc, arg_typeb_br *arg) \ + { return do_branch(dc, -1, arg->imm, DELAY, ABS, LINK ? arg->rd : 0); } + +DO_BR(br, bri, false, false, false) +DO_BR(bra, brai, false, true, false) +DO_BR(brd, brid, true, false, false) +DO_BR(brad, braid, true, true, false) +DO_BR(brld, brlid, true, false, true) +DO_BR(brald, bralid, true, true, true) + +static bool do_bcc(DisasContext *dc, int dest_rb, int dest_imm, + TCGCond cond, int ra, bool delay) { - static const int mb_to_tcg_cc[] = { - [CC_EQ] = TCG_COND_EQ, - [CC_NE] = TCG_COND_NE, - [CC_LT] = TCG_COND_LT, - [CC_LE] = TCG_COND_LE, - [CC_GE] = TCG_COND_GE, - [CC_GT] = TCG_COND_GT, - }; + TCGv_i32 zero, next; - switch (cc) { - case CC_EQ: - case CC_NE: - case CC_LT: - case CC_LE: - case CC_GE: - case CC_GT: - tcg_gen_setcondi_i32(mb_to_tcg_cc[cc], d, a, 0); - break; - default: - cpu_abort(CPU(dc->cpu), "Unknown condition code %x.\n", cc); - break; + if (delay) { + setup_dslot(dc, dest_rb < 0); } -} -static void eval_cond_jmp(DisasContext *dc, TCGv_i64 pc_true, TCGv_i64 pc_false) -{ - TCGv_i64 tmp_btaken = tcg_temp_new_i64(); - TCGv_i64 tmp_zero = tcg_const_i64(0); + dc->jmp_cond = cond; - tcg_gen_extu_i32_i64(tmp_btaken, env_btaken); - tcg_gen_movcond_i64(TCG_COND_NE, cpu_SR[SR_PC], - tmp_btaken, tmp_zero, - pc_true, pc_false); + /* Cache the condition register in cpu_bvalue across any delay slot. */ + tcg_gen_mov_i32(cpu_bvalue, reg_for_read(dc, ra)); - tcg_temp_free_i64(tmp_btaken); - tcg_temp_free_i64(tmp_zero); + /* Store the branch taken destination into btarget. */ + if (dest_rb > 0) { + dc->jmp_dest = -1; + tcg_gen_addi_i32(cpu_btarget, cpu_R[dest_rb], dc->base.pc_next); + } else { + dc->jmp_dest = dc->base.pc_next + dest_imm; + tcg_gen_movi_i32(cpu_btarget, dc->jmp_dest); + } + + /* Compute the final destination into btarget. */ + zero = tcg_const_i32(0); + next = tcg_const_i32(dc->base.pc_next + (delay + 1) * 4); + tcg_gen_movcond_i32(dc->jmp_cond, cpu_btarget, + reg_for_read(dc, ra), zero, + cpu_btarget, next); + tcg_temp_free_i32(zero); + tcg_temp_free_i32(next); + + return true; } -static void dec_setup_dslot(DisasContext *dc) +#define DO_BCC(NAME, COND) \ + static bool trans_##NAME(DisasContext *dc, arg_typea_bc *arg) \ + { return do_bcc(dc, arg->rb, 0, COND, arg->ra, false); } \ + static bool trans_##NAME##d(DisasContext *dc, arg_typea_bc *arg) \ + { return do_bcc(dc, arg->rb, 0, COND, arg->ra, true); } \ + static bool trans_##NAME##i(DisasContext *dc, arg_typeb_bc *arg) \ + { return do_bcc(dc, -1, arg->imm, COND, arg->ra, false); } \ + static bool trans_##NAME##id(DisasContext *dc, arg_typeb_bc *arg) \ + { return do_bcc(dc, -1, arg->imm, COND, arg->ra, true); } + +DO_BCC(beq, TCG_COND_EQ) +DO_BCC(bge, TCG_COND_GE) +DO_BCC(bgt, TCG_COND_GT) +DO_BCC(ble, TCG_COND_LE) +DO_BCC(blt, TCG_COND_LT) +DO_BCC(bne, TCG_COND_NE) + +static bool trans_brk(DisasContext *dc, arg_typea_br *arg) { - TCGv_i32 tmp = tcg_const_i32(dc->type_b && (dc->tb_flags & IMM_FLAG)); - - dc->delayed_branch = 2; - dc->tb_flags |= D_FLAG; + if (trap_userspace(dc, true)) { + return true; + } + tcg_gen_mov_i32(cpu_pc, reg_for_read(dc, arg->rb)); + if (arg->rd) { + tcg_gen_movi_i32(cpu_R[arg->rd], dc->base.pc_next); + } + tcg_gen_ori_i32(cpu_msr, cpu_msr, MSR_BIP); + tcg_gen_movi_tl(cpu_res_addr, -1); - tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUMBState, bimm)); - tcg_temp_free_i32(tmp); + dc->base.is_jmp = DISAS_UPDATE; + return true; } -static void dec_bcc(DisasContext *dc) +static bool trans_brki(DisasContext *dc, arg_typeb_br *arg) { - 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); + uint32_t imm = arg->imm; - dc->delayed_branch = 1; - if (dslot) { - dec_setup_dslot(dc); + if (trap_userspace(dc, imm != 0x8 && imm != 0x18)) { + return true; } + tcg_gen_movi_i32(cpu_pc, imm); + if (arg->rd) { + tcg_gen_movi_i32(cpu_R[arg->rd], dc->base.pc_next); + } + tcg_gen_movi_tl(cpu_res_addr, -1); - if (dec_alu_op_b_is_small_imm(dc)) { - int32_t offset = (int32_t)((int16_t)dc->imm); /* sign-extend. */ +#ifdef CONFIG_USER_ONLY + switch (imm) { + case 0x8: /* syscall trap */ + gen_raise_exception_sync(dc, EXCP_SYSCALL); + break; + case 0x18: /* debug trap */ + gen_raise_exception_sync(dc, EXCP_DEBUG); + break; + default: /* eliminated with trap_userspace check */ + g_assert_not_reached(); + } +#else + uint32_t msr_to_set = 0; - tcg_gen_movi_i64(env_btarget, dc->pc + offset); - dc->jmp = JMP_DIRECT_CC; - dc->jmp_pc = dc->pc + offset; - } else { - dc->jmp = JMP_INDIRECT; - tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); - tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); - tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); + if (imm != 0x18) { + msr_to_set |= MSR_BIP; } - eval_cc(dc, cc, env_btaken, cpu_R[dc->ra]); + if (imm == 0x8 || imm == 0x18) { + /* MSR_UM and MSR_VM are in tb_flags, so we know their value. */ + msr_to_set |= (dc->tb_flags & (MSR_UM | MSR_VM)) << 1; + tcg_gen_andi_i32(cpu_msr, cpu_msr, + ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM)); + } + tcg_gen_ori_i32(cpu_msr, cpu_msr, msr_to_set); + dc->base.is_jmp = DISAS_UPDATE; +#endif + + return true; } -static void dec_br(DisasContext *dc) +static bool trans_mbar(DisasContext *dc, arg_mbar *arg) { - unsigned int dslot, link, abs, mbar; - - dslot = dc->ir & (1 << 20); - abs = dc->ir & (1 << 19); - link = dc->ir & (1 << 18); + int mbar_imm = arg->imm; - /* Memory barrier. */ - mbar = (dc->ir >> 16) & 31; - if (mbar == 2 && dc->imm == 4) { - uint16_t mbar_imm = dc->rd; + /* Data access memory barrier. */ + if ((mbar_imm & 2) == 0) { + tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); + } - LOG_DIS("mbar %d\n", mbar_imm); + /* Sleep. */ + if (mbar_imm & 16) { + TCGv_i32 tmp_1; - /* Data access memory barrier. */ - if ((mbar_imm & 2) == 0) { - tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); + if (trap_userspace(dc, true)) { + /* Sleep is a privileged instruction. */ + return true; } - /* mbar IMM & 16 decodes to sleep. */ - if (mbar_imm & 16) { - TCGv_i32 tmp_hlt = tcg_const_i32(EXCP_HLT); - TCGv_i32 tmp_1 = tcg_const_i32(1); + t_sync_flags(dc); - LOG_DIS("sleep\n"); + tmp_1 = tcg_const_i32(1); + tcg_gen_st_i32(tmp_1, cpu_env, + -offsetof(MicroBlazeCPU, env) + +offsetof(CPUState, halted)); + tcg_temp_free_i32(tmp_1); - if (trap_userspace(dc, true)) { - /* Sleep is a privileged instruction. */ - return; - } + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next + 4); - t_sync_flags(dc); - tcg_gen_st_i32(tmp_1, cpu_env, - -offsetof(MicroBlazeCPU, env) - +offsetof(CPUState, halted)); - tcg_gen_movi_i64(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; - } - /* Break the TB. */ - dc->cpustate_changed = 1; - return; + gen_raise_exception(dc, EXCP_HLT); } - LOG_DIS("br%s%s%s%s imm=%x\n", - abs ? "a" : "", link ? "l" : "", - dc->type_b ? "i" : "", dslot ? "d" : "", - dc->imm); + /* + * If !(mbar_imm & 1), this is an instruction access memory barrier + * and we need to end the TB so that we recognize self-modified + * code immediately. + * + * However, there are some data mbars that need the TB break + * (and return to main loop) to recognize interrupts right away. + * E.g. recognizing a change to an interrupt controller register. + * + * Therefore, choose to end the TB always. + */ + dc->cpustate_changed = 1; + return true; +} - dc->delayed_branch = 1; - if (dslot) { - dec_setup_dslot(dc); +static bool do_rts(DisasContext *dc, arg_typeb_bc *arg, int to_set) +{ + if (trap_userspace(dc, to_set)) { + return true; } - if (link && dc->rd) - tcg_gen_movi_i32(cpu_R[dc->rd], dc->pc); - - dc->jmp = JMP_INDIRECT; - if (abs) { - tcg_gen_movi_i32(env_btaken, 1); - tcg_gen_extu_i32_i64(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 (trap_userspace(dc, true)) { - return; - } + dc->tb_flags_to_set |= to_set; + setup_dslot(dc, true); - 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_i32(env_btaken, 1); - tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); - tcg_gen_addi_i64(env_btarget, env_btarget, dc->pc); - tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); - } - } + dc->jmp_cond = TCG_COND_ALWAYS; + dc->jmp_dest = -1; + tcg_gen_addi_i32(cpu_btarget, reg_for_read(dc, arg->ra), arg->imm); + return true; } -static inline void do_rti(DisasContext *dc) -{ - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); - tcg_gen_shri_i32(t0, t1, 1); - tcg_gen_ori_i32(t1, t1, MSR_IE); - tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); +#define DO_RTS(NAME, IFLAG) \ + static bool trans_##NAME(DisasContext *dc, arg_typeb_bc *arg) \ + { return do_rts(dc, arg, IFLAG); } - tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_i32(t1, t1, t0); - msr_write(dc, t1); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - dc->tb_flags &= ~DRTI_FLAG; +DO_RTS(rtbd, DRTB_FLAG) +DO_RTS(rtid, DRTI_FLAG) +DO_RTS(rted, DRTE_FLAG) +DO_RTS(rtsd, 0) + +static bool trans_zero(DisasContext *dc, arg_zero *arg) +{ + /* If opcode_0_illegal, trap. */ + if (dc->cpu->cfg.opcode_0_illegal) { + trap_illegal(dc, true); + return true; + } + /* + * Otherwise, this is "add r0, r0, r0". + * Continue to trans_add so that MSR[C] gets cleared. + */ + return false; } -static inline void do_rtb(DisasContext *dc) +static void msr_read(DisasContext *dc, TCGv_i32 d) { - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); - tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); - tcg_gen_andi_i32(t1, t1, ~MSR_BIP); - tcg_gen_shri_i32(t0, t1, 1); - tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); + TCGv_i32 t; - tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_i32(t1, t1, t0); - msr_write(dc, t1); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - dc->tb_flags &= ~DRTB_FLAG; + /* Replicate the cpu_msr_c boolean into the proper bit and the copy. */ + t = tcg_temp_new_i32(); + tcg_gen_muli_i32(t, cpu_msr_c, MSR_C | MSR_CC); + tcg_gen_or_i32(d, cpu_msr, t); + tcg_temp_free_i32(t); } -static inline void do_rte(DisasContext *dc) +#ifndef CONFIG_USER_ONLY +static void msr_write(DisasContext *dc, TCGv_i32 v) { - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); + dc->cpustate_changed = 1; - tcg_gen_extrl_i64_i32(t1, cpu_SR[SR_MSR]); - tcg_gen_ori_i32(t1, t1, MSR_EE); - tcg_gen_andi_i32(t1, t1, ~MSR_EIP); - tcg_gen_shri_i32(t0, t1, 1); - tcg_gen_andi_i32(t0, t0, (MSR_VM | MSR_UM)); + /* Install MSR_C. */ + tcg_gen_extract_i32(cpu_msr_c, v, 2, 1); - tcg_gen_andi_i32(t1, t1, ~(MSR_VM | MSR_UM)); - tcg_gen_or_i32(t1, t1, t0); - msr_write(dc, t1); - tcg_temp_free_i32(t1); - tcg_temp_free_i32(t0); - dc->tb_flags &= ~DRTE_FLAG; + /* Clear MSR_C and MSR_CC; MSR_PVR is not writable, and is always clear. */ + tcg_gen_andi_i32(cpu_msr, v, ~(MSR_C | MSR_CC | MSR_PVR)); } +#endif -static void dec_rts(DisasContext *dc) +static bool do_msrclrset(DisasContext *dc, arg_type_msr *arg, bool set) { - unsigned int b_bit, i_bit, e_bit; - TCGv_i64 tmp64; + uint32_t imm = arg->imm; - i_bit = dc->ir & (1 << 21); - b_bit = dc->ir & (1 << 22); - e_bit = dc->ir & (1 << 23); - - if (trap_userspace(dc, i_bit || b_bit || e_bit)) { - return; + if (trap_userspace(dc, imm != MSR_C)) { + return true; } - dec_setup_dslot(dc); + if (arg->rd) { + msr_read(dc, cpu_R[arg->rd]); + } - if (i_bit) { - LOG_DIS("rtid ir=%x\n", dc->ir); - dc->tb_flags |= DRTI_FLAG; - } else if (b_bit) { - LOG_DIS("rtbd ir=%x\n", dc->ir); - dc->tb_flags |= DRTB_FLAG; - } else if (e_bit) { - LOG_DIS("rted ir=%x\n", dc->ir); - dc->tb_flags |= DRTE_FLAG; - } else - LOG_DIS("rts ir=%x\n", dc->ir); + /* + * Handle the carry bit separately. + * This is the only bit that userspace can modify. + */ + if (imm & MSR_C) { + tcg_gen_movi_i32(cpu_msr_c, set); + } - dc->jmp = JMP_INDIRECT; - tcg_gen_movi_i32(env_btaken, 1); + /* + * MSR_C and MSR_CC set above. + * MSR_PVR is not writable, and is always clear. + */ + imm &= ~(MSR_C | MSR_CC | MSR_PVR); - tmp64 = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(env_btarget, *(dec_alu_op_b(dc))); - tcg_gen_extu_i32_i64(tmp64, cpu_R[dc->ra]); - tcg_gen_add_i64(env_btarget, env_btarget, tmp64); - tcg_gen_andi_i64(env_btarget, env_btarget, UINT32_MAX); - tcg_temp_free_i64(tmp64); + if (imm != 0) { + if (set) { + tcg_gen_ori_i32(cpu_msr, cpu_msr, imm); + } else { + tcg_gen_andi_i32(cpu_msr, cpu_msr, ~imm); + } + dc->cpustate_changed = 1; + } + return true; } -static int dec_check_fpuv2(DisasContext *dc) +static bool trans_msrclr(DisasContext *dc, arg_type_msr *arg) { - if ((dc->cpu->cfg.use_fpu != 2) && (dc->tb_flags & MSR_EE_FLAG)) { - tcg_gen_movi_i64(cpu_SR[SR_ESR], ESR_EC_FPU); - t_gen_raise_exception(dc, EXCP_HW_EXCP); - } - return (dc->cpu->cfg.use_fpu == 2) ? PVR2_USE_FPU2_MASK : 0; + return do_msrclrset(dc, arg, false); } -static void dec_fpu(DisasContext *dc) +static bool trans_msrset(DisasContext *dc, arg_type_msr *arg) { - unsigned int fpu_insn; + return do_msrclrset(dc, arg, true); +} - if (trap_illegal(dc, !dc->cpu->cfg.use_fpu)) { - return; +static bool trans_mts(DisasContext *dc, arg_mts *arg) +{ + if (trap_userspace(dc, true)) { + return true; } - 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; +#ifdef CONFIG_USER_ONLY + g_assert_not_reached(); +#else + if (arg->e && arg->rs != 0x1003) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid extended mts reg 0x%x\n", arg->rs); + return true; + } - case 1: - gen_helper_frsub(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], - cpu_R[dc->rb]); - break; + TCGv_i32 src = reg_for_read(dc, arg->ra); + switch (arg->rs) { + case SR_MSR: + msr_write(dc, src); + break; + case SR_FSR: + tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, fsr)); + break; + case 0x800: + tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, slr)); + break; + case 0x802: + tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, shr)); + break; - case 2: - gen_helper_fmul(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], - cpu_R[dc->rb]); - break; + case 0x1000: /* PID */ + case 0x1001: /* ZPR */ + case 0x1002: /* TLBX */ + case 0x1003: /* TLBLO */ + case 0x1004: /* TLBHI */ + case 0x1005: /* TLBSX */ + { + TCGv_i32 tmp_ext = tcg_const_i32(arg->e); + TCGv_i32 tmp_reg = tcg_const_i32(arg->rs & 7); + + gen_helper_mmu_write(cpu_env, tmp_ext, tmp_reg, src); + tcg_temp_free_i32(tmp_reg); + tcg_temp_free_i32(tmp_ext); + } + break; - case 3: - gen_helper_fdiv(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra], - cpu_R[dc->rb]); - break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Invalid mts reg 0x%x\n", arg->rs); + return true; + } + dc->cpustate_changed = 1; + return true; +#endif +} - 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; +static bool trans_mfs(DisasContext *dc, arg_mfs *arg) +{ + TCGv_i32 dest = reg_for_write(dc, arg->rd); - case 5: - if (!dec_check_fpuv2(dc)) { - return; + if (arg->e) { + switch (arg->rs) { + case SR_EAR: + { + TCGv_i64 t64 = tcg_temp_new_i64(); + tcg_gen_ld_i64(t64, cpu_env, offsetof(CPUMBState, ear)); + tcg_gen_extrh_i64_i32(dest, t64); + tcg_temp_free_i64(t64); } - gen_helper_flt(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra]); + return true; +#ifndef CONFIG_USER_ONLY + case 0x1003: /* TLBLO */ + /* Handled below. */ break; +#endif + case 0x2006 ... 0x2009: + /* High bits of PVR6-9 not implemented. */ + tcg_gen_movi_i32(dest, 0); + return true; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid extended mfs reg 0x%x\n", arg->rs); + return true; + } + } - case 6: - if (!dec_check_fpuv2(dc)) { - return; - } - gen_helper_fint(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra]); - break; + switch (arg->rs) { + case SR_PC: + tcg_gen_movi_i32(dest, dc->base.pc_next); + break; + case SR_MSR: + msr_read(dc, dest); + break; + case SR_EAR: + { + TCGv_i64 t64 = tcg_temp_new_i64(); + tcg_gen_ld_i64(t64, cpu_env, offsetof(CPUMBState, ear)); + tcg_gen_extrl_i64_i32(dest, t64); + tcg_temp_free_i64(t64); + } + break; + case SR_ESR: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, esr)); + break; + case SR_FSR: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, fsr)); + break; + case SR_BTR: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, btr)); + break; + case SR_EDR: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, edr)); + break; + case 0x800: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, slr)); + break; + case 0x802: + tcg_gen_ld_i32(dest, cpu_env, offsetof(CPUMBState, shr)); + break; - case 7: - if (!dec_check_fpuv2(dc)) { - return; - } - gen_helper_fsqrt(cpu_R[dc->rd], cpu_env, cpu_R[dc->ra]); - break; +#ifndef CONFIG_USER_ONLY + case 0x1000: /* PID */ + case 0x1001: /* ZPR */ + case 0x1002: /* TLBX */ + case 0x1003: /* TLBLO */ + case 0x1004: /* TLBHI */ + case 0x1005: /* TLBSX */ + { + TCGv_i32 tmp_ext = tcg_const_i32(arg->e); + TCGv_i32 tmp_reg = tcg_const_i32(arg->rs & 7); + + gen_helper_mmu_read(dest, cpu_env, tmp_ext, tmp_reg); + tcg_temp_free_i32(tmp_reg); + tcg_temp_free_i32(tmp_ext); + } + break; +#endif - 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; + case 0x2000 ... 0x200c: + tcg_gen_ld_i32(dest, cpu_env, + offsetof(CPUMBState, pvr.regs[arg->rs - 0x2000])); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "Invalid mfs reg 0x%x\n", arg->rs); + break; } + return true; } -static void dec_null(DisasContext *dc) +static void do_rti(DisasContext *dc) { - if (trap_illegal(dc, true)) { - return; - } - qemu_log_mask(LOG_GUEST_ERROR, "unknown insn pc=%x opc=%x\n", dc->pc, dc->opcode); - dc->abort_at_next_insn = 1; + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_shri_i32(tmp, cpu_msr, 1); + tcg_gen_ori_i32(cpu_msr, cpu_msr, MSR_IE); + tcg_gen_andi_i32(tmp, tmp, MSR_VM | MSR_UM); + tcg_gen_andi_i32(cpu_msr, cpu_msr, ~(MSR_VM | MSR_UM)); + tcg_gen_or_i32(cpu_msr, cpu_msr, tmp); + + tcg_temp_free_i32(tmp); + dc->tb_flags &= ~DRTI_FLAG; +} + +static void do_rtb(DisasContext *dc) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_shri_i32(tmp, cpu_msr, 1); + tcg_gen_andi_i32(cpu_msr, cpu_msr, ~(MSR_VM | MSR_UM | MSR_BIP)); + tcg_gen_andi_i32(tmp, tmp, (MSR_VM | MSR_UM)); + tcg_gen_or_i32(cpu_msr, cpu_msr, tmp); + + tcg_temp_free_i32(tmp); + dc->tb_flags &= ~DRTB_FLAG; +} + +static void do_rte(DisasContext *dc) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_shri_i32(tmp, cpu_msr, 1); + tcg_gen_ori_i32(cpu_msr, cpu_msr, MSR_EE); + tcg_gen_andi_i32(tmp, tmp, (MSR_VM | MSR_UM)); + tcg_gen_andi_i32(cpu_msr, cpu_msr, ~(MSR_VM | MSR_UM | MSR_EIP)); + tcg_gen_or_i32(cpu_msr, cpu_msr, tmp); + + tcg_temp_free_i32(tmp); + dc->tb_flags &= ~DRTE_FLAG; } /* Insns connected to FSL or AXI stream attached devices. */ -static void dec_stream(DisasContext *dc) +static bool do_get(DisasContext *dc, int rd, int rb, int imm, int ctrl) { 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 (trap_userspace(dc, true)) { - return; + return true; } t_id = tcg_temp_new_i32(); - if (dc->type_b) { - tcg_gen_movi_i32(t_id, dc->imm & 0xf); - ctrl = dc->imm >> 10; + if (rb) { + tcg_gen_andi_i32(t_id, cpu_R[rb], 0xf); } else { - tcg_gen_andi_i32(t_id, cpu_R[dc->rb], 0xf); - ctrl = dc->imm >> 5; + tcg_gen_movi_i32(t_id, imm); } t_ctrl = tcg_const_i32(ctrl); + gen_helper_get(reg_for_write(dc, rd), t_id, t_ctrl); + tcg_temp_free_i32(t_id); + tcg_temp_free_i32(t_ctrl); + return true; +} - if (dc->rd == 0) { - gen_helper_put(t_id, t_ctrl, cpu_R[dc->ra]); +static bool trans_get(DisasContext *dc, arg_get *arg) +{ + return do_get(dc, arg->rd, 0, arg->imm, arg->ctrl); +} + +static bool trans_getd(DisasContext *dc, arg_getd *arg) +{ + return do_get(dc, arg->rd, arg->rb, 0, arg->ctrl); +} + +static bool do_put(DisasContext *dc, int ra, int rb, int imm, int ctrl) +{ + TCGv_i32 t_id, t_ctrl; + + if (trap_userspace(dc, true)) { + return true; + } + + t_id = tcg_temp_new_i32(); + if (rb) { + tcg_gen_andi_i32(t_id, cpu_R[rb], 0xf); } else { - gen_helper_get(cpu_R[dc->rd], t_id, t_ctrl); + tcg_gen_movi_i32(t_id, imm); } + + t_ctrl = tcg_const_i32(ctrl); + gen_helper_put(t_id, t_ctrl, reg_for_read(dc, ra)); tcg_temp_free_i32(t_id); tcg_temp_free_i32(t_ctrl); + return true; } -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 bool trans_put(DisasContext *dc, arg_put *arg) +{ + return do_put(dc, arg->ra, 0, arg->imm, arg->ctrl); +} -static inline void decode(DisasContext *dc, uint32_t ir) +static bool trans_putd(DisasContext *dc, arg_putd *arg) { - int i; + return do_put(dc, arg->ra, arg->rb, 0, arg->ctrl); +} - dc->ir = ir; - LOG_DIS("%8.8x\t", dc->ir); +static void mb_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) +{ + DisasContext *dc = container_of(dcb, DisasContext, base); + MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); + int bound; - if (ir == 0) { - trap_illegal(dc, dc->cpu->cfg.opcode_0_illegal); - /* Don't decode nop/zero instructions any further. */ - return; - } + dc->cpu = cpu; + dc->tb_flags = dc->base.tb->flags; + dc->cpustate_changed = 0; + dc->ext_imm = dc->base.tb->cs_base; + dc->r0 = NULL; + dc->r0_set = false; + dc->mem_index = cpu_mmu_index(&cpu->env, false); + dc->jmp_cond = dc->tb_flags & D_FLAG ? TCG_COND_ALWAYS : TCG_COND_NEVER; + dc->jmp_dest = -1; + + bound = -(dc->base.pc_first | TARGET_PAGE_MASK) / 4; + dc->base.max_insns = MIN(dc->base.max_insns, bound); +} - /* bit 2 seems to indicate insn type. */ - dc->type_b = ir & (1 << 29); +static void mb_tr_tb_start(DisasContextBase *dcb, CPUState *cs) +{ +} - 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); +static void mb_tr_insn_start(DisasContextBase *dcb, CPUState *cs) +{ + DisasContext *dc = container_of(dcb, DisasContext, base); - /* 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; - } - } + tcg_gen_insn_start(dc->base.pc_next, dc->tb_flags & ~MSR_TB_MASK); + dc->insn_start = tcg_last_op(); } -/* generate intermediate code for basic block 'tb'. */ -void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int max_insns) +static bool mb_tr_breakpoint_check(DisasContextBase *dcb, CPUState *cs, + const CPUBreakpoint *bp) { + DisasContext *dc = container_of(dcb, DisasContext, base); + + gen_raise_exception_sync(dc, EXCP_DEBUG); + + /* + * 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->base.pc_next += 4; + return true; +} + +static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs) +{ + DisasContext *dc = container_of(dcb, DisasContext, base); CPUMBState *env = cs->env_ptr; - MicroBlazeCPU *cpu = env_archcpu(env); - uint32_t pc_start; - struct DisasContext ctx; - struct DisasContext *dc = &ctx; - uint32_t page_start, org_flags; - uint32_t npc; - int num_insns; - - pc_start = tb->pc; - dc->cpu = cpu; - dc->tb = tb; - org_flags = dc->synced_flags = dc->tb_flags = tb->flags; + uint32_t ir; - dc->is_jmp = DISAS_NEXT; - dc->jmp = 0; - dc->delayed_branch = !!(dc->tb_flags & D_FLAG); - if (dc->delayed_branch) { - dc->jmp = JMP_INDIRECT; + /* TODO: This should raise an exception, not terminate qemu. */ + if (dc->base.pc_next & 3) { + cpu_abort(cs, "Microblaze: unaligned PC=%x\n", + (uint32_t)dc->base.pc_next); } - dc->pc = pc_start; - dc->singlestep_enabled = cs->singlestep_enabled; - dc->cpustate_changed = 0; - dc->abort_at_next_insn = 0; - if (pc_start & 3) { - cpu_abort(cs, "Microblaze: unaligned PC=%x\n", pc_start); + dc->tb_flags_to_set = 0; + + ir = cpu_ldl_code(env, dc->base.pc_next); + if (!decode(dc, ir)) { + trap_illegal(dc, true); } - page_start = pc_start & TARGET_PAGE_MASK; - num_insns = 0; + if (dc->r0) { + tcg_temp_free_i32(dc->r0); + dc->r0 = NULL; + dc->r0_set = false; + } - gen_tb_start(tb); - do - { - tcg_gen_insn_start(dc->pc); - num_insns++; + /* Discard the imm global when its contents cannot be used. */ + if ((dc->tb_flags & ~dc->tb_flags_to_set) & IMM_FLAG) { + tcg_gen_discard_i32(cpu_imm); + } -#if SIM_COMPAT - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { - tcg_gen_movi_i64(cpu_SR[SR_PC], dc->pc); - gen_helper_debug(); - } -#endif + dc->tb_flags &= ~(IMM_FLAG | BIMM_FLAG | D_FLAG); + dc->tb_flags |= dc->tb_flags_to_set; + dc->base.pc_next += 4; - 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; + if (dc->jmp_cond != TCG_COND_NEVER && !(dc->tb_flags & D_FLAG)) { + if (dc->tb_flags & DRTI_FLAG) { + do_rti(dc); + } else if (dc->tb_flags & DRTB_FLAG) { + do_rtb(dc); + } else if (dc->tb_flags & DRTE_FLAG) { + do_rte(dc); } + dc->base.is_jmp = DISAS_JUMP; + } - /* Pretty disas. */ - LOG_DIS("%8.8x:\t", dc->pc); + /* Force an exit if the per-tb cpu state has changed. */ + if (dc->base.is_jmp == DISAS_NEXT && dc->cpustate_changed) { + dc->base.is_jmp = DISAS_UPDATE; + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); + } +} - if (num_insns == max_insns && (tb_cflags(tb) & CF_LAST_IO)) { - gen_io_start(); - } +static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) +{ + DisasContext *dc = container_of(dcb, DisasContext, base); - 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) { - TCGv_i64 tmp_pc = tcg_const_i64(dc->pc); - eval_cond_jmp(dc, env_btarget, tmp_pc); - tcg_temp_free_i64(tmp_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_i32(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 - page_start < TARGET_PAGE_SIZE) - && 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_i64(cpu_SR[SR_PC], npc); - sync_jmpstate(dc); - } else - npc = dc->jmp_pc; - } - - /* 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_i64(cpu_SR[SR_PC], npc); + if (dc->base.is_jmp == DISAS_NORETURN) { + /* We have already exited the TB. */ + return; } + t_sync_flags(dc); - if (unlikely(cs->singlestep_enabled)) { - TCGv_i32 tmp = tcg_const_i32(EXCP_DEBUG); + switch (dc->base.is_jmp) { + case DISAS_TOO_MANY: + gen_goto_tb(dc, 0, dc->base.pc_next); + return; - if (dc->is_jmp != DISAS_JUMP) { - tcg_gen_movi_i64(cpu_SR[SR_PC], npc); + case DISAS_UPDATE: + if (unlikely(cs->singlestep_enabled)) { + gen_raise_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_exit_tb(NULL, 0); } - 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(NULL, 0); - break; - case DISAS_TB_JUMP: - /* nothing more to generate */ - break; + return; + + case DISAS_JUMP: + if (dc->jmp_dest != -1 && !cs->singlestep_enabled) { + /* Direct jump. */ + tcg_gen_discard_i32(cpu_btarget); + + if (dc->jmp_cond != TCG_COND_ALWAYS) { + /* Conditional direct jump. */ + TCGLabel *taken = gen_new_label(); + TCGv_i32 tmp = tcg_temp_new_i32(); + + /* + * Copy bvalue to a temp now, so we can discard bvalue. + * This can avoid writing bvalue to memory when the + * delay slot cannot raise an exception. + */ + tcg_gen_mov_i32(tmp, cpu_bvalue); + tcg_gen_discard_i32(cpu_bvalue); + + tcg_gen_brcondi_i32(dc->jmp_cond, tmp, 0, taken); + gen_goto_tb(dc, 1, dc->base.pc_next); + gen_set_label(taken); + } + gen_goto_tb(dc, 0, dc->jmp_dest); + return; } - } - gen_tb_end(tb, num_insns); - tb->size = dc->pc - pc_start; - tb->icount = num_insns; + /* Indirect jump (or direct jump w/ singlestep) */ + tcg_gen_mov_i32(cpu_pc, cpu_btarget); + tcg_gen_discard_i32(cpu_btarget); + + if (unlikely(cs->singlestep_enabled)) { + gen_raise_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_exit_tb(NULL, 0); + } + return; -#ifdef DEBUG_DISAS -#if !SIM_COMPAT - if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) - && qemu_log_in_addr_range(pc_start)) { - FILE *logfile = qemu_log_lock(); - qemu_log("--------------\n"); - log_target_disas(cs, pc_start, dc->pc - pc_start); - qemu_log_unlock(logfile); + default: + g_assert_not_reached(); } -#endif -#endif - assert(!dc->abort_at_next_insn); +} + +static void mb_tr_disas_log(const DisasContextBase *dcb, CPUState *cs) +{ + qemu_log("IN: %s\n", lookup_symbol(dcb->pc_first)); + log_target_disas(cs, dcb->pc_first, dcb->tb->size); +} + +static const TranslatorOps mb_tr_ops = { + .init_disas_context = mb_tr_init_disas_context, + .tb_start = mb_tr_tb_start, + .insn_start = mb_tr_insn_start, + .breakpoint_check = mb_tr_breakpoint_check, + .translate_insn = mb_tr_translate_insn, + .tb_stop = mb_tr_tb_stop, + .disas_log = mb_tr_disas_log, +}; + +void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb, int max_insns) +{ + DisasContext dc; + translator_loop(&mb_tr_ops, &dc.base, cpu, tb, max_insns); } void mb_cpu_dump_state(CPUState *cs, FILE *f, int flags) { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; + uint32_t iflags; int i; - if (!env) { - return; + qemu_fprintf(f, "pc=0x%08x msr=0x%05x mode=%s(saved=%s) eip=%d ie=%d\n", + env->pc, env->msr, + (env->msr & MSR_UM) ? "user" : "kernel", + (env->msr & MSR_UMS) ? "user" : "kernel", + (bool)(env->msr & MSR_EIP), + (bool)(env->msr & MSR_IE)); + + iflags = env->iflags; + qemu_fprintf(f, "iflags: 0x%08x", iflags); + if (iflags & IMM_FLAG) { + qemu_fprintf(f, " IMM(0x%08x)", env->imm); + } + if (iflags & BIMM_FLAG) { + qemu_fprintf(f, " BIMM"); + } + if (iflags & D_FLAG) { + qemu_fprintf(f, " D(btarget=0x%08x)", env->btarget); } + if (iflags & DRTI_FLAG) { + qemu_fprintf(f, " DRTI"); + } + if (iflags & DRTE_FLAG) { + qemu_fprintf(f, " DRTE"); + } + if (iflags & DRTB_FLAG) { + qemu_fprintf(f, " DRTB"); + } + if (iflags & ESR_ESS_FLAG) { + qemu_fprintf(f, " ESR_ESS(0x%04x)", iflags & ESR_ESS_MASK); + } + + qemu_fprintf(f, "\nesr=0x%04x fsr=0x%02x btr=0x%08x edr=0x%x\n" + "ear=0x" TARGET_FMT_lx " slr=0x%x shr=0x%x\n", + env->esr, env->fsr, env->btr, env->edr, + env->ear, env->slr, env->shr); - qemu_fprintf(f, "IN: PC=%" PRIx64 " %s\n", - env->sregs[SR_PC], lookup_symbol(env->sregs[SR_PC])); - qemu_fprintf(f, "rmsr=%" PRIx64 " resr=%" PRIx64 " rear=%" PRIx64 " " - "debug=%x imm=%x iflags=%x fsr=%" PRIx64 " " - "rbtr=%" PRIx64 "\n", - env->sregs[SR_MSR], env->sregs[SR_ESR], env->sregs[SR_EAR], - env->debug, env->imm, env->iflags, env->sregs[SR_FSR], - env->sregs[SR_BTR]); - qemu_fprintf(f, "btaken=%d btarget=%" PRIx64 " 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", - (bool)(env->sregs[SR_MSR] & MSR_EIP), - (bool)(env->sregs[SR_MSR] & MSR_IE)); for (i = 0; i < 12; i++) { - qemu_fprintf(f, "rpvr%2.2d=%8.8x ", i, env->pvr.regs[i]); - if ((i + 1) % 4 == 0) { - qemu_fprintf(f, "\n"); - } + qemu_fprintf(f, "rpvr%-2d=%08x%c", + i, env->pvr.regs[i], i % 4 == 3 ? '\n' : ' '); } - /* Registers that aren't modeled are reported as 0 */ - qemu_fprintf(f, "redr=%" PRIx64 " rpid=0 rzpr=0 rtlbx=0 rtlbsx=0 " - "rtlblo=0 rtlbhi=0\n", env->sregs[SR_EDR]); - qemu_fprintf(f, "slr=%x shr=%x\n", env->slr, env->shr); for (i = 0; i < 32; i++) { - qemu_fprintf(f, "r%2.2d=%8.8x ", i, env->regs[i]); - if ((i + 1) % 4 == 0) - qemu_fprintf(f, "\n"); - } - qemu_fprintf(f, "\n\n"); + qemu_fprintf(f, "r%2.2d=%08x%c", + i, env->regs[i], i % 4 == 3 ? '\n' : ' '); + } + qemu_fprintf(f, "\n"); } void mb_tcg_init(void) { - int i; +#define R(X) { &cpu_R[X], offsetof(CPUMBState, regs[X]), "r" #X } +#define SP(X) { &cpu_##X, offsetof(CPUMBState, X), #X } + + static const struct { + TCGv_i32 *var; int ofs; char name[8]; + } i32s[] = { + /* + * Note that r0 is handled specially in reg_for_read + * and reg_for_write. Nothing should touch cpu_R[0]. + * Leave that element NULL, which will assert quickly + * inside the tcg generator functions. + */ + R(1), R(2), R(3), R(4), R(5), R(6), R(7), + R(8), R(9), R(10), R(11), R(12), R(13), R(14), R(15), + R(16), R(17), R(18), R(19), R(20), R(21), R(22), R(23), + R(24), R(25), R(26), R(27), R(28), R(29), R(30), R(31), + + SP(pc), + SP(msr), + SP(msr_c), + SP(imm), + SP(iflags), + SP(bvalue), + SP(btarget), + SP(res_val), + }; - env_debug = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, debug), - "debug0"); - env_iflags = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, iflags), - "iflags"); - env_imm = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, imm), - "imm"); - env_btarget = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUMBState, btarget), - "btarget"); - env_btaken = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, btaken), - "btaken"); - env_res_addr = tcg_global_mem_new(cpu_env, - offsetof(CPUMBState, res_addr), - "res_addr"); - env_res_val = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, res_val), - "res_val"); - for (i = 0; i < ARRAY_SIZE(cpu_R); i++) { - cpu_R[i] = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUMBState, regs[i]), - regnames[i]); - } - for (i = 0; i < ARRAY_SIZE(cpu_SR); i++) { - cpu_SR[i] = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUMBState, sregs[i]), - special_regnames[i]); +#undef R +#undef SP + + for (int i = 0; i < ARRAY_SIZE(i32s); ++i) { + *i32s[i].var = + tcg_global_mem_new_i32(cpu_env, i32s[i].ofs, i32s[i].name); } + + cpu_res_addr = + tcg_global_mem_new(cpu_env, offsetof(CPUMBState, res_addr), "res_addr"); } void restore_state_to_opc(CPUMBState *env, TranslationBlock *tb, target_ulong *data) { - env->sregs[SR_PC] = data[0]; + env->pc = data[0]; + env->iflags = data[1]; } |