diff options
Diffstat (limited to 'target')
-rw-r--r-- | target/microblaze/cpu.c | 115 | ||||
-rw-r--r-- | target/microblaze/cpu.h | 78 | ||||
-rw-r--r-- | target/microblaze/gdbstub.c | 6 | ||||
-rw-r--r-- | target/microblaze/helper.c | 247 | ||||
-rw-r--r-- | target/microblaze/machine.c | 106 | ||||
-rw-r--r-- | target/microblaze/meson.build | 5 | ||||
-rw-r--r-- | target/microblaze/mmu.c | 39 | ||||
-rw-r--r-- | target/microblaze/mmu.h | 20 | ||||
-rw-r--r-- | target/microblaze/op_helper.c | 2 | ||||
-rw-r--r-- | target/microblaze/translate.c | 194 |
10 files changed, 478 insertions, 334 deletions
diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 6392524..9b24821 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -26,7 +26,6 @@ #include "cpu.h" #include "qemu/module.h" #include "hw/qdev-properties.h" -#include "migration/vmstate.h" #include "exec/exec-all.h" #include "fpu/softfloat-helpers.h" @@ -135,10 +134,6 @@ static void mb_cpu_reset(DeviceState *dev) #else mb_cpu_write_msr(env, 0); mmu_init(&env->mmu); - env->mmu.c_mmu = 3; - env->mmu.c_mmu_tlb_access = 3; - env->mmu.c_mmu_zones = 16; - env->mmu.c_addr_mask = MAKE_64BIT_MASK(0, cpu->cfg.addr_size); #endif } @@ -153,7 +148,6 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) CPUState *cs = CPU(dev); MicroBlazeCPUClass *mcc = MICROBLAZE_CPU_GET_CLASS(dev); MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - CPUMBState *env = &cpu->env; uint8_t version_code = 0; const char *version; int i = 0; @@ -173,16 +167,6 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) qemu_init_vcpu(cs); - env->pvr.regs[0] = PVR0_USE_EXC_MASK - | PVR0_USE_ICACHE_MASK - | PVR0_USE_DCACHE_MASK; - env->pvr.regs[2] = PVR2_D_OPB_MASK - | PVR2_D_LMB_MASK - | PVR2_I_OPB_MASK - | PVR2_I_LMB_MASK - | PVR2_FPU_EXC_MASK - | 0; - version = cpu->cfg.version ? cpu->cfg.version : DEFAULT_CPU_VERSION; for (i = 0; mb_cpu_lookup[i].name && version; i++) { if (strcmp(mb_cpu_lookup[i].name, version) == 0) { @@ -195,46 +179,58 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) qemu_log("Invalid MicroBlaze version number: %s\n", cpu->cfg.version); } - env->pvr.regs[0] |= (cpu->cfg.stackprot ? PVR0_SPROT_MASK : 0) | - (cpu->cfg.use_fpu ? PVR0_USE_FPU_MASK : 0) | - (cpu->cfg.use_hw_mul ? PVR0_USE_HW_MUL_MASK : 0) | - (cpu->cfg.use_barrel ? PVR0_USE_BARREL_MASK : 0) | - (cpu->cfg.use_div ? PVR0_USE_DIV_MASK : 0) | - (cpu->cfg.use_mmu ? PVR0_USE_MMU_MASK : 0) | - (cpu->cfg.endi ? PVR0_ENDI_MASK : 0) | - (version_code << PVR0_VERSION_SHIFT) | - (cpu->cfg.pvr == C_PVR_FULL ? PVR0_PVR_FULL_MASK : 0) | - cpu->cfg.pvr_user1; - - env->pvr.regs[1] = cpu->cfg.pvr_user2; - env->pvr.regs[2] |= (cpu->cfg.use_fpu ? PVR2_USE_FPU_MASK : 0) | - (cpu->cfg.use_fpu > 1 ? PVR2_USE_FPU2_MASK : 0) | - (cpu->cfg.use_hw_mul ? PVR2_USE_HW_MUL_MASK : 0) | - (cpu->cfg.use_hw_mul > 1 ? PVR2_USE_MUL64_MASK : 0) | - (cpu->cfg.use_barrel ? PVR2_USE_BARREL_MASK : 0) | - (cpu->cfg.use_div ? PVR2_USE_DIV_MASK : 0) | - (cpu->cfg.use_msr_instr ? PVR2_USE_MSR_INSTR : 0) | - (cpu->cfg.use_pcmp_instr ? PVR2_USE_PCMP_INSTR : 0) | - (cpu->cfg.dopb_bus_exception ? - PVR2_DOPB_BUS_EXC_MASK : 0) | - (cpu->cfg.iopb_bus_exception ? - PVR2_IOPB_BUS_EXC_MASK : 0) | - (cpu->cfg.div_zero_exception ? - PVR2_DIV_ZERO_EXC_MASK : 0) | - (cpu->cfg.illegal_opcode_exception ? - PVR2_ILL_OPCODE_EXC_MASK : 0) | - (cpu->cfg.unaligned_exceptions ? - PVR2_UNALIGNED_EXC_MASK : 0) | - (cpu->cfg.opcode_0_illegal ? - PVR2_OPCODE_0x0_ILL_MASK : 0); - - env->pvr.regs[5] |= cpu->cfg.dcache_writeback ? - PVR5_DCACHE_WRITEBACK_MASK : 0; - - env->pvr.regs[10] = 0x0c000000 | /* Default to spartan 3a dsp family. */ - (cpu->cfg.addr_size - 32) << PVR10_ASIZE_SHIFT; - env->pvr.regs[11] = (cpu->cfg.use_mmu ? PVR11_USE_MMU : 0) | - 16 << 17; + cpu->cfg.pvr_regs[0] = + (PVR0_USE_EXC_MASK | + PVR0_USE_ICACHE_MASK | + PVR0_USE_DCACHE_MASK | + (cpu->cfg.stackprot ? PVR0_SPROT_MASK : 0) | + (cpu->cfg.use_fpu ? PVR0_USE_FPU_MASK : 0) | + (cpu->cfg.use_hw_mul ? PVR0_USE_HW_MUL_MASK : 0) | + (cpu->cfg.use_barrel ? PVR0_USE_BARREL_MASK : 0) | + (cpu->cfg.use_div ? PVR0_USE_DIV_MASK : 0) | + (cpu->cfg.use_mmu ? PVR0_USE_MMU_MASK : 0) | + (cpu->cfg.endi ? PVR0_ENDI_MASK : 0) | + (version_code << PVR0_VERSION_SHIFT) | + (cpu->cfg.pvr == C_PVR_FULL ? PVR0_PVR_FULL_MASK : 0) | + cpu->cfg.pvr_user1); + + cpu->cfg.pvr_regs[1] = cpu->cfg.pvr_user2; + + cpu->cfg.pvr_regs[2] = + (PVR2_D_OPB_MASK | + PVR2_D_LMB_MASK | + PVR2_I_OPB_MASK | + PVR2_I_LMB_MASK | + PVR2_FPU_EXC_MASK | + (cpu->cfg.use_fpu ? PVR2_USE_FPU_MASK : 0) | + (cpu->cfg.use_fpu > 1 ? PVR2_USE_FPU2_MASK : 0) | + (cpu->cfg.use_hw_mul ? PVR2_USE_HW_MUL_MASK : 0) | + (cpu->cfg.use_hw_mul > 1 ? PVR2_USE_MUL64_MASK : 0) | + (cpu->cfg.use_barrel ? PVR2_USE_BARREL_MASK : 0) | + (cpu->cfg.use_div ? PVR2_USE_DIV_MASK : 0) | + (cpu->cfg.use_msr_instr ? PVR2_USE_MSR_INSTR : 0) | + (cpu->cfg.use_pcmp_instr ? PVR2_USE_PCMP_INSTR : 0) | + (cpu->cfg.dopb_bus_exception ? PVR2_DOPB_BUS_EXC_MASK : 0) | + (cpu->cfg.iopb_bus_exception ? PVR2_IOPB_BUS_EXC_MASK : 0) | + (cpu->cfg.div_zero_exception ? PVR2_DIV_ZERO_EXC_MASK : 0) | + (cpu->cfg.illegal_opcode_exception ? PVR2_ILL_OPCODE_EXC_MASK : 0) | + (cpu->cfg.unaligned_exceptions ? PVR2_UNALIGNED_EXC_MASK : 0) | + (cpu->cfg.opcode_0_illegal ? PVR2_OPCODE_0x0_ILL_MASK : 0)); + + cpu->cfg.pvr_regs[5] |= + cpu->cfg.dcache_writeback ? PVR5_DCACHE_WRITEBACK_MASK : 0; + + cpu->cfg.pvr_regs[10] = + (0x0c000000 | /* Default to spartan 3a dsp family. */ + (cpu->cfg.addr_size - 32) << PVR10_ASIZE_SHIFT); + + cpu->cfg.pvr_regs[11] = ((cpu->cfg.use_mmu ? PVR11_USE_MMU : 0) | + 16 << 17); + + cpu->cfg.mmu = 3; + cpu->cfg.mmu_tlb_access = 3; + cpu->cfg.mmu_zones = 16; + cpu->cfg.addr_mask = MAKE_64BIT_MASK(0, cpu->cfg.addr_size); mcc->parent_realize(dev, errp); } @@ -254,11 +250,6 @@ static void mb_cpu_initfn(Object *obj) #endif } -static const VMStateDescription vmstate_mb_cpu = { - .name = "cpu", - .unmigratable = 1, -}; - static Property mb_properties[] = { DEFINE_PROP_UINT32("base-vectors", MicroBlazeCPU, cfg.base_vectors, 0), DEFINE_PROP_BOOL("use-stack-protection", MicroBlazeCPU, cfg.stackprot, @@ -338,8 +329,8 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) #ifndef CONFIG_USER_ONLY cc->do_transaction_failed = mb_cpu_transaction_failed; cc->get_phys_page_debug = mb_cpu_get_phys_page_debug; -#endif dc->vmsd = &vmstate_mb_cpu; +#endif device_class_set_props(dc, mb_properties); cc->gdb_num_core_regs = 32 + 27; diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index a25a2b4..297b368 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -264,10 +264,10 @@ struct CPUMBState { /* MSR_UM (1 << 11) */ /* MSR_VM (1 << 13) */ /* ESR_ESS_MASK [11:5] -- unwind into iflags for unaligned excp */ +#define D_FLAG (1 << 12) /* Bit in ESR. */ #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 | BIMM_FLAG | IMM_FLAG | \ @@ -278,19 +278,54 @@ struct CPUMBState { #if !defined(CONFIG_USER_ONLY) /* Unified MMU. */ - struct microblaze_mmu mmu; + MicroBlazeMMU mmu; #endif /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; /* These fields are preserved on reset. */ - - struct { - uint32_t regs[13]; - } pvr; }; +/* + * Microblaze Configuration Settings + * + * Note that the structure is sorted by type and size to minimize holes. + */ +typedef struct { + char *version; + + uint64_t addr_mask; + + uint32_t base_vectors; + uint32_t pvr_user2; + uint32_t pvr_regs[13]; + + uint8_t addr_size; + uint8_t use_fpu; + uint8_t use_hw_mul; + uint8_t pvr_user1; + uint8_t pvr; + uint8_t mmu; + uint8_t mmu_tlb_access; + uint8_t mmu_zones; + + bool stackprot; + bool use_barrel; + bool use_div; + bool use_msr_instr; + bool use_pcmp_instr; + bool use_mmu; + bool dcache_writeback; + bool endi; + bool dopb_bus_exception; + bool iopb_bus_exception; + bool illegal_opcode_exception; + bool opcode_0_illegal; + bool div_zero_exception; + bool unaligned_exceptions; +} MicroBlazeCPUConfig; + /** * MicroBlazeCPU: * @env: #CPUMBState @@ -305,32 +340,7 @@ struct MicroBlazeCPU { CPUNegativeOffsetState neg; CPUMBState env; - - /* Microblaze Configuration Settings */ - struct { - bool stackprot; - uint32_t base_vectors; - uint8_t addr_size; - uint8_t use_fpu; - uint8_t use_hw_mul; - bool use_barrel; - bool use_div; - bool use_msr_instr; - bool use_pcmp_instr; - bool use_mmu; - bool dcache_writeback; - bool endi; - bool dopb_bus_exception; - bool iopb_bus_exception; - bool illegal_opcode_exception; - bool opcode_0_illegal; - bool div_zero_exception; - bool unaligned_exceptions; - uint8_t pvr_user1; - uint32_t pvr_user2; - char *version; - uint8_t pvr; - } cfg; + MicroBlazeCPUConfig cfg; }; @@ -419,4 +429,8 @@ static inline int cpu_mmu_index(CPUMBState *env, bool ifetch) return MMU_KERNEL_IDX; } +#ifndef CONFIG_USER_ONLY +extern const VMStateDescription vmstate_mb_cpu; +#endif + #endif diff --git a/target/microblaze/gdbstub.c b/target/microblaze/gdbstub.c index 08d6a0e..be39fd4 100644 --- a/target/microblaze/gdbstub.c +++ b/target/microblaze/gdbstub.c @@ -78,7 +78,7 @@ int mb_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) break; case GDB_PVR0 ... GDB_PVR11: /* PVR12 is intentionally skipped */ - val = env->pvr.regs[n - GDB_PVR0]; + val = cpu->cfg.pvr_regs[n - GDB_PVR0]; break; case GDB_EDR: val = env->edr; @@ -132,10 +132,6 @@ int mb_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) case GDB_BTR: env->btr = tmp; break; - case GDB_PVR0 ... GDB_PVR11: - /* PVR12 is intentionally skipped */ - env->pvr.regs[n - GDB_PVR0] = tmp; - break; case GDB_EDR: env->edr = tmp; break; diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 0009052..3d6ce1b 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -52,7 +52,7 @@ bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, { MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; - struct microblaze_mmu_lookup lu; + MicroBlazeMMULookup lu; unsigned int hit; int prot; @@ -64,7 +64,7 @@ bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, return true; } - hit = mmu_translate(&env->mmu, &lu, address, access_type, mmu_idx); + hit = mmu_translate(cpu, &lu, address, access_type, mmu_idx); if (likely(hit)) { uint32_t vaddr = address & TARGET_PAGE_MASK; uint32_t paddr = lu.paddr + vaddr - lu.vaddr; @@ -111,6 +111,7 @@ void mb_cpu_do_interrupt(CPUState *cs) MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; uint32_t t, msr = mb_cpu_read_msr(env); + bool set_esr; /* IMM flag cannot propagate across a branch and into the dslot. */ assert((env->iflags & (D_FLAG | IMM_FLAG)) != (D_FLAG | IMM_FLAG)); @@ -118,142 +119,114 @@ void mb_cpu_do_interrupt(CPUState *cs) assert((env->iflags & (D_FLAG | BIMM_FLAG)) != BIMM_FLAG); /* RTI flags are private to translate. */ assert(!(env->iflags & (DRTI_FLAG | DRTE_FLAG | DRTB_FLAG))); - env->res_addr = RES_ADDR_NONE; + switch (cs->exception_index) { - case EXCP_HW_EXCP: - if (!(env->pvr.regs[0] & PVR0_USE_EXC_MASK)) { - qemu_log_mask(LOG_GUEST_ERROR, "Exception raised on system without exceptions!\n"); - return; - } - - env->regs[17] = env->pc + 4; - env->esr &= ~(1 << 12); - - /* Exception breaks branch + dslot sequence? */ - if (env->iflags & D_FLAG) { - env->esr |= 1 << 12 ; - env->btr = env->btarget; - } - - /* Disable the MMU. */ - t = (msr & (MSR_VM | MSR_UM)) << 1; - msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); - msr |= t; - /* Exception in progress. */ - msr |= MSR_EIP; - mb_cpu_write_msr(env, msr); - - qemu_log_mask(CPU_LOG_INT, - "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 = 0; - env->pc = cpu->cfg.base_vectors + 0x20; - break; - - case EXCP_MMU: + case EXCP_HW_EXCP: + if (!(cpu->cfg.pvr_regs[0] & PVR0_USE_EXC_MASK)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Exception raised on system without exceptions!\n"); + return; + } + + qemu_log_mask(CPU_LOG_INT, + "INT: HWE at pc=%08x msr=%08x iflags=%x\n", + env->pc, msr, env->iflags); + + /* Exception breaks branch + dslot sequence? */ + set_esr = true; + env->esr &= ~D_FLAG; + if (env->iflags & D_FLAG) { + env->esr |= D_FLAG; + env->btr = env->btarget; + } + + /* Exception in progress. */ + msr |= MSR_EIP; + env->regs[17] = env->pc + 4; + env->pc = cpu->cfg.base_vectors + 0x20; + break; + + case EXCP_MMU: + qemu_log_mask(CPU_LOG_INT, + "INT: MMU at pc=%08x msr=%08x " + "ear=%" PRIx64 " iflags=%x\n", + env->pc, msr, env->ear, env->iflags); + + /* Exception breaks branch + dslot sequence? */ + set_esr = true; + env->esr &= ~D_FLAG; + if (env->iflags & D_FLAG) { + env->esr |= D_FLAG; + env->btr = env->btarget; + /* Reexecute the branch. */ + env->regs[17] = env->pc - (env->iflags & BIMM_FLAG ? 8 : 4); + } else if (env->iflags & IMM_FLAG) { + /* Reexecute the imm. */ + env->regs[17] = env->pc - 4; + } else { 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->esr &= ~(1 << 12); - /* Exception breaks branch + dslot sequence? */ - if (env->iflags & D_FLAG) { - env->esr |= 1 << 12 ; - env->btr = env->btarget; - - /* Reexecute the branch. */ - env->regs[17] -= 4; - /* was the branch immprefixed?. */ - if (env->iflags & BIMM_FLAG) { - env->regs[17] -= 4; - log_cpu_state_mask(CPU_LOG_INT, cs, 0); - } - } else if (env->iflags & IMM_FLAG) { - env->regs[17] -= 4; - } - - /* Disable the MMU. */ - t = (msr & (MSR_VM | MSR_UM)) << 1; - msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); - msr |= t; - /* Exception in progress. */ - msr |= MSR_EIP; - mb_cpu_write_msr(env, msr); - - qemu_log_mask(CPU_LOG_INT, - "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 = 0; - env->pc = cpu->cfg.base_vectors + 0x20; - break; - - case EXCP_IRQ: - assert(!(msr & (MSR_EIP | MSR_BIP))); - assert(msr & MSR_IE); - assert(!(env->iflags & (D_FLAG | IMM_FLAG))); - - t = (msr & (MSR_VM | MSR_UM)) << 1; - -#if 0 -#include "disas/disas.h" - -/* Useful instrumentation when debugging interrupt issues in either - the models or in sw. */ - { - const char *sym; - - sym = lookup_symbol(env->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->pc, msr, t, env->iflags, sym); - - log_cpu_state(cs, 0); - } - } -#endif - qemu_log_mask(CPU_LOG_INT, - "interrupt at pc=%x msr=%x %x iflags=%x\n", - env->pc, msr, t, env->iflags); - - msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM | MSR_IE); - msr |= t; - mb_cpu_write_msr(env, msr); - - env->regs[14] = env->pc; - env->iflags = 0; - env->pc = cpu->cfg.base_vectors + 0x10; - //log_cpu_state_mask(CPU_LOG_INT, cs, 0); - break; - - case EXCP_HW_BREAK: - assert(!(env->iflags & (D_FLAG | IMM_FLAG))); - - t = (msr & (MSR_VM | MSR_UM)) << 1; - qemu_log_mask(CPU_LOG_INT, - "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); - msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); - msr |= t; - msr |= MSR_BIP; - env->regs[16] = env->pc; - env->iflags = 0; - env->pc = cpu->cfg.base_vectors + 0x18; - mb_cpu_write_msr(env, msr); - break; - default: - cpu_abort(cs, "unhandled exception type=%d\n", - cs->exception_index); - break; + /* Exception in progress. */ + msr |= MSR_EIP; + env->pc = cpu->cfg.base_vectors + 0x20; + break; + + case EXCP_IRQ: + assert(!(msr & (MSR_EIP | MSR_BIP))); + assert(msr & MSR_IE); + assert(!(env->iflags & (D_FLAG | IMM_FLAG))); + + qemu_log_mask(CPU_LOG_INT, + "INT: DEV at pc=%08x msr=%08x iflags=%x\n", + env->pc, msr, env->iflags); + set_esr = false; + + /* Disable interrupts. */ + msr &= ~MSR_IE; + env->regs[14] = env->pc; + env->pc = cpu->cfg.base_vectors + 0x10; + break; + + case EXCP_HW_BREAK: + assert(!(env->iflags & (D_FLAG | IMM_FLAG))); + + qemu_log_mask(CPU_LOG_INT, + "INT: BRK at pc=%08x msr=%08x iflags=%x\n", + env->pc, msr, env->iflags); + set_esr = false; + + /* Break in progress. */ + msr |= MSR_BIP; + env->regs[16] = env->pc; + env->pc = cpu->cfg.base_vectors + 0x18; + break; + + default: + cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index); + /* not reached */ + } + + /* Save previous mode, disable mmu, disable user-mode. */ + t = (msr & (MSR_VM | MSR_UM)) << 1; + msr &= ~(MSR_VMS | MSR_UMS | MSR_VM | MSR_UM); + msr |= t; + mb_cpu_write_msr(env, msr); + + env->res_addr = RES_ADDR_NONE; + env->iflags = 0; + + if (!set_esr) { + qemu_log_mask(CPU_LOG_INT, + " to pc=%08x msr=%08x\n", env->pc, msr); + } else if (env->esr & D_FLAG) { + qemu_log_mask(CPU_LOG_INT, + " to pc=%08x msr=%08x esr=%04x btr=%08x\n", + env->pc, msr, env->esr, env->btr); + } else { + qemu_log_mask(CPU_LOG_INT, + " to pc=%08x msr=%08x esr=%04x\n", + env->pc, msr, env->esr); } } @@ -262,12 +235,12 @@ hwaddr mb_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); CPUMBState *env = &cpu->env; target_ulong vaddr, paddr = 0; - struct microblaze_mmu_lookup lu; + MicroBlazeMMULookup lu; int mmu_idx = cpu_mmu_index(env, false); unsigned int hit; if (mmu_idx != MMU_NOMMU_IDX) { - hit = mmu_translate(&env->mmu, &lu, addr, 0, 0); + hit = mmu_translate(cpu, &lu, addr, 0, 0); if (hit) { vaddr = addr & TARGET_PAGE_MASK; paddr = lu.paddr + vaddr - lu.vaddr; diff --git a/target/microblaze/machine.c b/target/microblaze/machine.c new file mode 100644 index 0000000..acdb8d0 --- /dev/null +++ b/target/microblaze/machine.c @@ -0,0 +1,106 @@ +/* + * Microblaze VMState for qemu. + * + * Copyright (c) 2020 Linaro, Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "migration/cpu.h" + + +static VMStateField vmstate_mmu_fields[] = { + VMSTATE_UINT64_2DARRAY(rams, MicroBlazeMMU, 2, TLB_ENTRIES), + VMSTATE_UINT8_ARRAY(tids, MicroBlazeMMU, TLB_ENTRIES), + VMSTATE_UINT32_ARRAY(regs, MicroBlazeMMU, 3), + VMSTATE_END_OF_LIST() +}; + +static const VMStateDescription vmstate_mmu = { + .name = "mmu", + .version_id = 0, + .minimum_version_id = 0, + .fields = vmstate_mmu_fields, +}; + +static int get_msr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + CPUMBState *env = container_of(opaque, CPUMBState, msr); + + mb_cpu_write_msr(env, qemu_get_be32(f)); + return 0; +} + +static int put_msr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, QJSON *vmdesc) +{ + CPUMBState *env = container_of(opaque, CPUMBState, msr); + + qemu_put_be32(f, mb_cpu_read_msr(env)); + return 0; +} + +static const VMStateInfo vmstate_msr = { + .name = "msr", + .get = get_msr, + .put = put_msr, +}; + +static VMStateField vmstate_env_fields[] = { + VMSTATE_UINT32_ARRAY(regs, CPUMBState, 32), + + VMSTATE_UINT32(pc, CPUMBState), + VMSTATE_SINGLE(msr, CPUMBState, 0, vmstate_msr, uint32_t), + VMSTATE_UINT32(esr, CPUMBState), + VMSTATE_UINT32(fsr, CPUMBState), + VMSTATE_UINT32(btr, CPUMBState), + VMSTATE_UINT32(edr, CPUMBState), + VMSTATE_UINT32(slr, CPUMBState), + VMSTATE_UINT32(shr, CPUMBState), + VMSTATE_UINT64(ear, CPUMBState), + + VMSTATE_UINT32(btarget, CPUMBState), + VMSTATE_UINT32(imm, CPUMBState), + VMSTATE_UINT32(iflags, CPUMBState), + + VMSTATE_UINT32(res_val, CPUMBState), + VMSTATE_UINTTL(res_addr, CPUMBState), + + VMSTATE_STRUCT(mmu, CPUMBState, 0, vmstate_mmu, MicroBlazeMMU), + + VMSTATE_END_OF_LIST() +}; + +static const VMStateDescription vmstate_env = { + .name = "env", + .version_id = 0, + .minimum_version_id = 0, + .fields = vmstate_env_fields, +}; + +static VMStateField vmstate_cpu_fields[] = { + VMSTATE_CPU(), + VMSTATE_STRUCT(env, MicroBlazeCPU, 1, vmstate_env, CPUMBState), + VMSTATE_END_OF_LIST() +}; + +const VMStateDescription vmstate_mb_cpu = { + .name = "cpu", + .version_id = 0, + .minimum_version_id = 0, + .fields = vmstate_cpu_fields, +}; diff --git a/target/microblaze/meson.build b/target/microblaze/meson.build index 639c3f7..05ee0ec 100644 --- a/target/microblaze/meson.build +++ b/target/microblaze/meson.build @@ -11,7 +11,10 @@ microblaze_ss.add(files( )) microblaze_softmmu_ss = ss.source_set() -microblaze_softmmu_ss.add(files('mmu.c')) +microblaze_softmmu_ss.add(files( + 'mmu.c', + 'machine.c', +)) target_arch += {'microblaze': microblaze_ss} target_softmmu_arch += {'microblaze': microblaze_softmmu_ss} diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 6e583d7..1dbbb27 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -35,7 +35,7 @@ static unsigned int tlb_decode_size(unsigned int f) static void mmu_flush_idx(CPUMBState *env, unsigned int idx) { CPUState *cs = env_cpu(env); - struct microblaze_mmu *mmu = &env->mmu; + MicroBlazeMMU *mmu = &env->mmu; unsigned int tlb_size; uint32_t tlb_tag, end, t; @@ -55,7 +55,7 @@ static void mmu_flush_idx(CPUMBState *env, unsigned int idx) static void mmu_change_pid(CPUMBState *env, unsigned int newpid) { - struct microblaze_mmu *mmu = &env->mmu; + MicroBlazeMMU *mmu = &env->mmu; unsigned int i; uint32_t t; @@ -73,10 +73,10 @@ static void mmu_change_pid(CPUMBState *env, unsigned int newpid) } /* rw - 0 = read, 1 = write, 2 = fetch. */ -unsigned int mmu_translate(struct microblaze_mmu *mmu, - struct microblaze_mmu_lookup *lu, +unsigned int mmu_translate(MicroBlazeCPU *cpu, MicroBlazeMMULookup *lu, target_ulong vaddr, int rw, int mmu_idx) { + MicroBlazeMMU *mmu = &cpu->env.mmu; unsigned int i, hit = 0; unsigned int tlb_ex = 0, tlb_wr = 0, tlb_zsel; uint64_t tlb_tag, tlb_rpn, mask; @@ -115,13 +115,13 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, t0 = mmu->regs[MMU_R_ZPR] >> (30 - (tlb_zsel * 2)); t0 &= 0x3; - if (tlb_zsel > mmu->c_mmu_zones) { + if (tlb_zsel > cpu->cfg.mmu_zones) { qemu_log_mask(LOG_GUEST_ERROR, "tlb zone select out of range! %d\n", tlb_zsel); t0 = 1; /* Ignore. */ } - if (mmu->c_mmu == 1) { + if (cpu->cfg.mmu == 1) { t0 = 1; /* Zones are disabled. */ } @@ -158,7 +158,7 @@ unsigned int mmu_translate(struct microblaze_mmu *mmu, tlb_rpn = d & TLB_RPN_MASK; lu->vaddr = tlb_tag; - lu->paddr = tlb_rpn & mmu->c_addr_mask; + lu->paddr = tlb_rpn & cpu->cfg.addr_mask; lu->size = tlb_size; lu->err = ERR_HIT; lu->idx = i; @@ -176,10 +176,11 @@ done: /* Writes/reads to the MMU's special regs end up here. */ uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) { + MicroBlazeCPU *cpu = env_archcpu(env); unsigned int i; uint32_t r = 0; - if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { + if (cpu->cfg.mmu < 2 || !cpu->cfg.mmu_tlb_access) { qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return 0; } @@ -192,7 +193,7 @@ uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) /* Reads to HI/LO trig reads from the mmu rams. */ case MMU_R_TLBLO: case MMU_R_TLBHI: - if (!(env->mmu.c_mmu_tlb_access & 1)) { + if (!(cpu->cfg.mmu_tlb_access & 1)) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return 0; @@ -205,7 +206,7 @@ uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) break; case MMU_R_PID: case MMU_R_ZPR: - if (!(env->mmu.c_mmu_tlb_access & 1)) { + if (!(cpu->cfg.mmu_tlb_access & 1)) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return 0; @@ -228,12 +229,14 @@ uint32_t mmu_read(CPUMBState *env, bool ext, uint32_t rn) void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) { + MicroBlazeCPU *cpu = env_archcpu(env); uint64_t tmp64; unsigned int i; + qemu_log_mask(CPU_LOG_MMU, "%s rn=%d=%x old=%x\n", __func__, rn, v, env->mmu.regs[rn]); - if (env->mmu.c_mmu < 2 || !env->mmu.c_mmu_tlb_access) { + if (cpu->cfg.mmu < 2 || !cpu->cfg.mmu_tlb_access) { qemu_log_mask(LOG_GUEST_ERROR, "MMU access on MMU-less system\n"); return; } @@ -259,7 +262,7 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) env->mmu.rams[rn & 1][i] = deposit64(tmp64, ext * 32, 32, v); break; case MMU_R_ZPR: - if (env->mmu.c_mmu_tlb_access <= 1) { + if (cpu->cfg.mmu_tlb_access <= 1) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return; @@ -273,7 +276,7 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) env->mmu.regs[rn] = v; break; case MMU_R_PID: - if (env->mmu.c_mmu_tlb_access <= 1) { + if (cpu->cfg.mmu_tlb_access <= 1) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return; @@ -290,17 +293,17 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) break; case MMU_R_TLBSX: { - struct microblaze_mmu_lookup lu; + MicroBlazeMMULookup lu; int hit; - if (env->mmu.c_mmu_tlb_access <= 1) { + if (cpu->cfg.mmu_tlb_access <= 1) { qemu_log_mask(LOG_GUEST_ERROR, "Invalid access to MMU reg %d\n", rn); return; } - hit = mmu_translate(&env->mmu, &lu, - v & TLB_EPN_MASK, 0, cpu_mmu_index(env, false)); + hit = mmu_translate(cpu, &lu, v & TLB_EPN_MASK, + 0, cpu_mmu_index(env, false)); if (hit) { env->mmu.regs[MMU_R_TLBX] = lu.idx; } else { @@ -314,7 +317,7 @@ void mmu_write(CPUMBState *env, bool ext, uint32_t rn, uint32_t v) } } -void mmu_init(struct microblaze_mmu *mmu) +void mmu_init(MicroBlazeMMU *mmu) { int i; for (i = 0; i < ARRAY_SIZE(mmu->regs); i++) { diff --git a/target/microblaze/mmu.h b/target/microblaze/mmu.h index 75e5301..7d0fbb8 100644 --- a/target/microblaze/mmu.h +++ b/target/microblaze/mmu.h @@ -63,23 +63,16 @@ #define TLB_ENTRIES 64 -struct microblaze_mmu -{ +typedef struct { /* Data and tag brams. */ uint64_t rams[2][TLB_ENTRIES]; /* We keep a separate ram for the tids to avoid the 48 bit tag width. */ uint8_t tids[TLB_ENTRIES]; /* Control flops. */ uint32_t regs[3]; +} MicroBlazeMMU; - int c_mmu; - int c_mmu_tlb_access; - int c_mmu_zones; - uint64_t c_addr_mask; /* Mask to apply to physical addresses. */ -}; - -struct microblaze_mmu_lookup -{ +typedef struct { uint32_t paddr; uint32_t vaddr; unsigned int size; @@ -88,13 +81,12 @@ struct microblaze_mmu_lookup enum { ERR_PROT, ERR_MISS, ERR_HIT } err; -}; +} MicroBlazeMMULookup; -unsigned int mmu_translate(struct microblaze_mmu *mmu, - struct microblaze_mmu_lookup *lu, +unsigned int mmu_translate(MicroBlazeCPU *cpu, MicroBlazeMMULookup *lu, target_ulong vaddr, int rw, int mmu_idx); uint32_t mmu_read(CPUMBState *env, bool ea, uint32_t rn); void mmu_write(CPUMBState *env, bool ea, uint32_t rn, uint32_t v); -void mmu_init(struct microblaze_mmu *mmu); +void mmu_init(MicroBlazeMMU *mmu); #endif diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 4614e99..757f3ff 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -134,7 +134,7 @@ static void update_fpu_flags(CPUMBState *env, int flags, uintptr_t ra) raise = 1; } if (raise - && (env->pvr.regs[2] & PVR2_FPU_EXC_MASK) + && (env_archcpu(env)->cfg.pvr_regs[2] & PVR2_FPU_EXC_MASK) && (env->msr & MSR_EE)) { raise_fpu_exception(env, ra); } diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index a8a3249..abfcc7e 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -37,7 +37,12 @@ /* 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_EXIT DISAS_TARGET_1 /* all cpu state modified dynamically */ + +/* cpu state besides pc was modified dynamically; update pc to next */ +#define DISAS_EXIT_NEXT DISAS_TARGET_2 +/* cpu state besides pc was modified dynamically; update pc to btarget */ +#define DISAS_EXIT_JUMP DISAS_TARGET_3 static TCGv_i32 cpu_R[32]; static TCGv_i32 cpu_pc; @@ -55,7 +60,7 @@ static TCGv_i32 cpu_res_val; /* This is the state at translation time. */ typedef struct DisasContext { DisasContextBase base; - MicroBlazeCPU *cpu; + const MicroBlazeCPUConfig *cfg; /* TCG op of the current insn_start. */ TCGOp *insn_start; @@ -65,7 +70,6 @@ typedef struct DisasContext { /* Decoder. */ uint32_t ext_imm; - unsigned int cpustate_changed; unsigned int tb_flags; unsigned int tb_flags_to_set; int mem_index; @@ -143,7 +147,7 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) tcg_gen_exit_tb(dc->base.tb, n); } else { tcg_gen_movi_i32(cpu_pc, dest); - tcg_gen_exit_tb(NULL, 0); + tcg_gen_lookup_and_goto_ptr(); } dc->base.is_jmp = DISAS_NORETURN; } @@ -155,7 +159,7 @@ static void gen_goto_tb(DisasContext *dc, int n, target_ulong dest) static bool trap_illegal(DisasContext *dc, bool cond) { if (cond && (dc->tb_flags & MSR_EE) - && dc->cpu->cfg.illegal_opcode_exception) { + && dc->cfg->illegal_opcode_exception) { gen_raise_hw_excp(dc, ESR_EC_ILLEGAL_OP); } return cond; @@ -175,6 +179,21 @@ static bool trap_userspace(DisasContext *dc, bool cond) return cond_user; } +/* + * Return true, and log an error, if the current insn is + * within a delay slot. + */ +static bool invalid_delay_slot(DisasContext *dc, const char *insn_type) +{ + if (dc->tb_flags & D_FLAG) { + qemu_log_mask(LOG_GUEST_ERROR, + "Invalid insn in delay slot: %s at %08x\n", + insn_type, (uint32_t)dc->base.pc_next); + return true; + } + return false; +} + static TCGv_i32 reg_for_read(DisasContext *dc, int reg) { if (likely(reg != 0)) { @@ -272,7 +291,7 @@ static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects, #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); } + { return dc->cfg->CFG && do_typea(dc, a, SE, FN); } #define DO_TYPEA0(NAME, SE, FN) \ static bool trans_##NAME(DisasContext *dc, arg_typea0 *a) \ @@ -280,7 +299,7 @@ static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects, #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); } + { return dc->cfg->CFG && do_typea0(dc, a, SE, FN); } #define DO_TYPEBI(NAME, SE, FNI) \ static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \ @@ -288,7 +307,7 @@ static bool do_typeb_val(DisasContext *dc, arg_typeb *arg, bool side_effects, #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); } + { return dc->cfg->CFG && do_typeb_imm(dc, a, SE, FNI); } #define DO_TYPEBV(NAME, SE, FN) \ static bool trans_##NAME(DisasContext *dc, arg_typeb *a) \ @@ -496,6 +515,9 @@ DO_TYPEA_CFG(idivu, use_div, true, gen_idivu) static bool trans_imm(DisasContext *dc, arg_imm *arg) { + if (invalid_delay_slot(dc, "imm")) { + return true; + } dc->ext_imm = arg->imm << 16; tcg_gen_movi_i32(cpu_imm, dc->ext_imm); dc->tb_flags_to_set = IMM_FLAG; @@ -661,7 +683,7 @@ static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) tcg_gen_movi_tl(ret, 0); } - if ((ra == 1 || rb == 1) && dc->cpu->cfg.stackprot) { + if ((ra == 1 || rb == 1) && dc->cfg->stackprot) { gen_helper_stackprot(cpu_env, ret); } return ret; @@ -681,7 +703,7 @@ static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) tcg_gen_movi_tl(ret, (uint32_t)imm); } - if (ra == 1 && dc->cpu->cfg.stackprot) { + if (ra == 1 && dc->cfg->stackprot) { gen_helper_stackprot(cpu_env, ret); } return ret; @@ -690,7 +712,7 @@ static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) #ifndef CONFIG_USER_ONLY static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) { - int addr_size = dc->cpu->cfg.addr_size; + int addr_size = dc->cfg->addr_size; TCGv ret = tcg_temp_new(); if (addr_size == 32 || ra == 0) { @@ -750,7 +772,7 @@ static bool do_load(DisasContext *dc, int rd, TCGv addr, MemOp mop, if (size > MO_8 && (dc->tb_flags & MSR_EE) && - dc->cpu->cfg.unaligned_exceptions) { + dc->cfg->unaligned_exceptions) { record_unaligned_ess(dc, rd, size, false); mop |= MO_ALIGN; } @@ -896,7 +918,7 @@ static bool do_store(DisasContext *dc, int rd, TCGv addr, MemOp mop, if (size > MO_8 && (dc->tb_flags & MSR_EE) && - dc->cpu->cfg.unaligned_exceptions) { + dc->cfg->unaligned_exceptions) { record_unaligned_ess(dc, rd, size, true); mop |= MO_ALIGN; } @@ -1063,6 +1085,9 @@ static bool do_branch(DisasContext *dc, int dest_rb, int dest_imm, { uint32_t add_pc; + if (invalid_delay_slot(dc, "branch")) { + return true; + } if (delay) { setup_dslot(dc, dest_rb < 0); } @@ -1102,6 +1127,9 @@ static bool do_bcc(DisasContext *dc, int dest_rb, int dest_imm, { TCGv_i32 zero, next; + if (invalid_delay_slot(dc, "bcc")) { + return true; + } if (delay) { setup_dslot(dc, dest_rb < 0); } @@ -1154,6 +1182,10 @@ static bool trans_brk(DisasContext *dc, arg_typea_br *arg) if (trap_userspace(dc, true)) { return true; } + if (invalid_delay_slot(dc, "brk")) { + 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); @@ -1161,7 +1193,7 @@ static bool trans_brk(DisasContext *dc, arg_typea_br *arg) tcg_gen_ori_i32(cpu_msr, cpu_msr, MSR_BIP); tcg_gen_movi_tl(cpu_res_addr, -1); - dc->base.is_jmp = DISAS_UPDATE; + dc->base.is_jmp = DISAS_EXIT; return true; } @@ -1172,6 +1204,10 @@ static bool trans_brki(DisasContext *dc, arg_typeb_br *arg) if (trap_userspace(dc, imm != 0x8 && imm != 0x18)) { return true; } + if (invalid_delay_slot(dc, "brki")) { + return true; + } + tcg_gen_movi_i32(cpu_pc, imm); if (arg->rd) { tcg_gen_movi_i32(cpu_R[arg->rd], dc->base.pc_next); @@ -1202,7 +1238,7 @@ static bool trans_brki(DisasContext *dc, arg_typeb_br *arg) ~(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; + dc->base.is_jmp = DISAS_EXIT; #endif return true; @@ -1212,6 +1248,11 @@ static bool trans_mbar(DisasContext *dc, arg_mbar *arg) { int mbar_imm = arg->imm; + /* Note that mbar is a specialized branch instruction. */ + if (invalid_delay_slot(dc, "mbar")) { + return true; + } + /* Data access memory barrier. */ if ((mbar_imm & 2) == 0) { tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); @@ -1250,7 +1291,7 @@ static bool trans_mbar(DisasContext *dc, arg_mbar *arg) * * Therefore, choose to end the TB always. */ - dc->cpustate_changed = 1; + dc->base.is_jmp = DISAS_EXIT_NEXT; return true; } @@ -1259,6 +1300,10 @@ static bool do_rts(DisasContext *dc, arg_typeb_bc *arg, int to_set) if (trap_userspace(dc, to_set)) { return true; } + if (invalid_delay_slot(dc, "rts")) { + return true; + } + dc->tb_flags_to_set |= to_set; setup_dslot(dc, true); @@ -1280,7 +1325,7 @@ 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) { + if (dc->cfg->opcode_0_illegal) { trap_illegal(dc, true); return true; } @@ -1302,19 +1347,6 @@ static void msr_read(DisasContext *dc, TCGv_i32 d) tcg_temp_free_i32(t); } -#ifndef CONFIG_USER_ONLY -static void msr_write(DisasContext *dc, TCGv_i32 v) -{ - dc->cpustate_changed = 1; - - /* Install MSR_C. */ - tcg_gen_extract_i32(cpu_msr_c, v, 2, 1); - - /* 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 bool do_msrclrset(DisasContext *dc, arg_type_msr *arg, bool set) { uint32_t imm = arg->imm; @@ -1347,7 +1379,7 @@ static bool do_msrclrset(DisasContext *dc, arg_type_msr *arg, bool set) } else { tcg_gen_andi_i32(cpu_msr, cpu_msr, ~imm); } - dc->cpustate_changed = 1; + dc->base.is_jmp = DISAS_EXIT_NEXT; } return true; } @@ -1380,7 +1412,13 @@ static bool trans_mts(DisasContext *dc, arg_mts *arg) TCGv_i32 src = reg_for_read(dc, arg->ra); switch (arg->rs) { case SR_MSR: - msr_write(dc, src); + /* Install MSR_C. */ + tcg_gen_extract_i32(cpu_msr_c, src, 2, 1); + /* + * Clear MSR_C and MSR_CC; + * MSR_PVR is not writable, and is always clear. + */ + tcg_gen_andi_i32(cpu_msr, src, ~(MSR_C | MSR_CC | MSR_PVR)); break; case SR_FSR: tcg_gen_st_i32(src, cpu_env, offsetof(CPUMBState, fsr)); @@ -1412,7 +1450,7 @@ static bool trans_mts(DisasContext *dc, arg_mts *arg) qemu_log_mask(LOG_GUEST_ERROR, "Invalid mts reg 0x%x\n", arg->rs); return true; } - dc->cpustate_changed = 1; + dc->base.is_jmp = DISAS_EXIT_NEXT; return true; #endif } @@ -1501,7 +1539,8 @@ static bool trans_mfs(DisasContext *dc, arg_mfs *arg) case 0x2000 ... 0x200c: tcg_gen_ld_i32(dest, cpu_env, - offsetof(CPUMBState, pvr.regs[arg->rs - 0x2000])); + offsetof(MicroBlazeCPU, cfg.pvr_regs[arg->rs - 0x2000]) + - offsetof(MicroBlazeCPU, env)); break; default: qemu_log_mask(LOG_GUEST_ERROR, "Invalid mfs reg 0x%x\n", arg->rs); @@ -1521,7 +1560,6 @@ static void do_rti(DisasContext *dc) 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) @@ -1534,7 +1572,6 @@ static void do_rtb(DisasContext *dc) 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) @@ -1548,7 +1585,6 @@ static void do_rte(DisasContext *dc) 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. */ @@ -1622,9 +1658,8 @@ static void mb_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); int bound; - dc->cpu = cpu; + dc->cfg = &cpu->cfg; 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; @@ -1700,20 +1735,47 @@ static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs) dc->base.pc_next += 4; 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); + /* + * Finish any return-from branch. + */ + uint32_t rt_ibe = dc->tb_flags & (DRTI_FLAG | DRTB_FLAG | DRTE_FLAG); + if (unlikely(rt_ibe != 0)) { + dc->tb_flags &= ~(DRTI_FLAG | DRTB_FLAG | DRTE_FLAG); + if (rt_ibe & DRTI_FLAG) { + do_rti(dc); + } else if (rt_ibe & DRTB_FLAG) { + do_rtb(dc); + } else { + do_rte(dc); + } } - dc->base.is_jmp = DISAS_JUMP; - } - /* 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); + /* Complete the branch, ending the TB. */ + switch (dc->base.is_jmp) { + case DISAS_NORETURN: + /* + * E.g. illegal insn in a delay slot. We've already exited + * and will handle D_FLAG in mb_cpu_do_interrupt. + */ + break; + case DISAS_NEXT: + /* + * Normal insn a delay slot. + * However, the return-from-exception type insns should + * return to the main loop, as they have adjusted MSR. + */ + dc->base.is_jmp = (rt_ibe ? DISAS_EXIT_JUMP : DISAS_JUMP); + break; + case DISAS_EXIT_NEXT: + /* + * E.g. mts insn in a delay slot. Continue with btarget, + * but still return to the main loop. + */ + dc->base.is_jmp = DISAS_EXIT_JUMP; + break; + default: + g_assert_not_reached(); + } } } @@ -1733,13 +1795,15 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) gen_goto_tb(dc, 0, dc->base.pc_next); return; - case DISAS_UPDATE: - if (unlikely(cs->singlestep_enabled)) { - gen_raise_exception(dc, EXCP_DEBUG); - } else { - tcg_gen_exit_tb(NULL, 0); - } - return; + case DISAS_EXIT: + break; + case DISAS_EXIT_NEXT: + tcg_gen_movi_i32(cpu_pc, dc->base.pc_next); + break; + case DISAS_EXIT_JUMP: + tcg_gen_mov_i32(cpu_pc, cpu_btarget); + tcg_gen_discard_i32(cpu_btarget); + break; case DISAS_JUMP: if (dc->jmp_dest != -1 && !cs->singlestep_enabled) { @@ -1774,13 +1838,20 @@ static void mb_tr_tb_stop(DisasContextBase *dcb, CPUState *cs) if (unlikely(cs->singlestep_enabled)) { gen_raise_exception(dc, EXCP_DEBUG); } else { - tcg_gen_exit_tb(NULL, 0); + tcg_gen_lookup_and_goto_ptr(); } return; default: g_assert_not_reached(); } + + /* Finish DISAS_EXIT_* */ + if (unlikely(cs->singlestep_enabled)) { + gen_raise_exception(dc, EXCP_DEBUG); + } else { + tcg_gen_exit_tb(NULL, 0); + } } static void mb_tr_disas_log(const DisasContextBase *dcb, CPUState *cs) @@ -1848,11 +1919,6 @@ void mb_cpu_dump_state(CPUState *cs, FILE *f, int flags) env->esr, env->fsr, env->btr, env->edr, env->ear, env->slr, env->shr); - for (i = 0; i < 12; i++) { - qemu_fprintf(f, "rpvr%-2d=%08x%c", - i, env->pvr.regs[i], i % 4 == 3 ? '\n' : ' '); - } - for (i = 0; i < 32; i++) { qemu_fprintf(f, "r%2.2d=%08x%c", i, env->regs[i], i % 4 == 3 ? '\n' : ' '); |