Use lookup_and_goto_tb.

Cleanup and fill in VMStateDescription.
 -----BEGIN PGP SIGNATURE-----
 
 iQFRBAABCgA7FiEEekgeeIaLTbaoWgXAZN846K9+IV8FAl9WkbMdHHJpY2hhcmQu
 aGVuZGVyc29uQGxpbmFyby5vcmcACgkQZN846K9+IV/fvQf/UvUOipiXP7vafHI1
 Qx3NZ3nJHOMRz58eBDLidSkWgQM7+zHjBo1V5CvtM6Ajywpsn4IFe+4SJb7MVAYq
 6BSj2VDMq5fCboL52i3xJyBHTE7yqlb4bV3uNSk7dXwf5QQs0sT9PLYp6TuxjSj5
 SLicEron3uCc6Y0Z1tX1yKPjl2Lz5PoZ4Z98m6wZhd/pQbbc23+hMlz91fjyVAs2
 d9ZDnfxL71XQeTUb5tOLC2OK0rQJDQzzMSAO4Ilnrg/w6k0LGlP/kvYsHI+qya1q
 Rm+iBRGZQoItzkzkL1sWXP5StF9xLPRK60cET0N7vMnwN6sbpd3fOOWhE9EDtDWB
 tK0wxQ==
 =1+dD
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/rth/tags/pull-mb-20200907-2' into staging

Use lookup_and_goto_tb.
Cleanup and fill in VMStateDescription.

# gpg: Signature made Mon 07 Sep 2020 21:01:55 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-20200907-2:
  configure: Do not set TARGET_ABI32 for microblaze
  target/microblaze: Put MicroBlazeCPUConfig into DisasContext
  target/microblaze: Fill in VMStateDescription for cpu
  target/microblaze: Move mmu parameters to MicroBlazeCPUConfig
  target/microblaze: Treat pvr_regs as constant
  target/microblaze: Move pvr regs to MicroBlazeCPUConfig
  target/microblaze: Reorg MicroBlazeCPUConfig to minimize holes
  target/microblaze: Split out MicroBlazeCPUConfig
  target/microblaze: Diagnose invalid insns in delay slots
  target/microblaze: Use tcg_gen_lookup_and_goto_ptr
  target/microblaze: Force rtid, rted, rtbd to exit
  target/microblaze: Handle DISAS_EXIT_NEXT in delay slot
  target/microblaze: Replace cpustate_changed with DISAS_EXIT_NEXT
  target/microblaze: Introduce DISAS_EXIT_NEXT, DISAS_EXIT_JUMP
  target/microblaze: Rename DISAS_UPDATE to DISAS_EXIT
  target/microblaze: Rename mmu structs
  target/microblaze: Cleanup mb_cpu_do_interrupt
  target/microblaze: Renumber D_FLAG

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
master
Peter Maydell 2020-09-08 15:26:13 +01:00
commit 00942071a0
12 changed files with 463 additions and 320 deletions

1
configure vendored
View File

@ -7608,7 +7608,6 @@ case "$target_name" in
TARGET_SYSTBL_ABI=common
mttcg="yes"
bflt="yes"
echo "TARGET_ABI32=y" >> $config_target_mak
;;
mips|mipsel)
mttcg="yes"

View File

@ -200,9 +200,9 @@ petalogix_ml605_init(MachineState *machine)
}
/* setup PVR to match kernel settings */
cpu->env.pvr.regs[4] = 0xc56b8000;
cpu->env.pvr.regs[5] = 0xc56be000;
cpu->env.pvr.regs[10] = 0x0e000000; /* virtex 6 */
cpu->cfg.pvr_regs[4] = 0xc56b8000;
cpu->cfg.pvr_regs[5] = 0xc56be000;
cpu->cfg.pvr_regs[10] = 0x0e000000; /* virtex 6 */
microblaze_load_kernel(cpu, MEMORY_BASEADDR, ram_size,
machine->initrd_filename,

View File

@ -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;
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);
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);
cpu->cfg.pvr_regs[1] = cpu->cfg.pvr_user2;
env->pvr.regs[5] |= cpu->cfg.dcache_writeback ?
PVR5_DCACHE_WRITEBACK_MASK : 0;
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));
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[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;

View File

@ -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

View File

@ -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;

View File

@ -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;
}
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;
}
env->regs[17] = env->pc + 4;
env->esr &= ~(1 << 12);
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? */
if (env->iflags & D_FLAG) {
env->esr |= 1 << 12 ;
env->btr = env->btarget;
}
/* 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;
}
/* 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);
/* Exception in progress. */
msr |= MSR_EIP;
env->regs[17] = env->pc + 4;
env->pc = cpu->cfg.base_vectors + 0x20;
break;
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:
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);
case EXCP_MMU:
/* 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);
/* Exception in progress. */
msr |= MSR_EIP;
env->pc = cpu->cfg.base_vectors + 0x20;
break;
env->esr &= ~(1 << 12);
/* Exception breaks branch + dslot sequence? */
if (env->iflags & D_FLAG) {
env->esr |= 1 << 12 ;
env->btr = env->btarget;
case EXCP_IRQ:
assert(!(msr & (MSR_EIP | MSR_BIP)));
assert(msr & MSR_IE);
assert(!(env->iflags & (D_FLAG | IMM_FLAG)));
/* 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;
}
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 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);
/* Disable interrupts. */
msr &= ~MSR_IE;
env->regs[14] = env->pc;
env->pc = cpu->cfg.base_vectors + 0x10;
break;
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_HW_BREAK:
assert(!(env->iflags & (D_FLAG | IMM_FLAG)));
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: BRK at pc=%08x msr=%08x iflags=%x\n",
env->pc, msr, env->iflags);
set_esr = false;
t = (msr & (MSR_VM | MSR_UM)) << 1;
/* Break in progress. */
msr |= MSR_BIP;
env->regs[16] = env->pc;
env->pc = cpu->cfg.base_vectors + 0x18;
break;
#if 0
#include "disas/disas.h"
default:
cpu_abort(cs, "unhandled exception type=%d\n", cs->exception_index);
/* not reached */
}
/* Useful instrumentation when debugging interrupt issues in either
the models or in sw. */
{
const char *sym;
/* 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);
sym = lookup_symbol(env->pc);
if (sym
&& (!strcmp("netif_rx", sym)
|| !strcmp("process_backlog", sym))) {
env->res_addr = RES_ADDR_NONE;
env->iflags = 0;
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;
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;

106
target/microblaze/machine.c Normal file
View File

@ -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,
};

View File

@ -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}

View File

@ -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++) {

View File

@ -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

View File

@ -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);
}

View File

@ -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' : ' ');