MIPS target (Jocelyn Mayer)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1464 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
parent
6643d27ea0
commit
6af0bf9c7c
|
@ -72,6 +72,10 @@ ifeq ($(ARCH), ppc)
|
||||||
PROGS+=$(QEMU_SYSTEM)
|
PROGS+=$(QEMU_SYSTEM)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
endif # TARGET_ARCH = ppc
|
||||||
|
|
||||||
|
ifeq ($(TARGET_ARCH), mips)
|
||||||
|
|
||||||
ifeq ($(ARCH), i386)
|
ifeq ($(ARCH), i386)
|
||||||
ifdef CONFIG_SOFTMMU
|
ifdef CONFIG_SOFTMMU
|
||||||
PROGS+=$(QEMU_SYSTEM)
|
PROGS+=$(QEMU_SYSTEM)
|
||||||
|
@ -84,7 +88,7 @@ PROGS+=$(QEMU_SYSTEM)
|
||||||
endif
|
endif
|
||||||
endif # ARCH = x86_64
|
endif # ARCH = x86_64
|
||||||
|
|
||||||
endif # TARGET_ARCH = ppc
|
endif # TARGET_ARCH = mips
|
||||||
|
|
||||||
ifeq ($(TARGET_ARCH), sparc)
|
ifeq ($(TARGET_ARCH), sparc)
|
||||||
|
|
||||||
|
@ -263,6 +267,10 @@ ifeq ($(TARGET_ARCH), ppc)
|
||||||
LIBOBJS+= op_helper.o helper.o
|
LIBOBJS+= op_helper.o helper.o
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(TARGET_ARCH), mips)
|
||||||
|
LIBOBJS+= op_helper.o helper.o
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||||
LIBOBJS+= op_helper.o helper.o
|
LIBOBJS+= op_helper.o helper.o
|
||||||
endif
|
endif
|
||||||
|
@ -288,6 +296,9 @@ endif
|
||||||
ifeq ($(findstring ppc, $(TARGET_ARCH) $(ARCH)),ppc)
|
ifeq ($(findstring ppc, $(TARGET_ARCH) $(ARCH)),ppc)
|
||||||
LIBOBJS+=ppc-dis.o
|
LIBOBJS+=ppc-dis.o
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(findstring mips, $(TARGET_ARCH) $(ARCH)),mips)
|
||||||
|
LIBOBJS+=mips-dis.o
|
||||||
|
endif
|
||||||
ifeq ($(findstring sparc, $(TARGET_BASE_ARCH) $(ARCH)),sparc)
|
ifeq ($(findstring sparc, $(TARGET_BASE_ARCH) $(ARCH)),sparc)
|
||||||
LIBOBJS+=sparc-dis.o
|
LIBOBJS+=sparc-dis.o
|
||||||
endif
|
endif
|
||||||
|
@ -348,6 +359,10 @@ VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
|
||||||
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o
|
VL_OBJS+= mc146818rtc.o serial.o i8259.o i8254.o fdc.o m48t59.o
|
||||||
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o
|
VL_OBJS+= ppc_prep.o ppc_chrp.o cuda.o adb.o openpic.o heathrow_pic.o mixeng.o
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(TARGET_ARCH), mips)
|
||||||
|
VL_OBJS+= mips.o mips_r4k.o dma.o vga.o serial.o #ide.o ne2000.o pckbd.o
|
||||||
|
VL_OBJS+= #i8259.o i8254.o fdc.o m48t59.o
|
||||||
|
endif
|
||||||
ifeq ($(TARGET_BASE_ARCH), sparc)
|
ifeq ($(TARGET_BASE_ARCH), sparc)
|
||||||
ifeq ($(TARGET_ARCH), sparc64)
|
ifeq ($(TARGET_ARCH), sparc64)
|
||||||
VL_OBJS+= sun4u.o m48t08.o magic-load.o slavio_serial.o
|
VL_OBJS+= sun4u.o m48t08.o magic-load.o slavio_serial.o
|
||||||
|
@ -455,6 +470,11 @@ op.o: op.c op_template.h op_mem.h
|
||||||
op_helper.o: op_helper_mem.h
|
op_helper.o: op_helper_mem.h
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifeq ($(TARGET_ARCH), mips)
|
||||||
|
op.o: op.c op_template.c op_mem.c
|
||||||
|
op_helper.o: op_helper_mem.c
|
||||||
|
endif
|
||||||
|
|
||||||
mixeng.o: mixeng.c mixeng.h mixeng_template.h
|
mixeng.o: mixeng.c mixeng.h mixeng_template.h
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
|
|
|
@ -617,6 +617,13 @@ void page_unprotect_range(uint8_t *data, unsigned long data_size);
|
||||||
#define cpu_gen_code cpu_ppc_gen_code
|
#define cpu_gen_code cpu_ppc_gen_code
|
||||||
#define cpu_signal_handler cpu_ppc_signal_handler
|
#define cpu_signal_handler cpu_ppc_signal_handler
|
||||||
|
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
#define CPUState CPUMIPSState
|
||||||
|
#define cpu_init cpu_mips_init
|
||||||
|
#define cpu_exec cpu_mips_exec
|
||||||
|
#define cpu_gen_code cpu_mips_gen_code
|
||||||
|
#define cpu_signal_handler cpu_mips_signal_handler
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
#error unsupported target CPU
|
#error unsupported target CPU
|
||||||
|
|
74
cpu-exec.c
74
cpu-exec.c
|
@ -182,6 +182,7 @@ int cpu_exec(CPUState *env1)
|
||||||
saved_regwptr = REGWPTR;
|
saved_regwptr = REGWPTR;
|
||||||
#endif
|
#endif
|
||||||
#elif defined(TARGET_PPC)
|
#elif defined(TARGET_PPC)
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
#else
|
#else
|
||||||
#error unsupported target CPU
|
#error unsupported target CPU
|
||||||
#endif
|
#endif
|
||||||
|
@ -220,6 +221,8 @@ int cpu_exec(CPUState *env1)
|
||||||
env->exception_next_eip, 0);
|
env->exception_next_eip, 0);
|
||||||
#elif defined(TARGET_PPC)
|
#elif defined(TARGET_PPC)
|
||||||
do_interrupt(env);
|
do_interrupt(env);
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
do_interrupt(env);
|
||||||
#elif defined(TARGET_SPARC)
|
#elif defined(TARGET_SPARC)
|
||||||
do_interrupt(env->exception_index);
|
do_interrupt(env->exception_index);
|
||||||
#endif
|
#endif
|
||||||
|
@ -301,6 +304,19 @@ int cpu_exec(CPUState *env1)
|
||||||
env->interrupt_request &= ~CPU_INTERRUPT_TIMER;
|
env->interrupt_request &= ~CPU_INTERRUPT_TIMER;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||||
|
(env->CP0_Status & (1 << CP0St_IE)) &&
|
||||||
|
(env->CP0_Cause & 0x0000FC00) &&
|
||||||
|
!(env->hflags & MIPS_HFLAG_EXL) &&
|
||||||
|
!(env->hflags & MIPS_HFLAG_ERL) &&
|
||||||
|
!(env->hflags & MIPS_HFLAG_DM)) {
|
||||||
|
/* Raise it */
|
||||||
|
env->exception_index = EXCP_EXT_INTERRUPT;
|
||||||
|
env->error_code = 0;
|
||||||
|
do_interrupt(env);
|
||||||
|
env->interrupt_request &= ~CPU_INTERRUPT_HARD;
|
||||||
|
}
|
||||||
#elif defined(TARGET_SPARC)
|
#elif defined(TARGET_SPARC)
|
||||||
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
|
||||||
(env->psret != 0)) {
|
(env->psret != 0)) {
|
||||||
|
@ -376,6 +392,8 @@ int cpu_exec(CPUState *env1)
|
||||||
cpu_dump_state(env, logfile, fprintf, 0);
|
cpu_dump_state(env, logfile, fprintf, 0);
|
||||||
#elif defined(TARGET_PPC)
|
#elif defined(TARGET_PPC)
|
||||||
cpu_dump_state(env, logfile, fprintf, 0);
|
cpu_dump_state(env, logfile, fprintf, 0);
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
cpu_dump_state(env, logfile, fprintf, 0);
|
||||||
#else
|
#else
|
||||||
#error unsupported target CPU
|
#error unsupported target CPU
|
||||||
#endif
|
#endif
|
||||||
|
@ -407,6 +425,10 @@ int cpu_exec(CPUState *env1)
|
||||||
(msr_se << MSR_SE) | (msr_le << MSR_LE);
|
(msr_se << MSR_SE) | (msr_le << MSR_LE);
|
||||||
cs_base = 0;
|
cs_base = 0;
|
||||||
pc = env->nip;
|
pc = env->nip;
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
flags = env->hflags & MIPS_HFLAGS_TMASK;
|
||||||
|
cs_base = NULL;
|
||||||
|
pc = env->PC;
|
||||||
#else
|
#else
|
||||||
#error unsupported CPU
|
#error unsupported CPU
|
||||||
#endif
|
#endif
|
||||||
|
@ -684,6 +706,7 @@ int cpu_exec(CPUState *env1)
|
||||||
REGWPTR = saved_regwptr;
|
REGWPTR = saved_regwptr;
|
||||||
#endif
|
#endif
|
||||||
#elif defined(TARGET_PPC)
|
#elif defined(TARGET_PPC)
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
#else
|
#else
|
||||||
#error unsupported target CPU
|
#error unsupported target CPU
|
||||||
#endif
|
#endif
|
||||||
|
@ -935,6 +958,57 @@ static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
|
||||||
/* never comes here */
|
/* never comes here */
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined (TARGET_MIPS)
|
||||||
|
static inline int handle_cpu_signal(unsigned long pc, unsigned long address,
|
||||||
|
int is_write, sigset_t *old_set,
|
||||||
|
void *puc)
|
||||||
|
{
|
||||||
|
TranslationBlock *tb;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (cpu_single_env)
|
||||||
|
env = cpu_single_env; /* XXX: find a correct solution for multithread */
|
||||||
|
#if defined(DEBUG_SIGNAL)
|
||||||
|
printf("qemu: SIGSEGV pc=0x%08lx address=%08lx w=%d oldset=0x%08lx\n",
|
||||||
|
pc, address, is_write, *(unsigned long *)old_set);
|
||||||
|
#endif
|
||||||
|
/* XXX: locking issue */
|
||||||
|
if (is_write && page_unprotect(address, pc, puc)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see if it is an MMU fault */
|
||||||
|
ret = cpu_ppc_handle_mmu_fault(env, address, is_write, msr_pr, 0);
|
||||||
|
if (ret < 0)
|
||||||
|
return 0; /* not an MMU fault */
|
||||||
|
if (ret == 0)
|
||||||
|
return 1; /* the MMU fault was handled without causing real CPU fault */
|
||||||
|
|
||||||
|
/* now we have a real cpu fault */
|
||||||
|
tb = tb_find_pc(pc);
|
||||||
|
if (tb) {
|
||||||
|
/* the PC is inside the translated code. It means that we have
|
||||||
|
a virtual CPU fault */
|
||||||
|
cpu_restore_state(tb, env, pc, puc);
|
||||||
|
}
|
||||||
|
if (ret == 1) {
|
||||||
|
#if 0
|
||||||
|
printf("PF exception: NIP=0x%08x error=0x%x %p\n",
|
||||||
|
env->nip, env->error_code, tb);
|
||||||
|
#endif
|
||||||
|
/* we restore the process signal mask as the sigreturn should
|
||||||
|
do it (XXX: use sigsetjmp) */
|
||||||
|
sigprocmask(SIG_SETMASK, old_set, NULL);
|
||||||
|
do_raise_exception_err(env->exception_index, env->error_code);
|
||||||
|
} else {
|
||||||
|
/* activate soft MMU for this block */
|
||||||
|
cpu_resume_from_signal(env, puc);
|
||||||
|
}
|
||||||
|
/* never comes here */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#error unsupported target CPU
|
#error unsupported target CPU
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -404,6 +404,8 @@ extern int generic_symbol_at_address
|
||||||
|
|
||||||
bfd_vma bfd_getl32 (const bfd_byte *addr);
|
bfd_vma bfd_getl32 (const bfd_byte *addr);
|
||||||
bfd_vma bfd_getb32 (const bfd_byte *addr);
|
bfd_vma bfd_getb32 (const bfd_byte *addr);
|
||||||
|
bfd_vma bfd_getl16 (const bfd_byte *addr);
|
||||||
|
bfd_vma bfd_getb16 (const bfd_byte *addr);
|
||||||
typedef enum bfd_boolean {false, true} boolean;
|
typedef enum bfd_boolean {false, true} boolean;
|
||||||
|
|
||||||
#endif /* ! defined (DIS_ASM_H) */
|
#endif /* ! defined (DIS_ASM_H) */
|
||||||
|
|
26
disas.c
26
disas.c
|
@ -108,6 +108,24 @@ bfd_vma bfd_getb32 (const bfd_byte *addr)
|
||||||
return (bfd_vma) v;
|
return (bfd_vma) v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bfd_vma bfd_getl16 (const bfd_byte *addr)
|
||||||
|
{
|
||||||
|
unsigned long v;
|
||||||
|
|
||||||
|
v = (unsigned long) addr[0];
|
||||||
|
v |= (unsigned long) addr[1] << 8;
|
||||||
|
return (bfd_vma) v;
|
||||||
|
}
|
||||||
|
|
||||||
|
bfd_vma bfd_getb16 (const bfd_byte *addr)
|
||||||
|
{
|
||||||
|
unsigned long v;
|
||||||
|
|
||||||
|
v = (unsigned long) addr[0] << 24;
|
||||||
|
v |= (unsigned long) addr[1] << 16;
|
||||||
|
return (bfd_vma) v;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef TARGET_ARM
|
#ifdef TARGET_ARM
|
||||||
static int
|
static int
|
||||||
print_insn_thumb1(bfd_vma pc, disassemble_info *info)
|
print_insn_thumb1(bfd_vma pc, disassemble_info *info)
|
||||||
|
@ -162,6 +180,8 @@ void target_disas(FILE *out, target_ulong code, target_ulong size, int flags)
|
||||||
if (cpu_single_env->msr[MSR_LE])
|
if (cpu_single_env->msr[MSR_LE])
|
||||||
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
disasm_info.endian = BFD_ENDIAN_LITTLE;
|
||||||
print_insn = print_insn_ppc;
|
print_insn = print_insn_ppc;
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
print_insn = print_insn_big_mips;
|
||||||
#else
|
#else
|
||||||
fprintf(out, "0x" TARGET_FMT_lx
|
fprintf(out, "0x" TARGET_FMT_lx
|
||||||
": Asm output not supported on this arch\n", code);
|
": Asm output not supported on this arch\n", code);
|
||||||
|
@ -222,6 +242,10 @@ void disas(FILE *out, void *code, unsigned long size)
|
||||||
print_insn = print_insn_sparc;
|
print_insn = print_insn_sparc;
|
||||||
#elif defined(__arm__)
|
#elif defined(__arm__)
|
||||||
print_insn = print_insn_arm;
|
print_insn = print_insn_arm;
|
||||||
|
#elif defined(__MIPSEB__)
|
||||||
|
print_insn = print_insn_big_mips;
|
||||||
|
#elif defined(__MIPSEL__)
|
||||||
|
print_insn = print_insn_little_mips;
|
||||||
#else
|
#else
|
||||||
fprintf(out, "0x%lx: Asm output not supported on this arch\n",
|
fprintf(out, "0x%lx: Asm output not supported on this arch\n",
|
||||||
(long) code);
|
(long) code);
|
||||||
|
@ -332,6 +356,8 @@ void monitor_disas(target_ulong pc, int nb_insn, int is_physical, int flags)
|
||||||
print_insn = print_insn_sparc;
|
print_insn = print_insn_sparc;
|
||||||
#elif defined(TARGET_PPC)
|
#elif defined(TARGET_PPC)
|
||||||
print_insn = print_insn_ppc;
|
print_insn = print_insn_ppc;
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
print_insn = print_insn_big_mips;
|
||||||
#else
|
#else
|
||||||
term_printf("0x" TARGET_FMT_lx
|
term_printf("0x" TARGET_FMT_lx
|
||||||
": Asm output not supported on this arch\n", pc);
|
": Asm output not supported on this arch\n", pc);
|
||||||
|
|
18
elf.h
18
elf.h
|
@ -31,11 +31,29 @@ typedef int64_t Elf64_Sxword;
|
||||||
#define PT_LOPROC 0x70000000
|
#define PT_LOPROC 0x70000000
|
||||||
#define PT_HIPROC 0x7fffffff
|
#define PT_HIPROC 0x7fffffff
|
||||||
#define PT_MIPS_REGINFO 0x70000000
|
#define PT_MIPS_REGINFO 0x70000000
|
||||||
|
#define PT_MIPS_OPTIONS 0x70000001
|
||||||
|
|
||||||
/* Flags in the e_flags field of the header */
|
/* Flags in the e_flags field of the header */
|
||||||
|
/* MIPS architecture level. */
|
||||||
|
#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */
|
||||||
|
#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */
|
||||||
|
#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */
|
||||||
|
#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */
|
||||||
|
#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */
|
||||||
|
#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */
|
||||||
|
#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */
|
||||||
|
|
||||||
|
/* The ABI of a file. */
|
||||||
|
#define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */
|
||||||
|
#define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */
|
||||||
|
|
||||||
#define EF_MIPS_NOREORDER 0x00000001
|
#define EF_MIPS_NOREORDER 0x00000001
|
||||||
#define EF_MIPS_PIC 0x00000002
|
#define EF_MIPS_PIC 0x00000002
|
||||||
#define EF_MIPS_CPIC 0x00000004
|
#define EF_MIPS_CPIC 0x00000004
|
||||||
|
#define EF_MIPS_ABI2 0x00000020
|
||||||
|
#define EF_MIPS_OPTIONS_FIRST 0x00000080
|
||||||
|
#define EF_MIPS_32BITMODE 0x00000100
|
||||||
|
#define EF_MIPS_ABI 0x0000f000
|
||||||
#define EF_MIPS_ARCH 0xf0000000
|
#define EF_MIPS_ARCH 0xf0000000
|
||||||
|
|
||||||
/* These constants define the different elf file types */
|
/* These constants define the different elf file types */
|
||||||
|
|
|
@ -582,6 +582,8 @@ static inline target_ulong get_phys_addr_code(CPUState *env, target_ulong addr)
|
||||||
is_user = ((env->hflags & HF_CPL_MASK) == 3);
|
is_user = ((env->hflags & HF_CPL_MASK) == 3);
|
||||||
#elif defined (TARGET_PPC)
|
#elif defined (TARGET_PPC)
|
||||||
is_user = msr_pr;
|
is_user = msr_pr;
|
||||||
|
#elif defined (TARGET_MIPS)
|
||||||
|
is_user = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM);
|
||||||
#elif defined (TARGET_SPARC)
|
#elif defined (TARGET_SPARC)
|
||||||
is_user = (env->psrs == 0);
|
is_user = (env->psrs == 0);
|
||||||
#else
|
#else
|
||||||
|
|
309
hw/mips_r4k.c
Normal file
309
hw/mips_r4k.c
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
#include "vl.h"
|
||||||
|
|
||||||
|
#define DEBUG_IRQ_COUNT
|
||||||
|
|
||||||
|
#define BIOS_FILENAME "mips_bios.bin"
|
||||||
|
//#define BIOS_FILENAME "system.bin"
|
||||||
|
#define KERNEL_LOAD_ADDR 0x80010000
|
||||||
|
#define INITRD_LOAD_ADDR 0x80800000
|
||||||
|
|
||||||
|
/* MIPS R4K IRQ controler */
|
||||||
|
#if defined(DEBUG_IRQ_COUNT)
|
||||||
|
static uint64_t irq_count[16];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern FILE *logfile;
|
||||||
|
|
||||||
|
void mips_set_irq (int n_IRQ, int level)
|
||||||
|
{
|
||||||
|
uint32_t mask;
|
||||||
|
|
||||||
|
if (n_IRQ < 0 || n_IRQ >= 8)
|
||||||
|
return;
|
||||||
|
mask = 0x100 << n_IRQ;
|
||||||
|
if (level != 0) {
|
||||||
|
#if 1
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "%s n %d l %d mask %08x %08x\n",
|
||||||
|
__func__, n_IRQ, level, mask, cpu_single_env->CP0_Status);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
cpu_single_env->CP0_Cause |= mask;
|
||||||
|
if ((cpu_single_env->CP0_Status & 0x00000001) &&
|
||||||
|
(cpu_single_env->CP0_Status & mask)) {
|
||||||
|
#if defined(DEBUG_IRQ_COUNT)
|
||||||
|
irq_count[n_IRQ]++;
|
||||||
|
#endif
|
||||||
|
#if 1
|
||||||
|
if (logfile)
|
||||||
|
fprintf(logfile, "%s raise IRQ\n", __func__);
|
||||||
|
#endif
|
||||||
|
cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cpu_single_env->CP0_Cause &= ~mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pic_set_irq (int n_IRQ, int level)
|
||||||
|
{
|
||||||
|
mips_set_irq(n_IRQ + 2, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pic_info (void)
|
||||||
|
{
|
||||||
|
term_printf("IRQ asserted: %02x mask: %02x\n",
|
||||||
|
(cpu_single_env->CP0_Cause >> 8) & 0xFF,
|
||||||
|
(cpu_single_env->CP0_Status >> 8) & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void irq_info (void)
|
||||||
|
{
|
||||||
|
#if !defined(DEBUG_IRQ_COUNT)
|
||||||
|
term_printf("irq statistic code not compiled.\n");
|
||||||
|
#else
|
||||||
|
int i;
|
||||||
|
int64_t count;
|
||||||
|
|
||||||
|
term_printf("IRQ statistics:\n");
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
count = irq_count[i];
|
||||||
|
if (count > 0)
|
||||||
|
term_printf("%2d: %lld\n", i, count);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_mips_irqctrl_init (void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MIPS R4K timer */
|
||||||
|
uint32_t cpu_mips_get_random (CPUState *env)
|
||||||
|
{
|
||||||
|
uint64_t now = qemu_get_clock(vm_clock);
|
||||||
|
|
||||||
|
return (uint32_t)now & 0x0000000F;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t cpu_mips_get_count (CPUState *env)
|
||||||
|
{
|
||||||
|
return env->CP0_Count +
|
||||||
|
(uint32_t)muldiv64(qemu_get_clock(vm_clock),
|
||||||
|
100 * 1000 * 1000, ticks_per_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cpu_mips_update_count (CPUState *env, uint32_t count,
|
||||||
|
uint32_t compare)
|
||||||
|
{
|
||||||
|
uint64_t now, next;
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
tmp = count;
|
||||||
|
if (count == compare)
|
||||||
|
tmp++;
|
||||||
|
now = qemu_get_clock(vm_clock);
|
||||||
|
next = now + muldiv64(compare - tmp, ticks_per_sec, 100 * 1000 * 1000);
|
||||||
|
if (next == now)
|
||||||
|
next++;
|
||||||
|
#if 1
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "%s: 0x%08llx %08x %08x => 0x%08llx\n",
|
||||||
|
__func__, now, count, compare, next - now);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* Store new count and compare registers */
|
||||||
|
env->CP0_Compare = compare;
|
||||||
|
env->CP0_Count =
|
||||||
|
count - (uint32_t)muldiv64(now, 100 * 1000 * 1000, ticks_per_sec);
|
||||||
|
/* Adjust timer */
|
||||||
|
qemu_mod_timer(env->timer, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_mips_store_count (CPUState *env, uint32_t value)
|
||||||
|
{
|
||||||
|
cpu_mips_update_count(env, value, env->CP0_Compare);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_mips_store_compare (CPUState *env, uint32_t value)
|
||||||
|
{
|
||||||
|
cpu_mips_update_count(env, cpu_mips_get_count(env), value);
|
||||||
|
pic_set_irq(5, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mips_timer_cb (void *opaque)
|
||||||
|
{
|
||||||
|
CPUState *env;
|
||||||
|
|
||||||
|
env = opaque;
|
||||||
|
#if 1
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "%s\n", __func__);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
cpu_mips_update_count(env, cpu_mips_get_count(env), env->CP0_Compare);
|
||||||
|
pic_set_irq(5, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_mips_clock_init (CPUState *env)
|
||||||
|
{
|
||||||
|
env->timer = qemu_new_timer(vm_clock, &mips_timer_cb, env);
|
||||||
|
env->CP0_Compare = 0;
|
||||||
|
cpu_mips_update_count(env, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void io_writeb (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
if (logfile)
|
||||||
|
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value);
|
||||||
|
cpu_outb(NULL, addr & 0xffff, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t io_readb (void *opaque, target_phys_addr_t addr)
|
||||||
|
{
|
||||||
|
uint32_t ret = cpu_inb(NULL, addr & 0xffff);
|
||||||
|
if (logfile)
|
||||||
|
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void io_writew (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
if (logfile)
|
||||||
|
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value);
|
||||||
|
#ifdef TARGET_WORDS_BIGENDIAN
|
||||||
|
value = bswap16(value);
|
||||||
|
#endif
|
||||||
|
cpu_outw(NULL, addr & 0xffff, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t io_readw (void *opaque, target_phys_addr_t addr)
|
||||||
|
{
|
||||||
|
uint32_t ret = cpu_inw(NULL, addr & 0xffff);
|
||||||
|
#ifdef TARGET_WORDS_BIGENDIAN
|
||||||
|
ret = bswap16(ret);
|
||||||
|
#endif
|
||||||
|
if (logfile)
|
||||||
|
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void io_writel (void *opaque, target_phys_addr_t addr, uint32_t value)
|
||||||
|
{
|
||||||
|
if (logfile)
|
||||||
|
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, value);
|
||||||
|
#ifdef TARGET_WORDS_BIGENDIAN
|
||||||
|
value = bswap32(value);
|
||||||
|
#endif
|
||||||
|
cpu_outl(NULL, addr & 0xffff, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t io_readl (void *opaque, target_phys_addr_t addr)
|
||||||
|
{
|
||||||
|
uint32_t ret = cpu_inl(NULL, addr & 0xffff);
|
||||||
|
|
||||||
|
#ifdef TARGET_WORDS_BIGENDIAN
|
||||||
|
ret = bswap32(ret);
|
||||||
|
#endif
|
||||||
|
if (logfile)
|
||||||
|
fprintf(logfile, "%s: addr %08x val %08x\n", __func__, addr, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
CPUWriteMemoryFunc *io_write[] = {
|
||||||
|
&io_writeb,
|
||||||
|
&io_writew,
|
||||||
|
&io_writel,
|
||||||
|
};
|
||||||
|
|
||||||
|
CPUReadMemoryFunc *io_read[] = {
|
||||||
|
&io_readb,
|
||||||
|
&io_readw,
|
||||||
|
&io_readl,
|
||||||
|
};
|
||||||
|
|
||||||
|
void mips_r4k_init (int ram_size, int vga_ram_size, int boot_device,
|
||||||
|
DisplayState *ds, const char **fd_filename, int snapshot,
|
||||||
|
const char *kernel_filename, const char *kernel_cmdline,
|
||||||
|
const char *initrd_filename)
|
||||||
|
{
|
||||||
|
char buf[1024];
|
||||||
|
target_ulong kernel_base, kernel_size, initrd_base, initrd_size;
|
||||||
|
unsigned long bios_offset;
|
||||||
|
int io_memory;
|
||||||
|
int linux_boot;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
printf("%s: start\n", __func__);
|
||||||
|
linux_boot = (kernel_filename != NULL);
|
||||||
|
/* allocate RAM */
|
||||||
|
cpu_register_physical_memory(0, ram_size, IO_MEM_RAM);
|
||||||
|
bios_offset = ram_size + vga_ram_size;
|
||||||
|
snprintf(buf, sizeof(buf), "%s/%s", bios_dir, BIOS_FILENAME);
|
||||||
|
printf("%s: load BIOS '%s' size %d\n", __func__, buf, BIOS_SIZE);
|
||||||
|
ret = load_image(buf, phys_ram_base + bios_offset);
|
||||||
|
if (ret != BIOS_SIZE) {
|
||||||
|
fprintf(stderr, "qemu: could not load MIPS bios '%s'\n", buf);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
cpu_register_physical_memory((uint32_t)(0x1fc00000),
|
||||||
|
BIOS_SIZE, bios_offset | IO_MEM_ROM);
|
||||||
|
#if 0
|
||||||
|
memcpy(phys_ram_base + 0x10000, phys_ram_base + bios_offset, BIOS_SIZE);
|
||||||
|
cpu_single_env->PC = 0x80010004;
|
||||||
|
#else
|
||||||
|
cpu_single_env->PC = 0xBFC00004;
|
||||||
|
#endif
|
||||||
|
if (linux_boot) {
|
||||||
|
kernel_base = KERNEL_LOAD_ADDR;
|
||||||
|
/* now we can load the kernel */
|
||||||
|
kernel_size = load_image(kernel_filename, phys_ram_base + kernel_base);
|
||||||
|
if (kernel_size < 0) {
|
||||||
|
fprintf(stderr, "qemu: could not load kernel '%s'\n",
|
||||||
|
kernel_filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
/* load initrd */
|
||||||
|
if (initrd_filename) {
|
||||||
|
initrd_base = INITRD_LOAD_ADDR;
|
||||||
|
initrd_size = load_image(initrd_filename,
|
||||||
|
phys_ram_base + initrd_base);
|
||||||
|
if (initrd_size < 0) {
|
||||||
|
fprintf(stderr, "qemu: could not load initial ram disk '%s'\n",
|
||||||
|
initrd_filename);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
initrd_base = 0;
|
||||||
|
initrd_size = 0;
|
||||||
|
}
|
||||||
|
cpu_single_env->PC = KERNEL_LOAD_ADDR;
|
||||||
|
} else {
|
||||||
|
kernel_base = 0;
|
||||||
|
kernel_size = 0;
|
||||||
|
initrd_base = 0;
|
||||||
|
initrd_size = 0;
|
||||||
|
}
|
||||||
|
/* XXX: should not be ! */
|
||||||
|
printf("%s: init VGA\n", __func__);
|
||||||
|
vga_initialize(NULL, ds, phys_ram_base + ram_size, ram_size,
|
||||||
|
vga_ram_size);
|
||||||
|
|
||||||
|
|
||||||
|
/* Init internal devices */
|
||||||
|
cpu_mips_clock_init(cpu_single_env);
|
||||||
|
cpu_mips_irqctrl_init();
|
||||||
|
|
||||||
|
isa_mem_base = 0x78000000;
|
||||||
|
/* Register 64 KB of ISA IO space at random address */
|
||||||
|
io_memory = cpu_register_io_memory(0, io_read, io_write, NULL);
|
||||||
|
cpu_register_physical_memory(0x70000000, 0x00010000, io_memory);
|
||||||
|
serial_init(0x3f8, 4, serial_hds[0]);
|
||||||
|
printf("%s: done\n", __func__);
|
||||||
|
}
|
||||||
|
|
||||||
|
QEMUMachine mips_machine = {
|
||||||
|
"mips",
|
||||||
|
"mips r4k platform",
|
||||||
|
mips_r4k_init,
|
||||||
|
};
|
|
@ -55,6 +55,8 @@
|
||||||
#define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3)
|
#define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3)
|
||||||
#elif defined (TARGET_PPC)
|
#elif defined (TARGET_PPC)
|
||||||
#define CPU_MEM_INDEX (msr_pr)
|
#define CPU_MEM_INDEX (msr_pr)
|
||||||
|
#elif defined (TARGET_MIPS)
|
||||||
|
#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM)
|
||||||
#elif defined (TARGET_SPARC)
|
#elif defined (TARGET_SPARC)
|
||||||
#define CPU_MEM_INDEX ((env->psrs) == 0)
|
#define CPU_MEM_INDEX ((env->psrs) == 0)
|
||||||
#endif
|
#endif
|
||||||
|
@ -66,6 +68,8 @@
|
||||||
#define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3)
|
#define CPU_MEM_INDEX ((env->hflags & HF_CPL_MASK) == 3)
|
||||||
#elif defined (TARGET_PPC)
|
#elif defined (TARGET_PPC)
|
||||||
#define CPU_MEM_INDEX (msr_pr)
|
#define CPU_MEM_INDEX (msr_pr)
|
||||||
|
#elif defined (TARGET_MIPS)
|
||||||
|
#define CPU_MEM_INDEX ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM)
|
||||||
#elif defined (TARGET_SPARC)
|
#elif defined (TARGET_SPARC)
|
||||||
#define CPU_MEM_INDEX ((env->psrs) == 0)
|
#define CPU_MEM_INDEX ((env->psrs) == 0)
|
||||||
#endif
|
#endif
|
||||||
|
|
415
target-mips/cpu.h
Normal file
415
target-mips/cpu.h
Normal file
|
@ -0,0 +1,415 @@
|
||||||
|
#if !defined (__MIPS_CPU_H__)
|
||||||
|
#define __MIPS_CPU_H__
|
||||||
|
|
||||||
|
#include "mips-defs.h"
|
||||||
|
#include "cpu-defs.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "softfloat.h"
|
||||||
|
|
||||||
|
typedef union fpr_t fpr_t;
|
||||||
|
union fpr_t {
|
||||||
|
double d;
|
||||||
|
float f;
|
||||||
|
uint32_t u[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(MIPS_USES_R4K_TLB)
|
||||||
|
typedef struct tlb_t tlb_t;
|
||||||
|
struct tlb_t {
|
||||||
|
target_ulong VPN;
|
||||||
|
target_ulong end;
|
||||||
|
uint8_t ASID;
|
||||||
|
uint8_t G;
|
||||||
|
uint8_t C[2];
|
||||||
|
uint8_t V[2];
|
||||||
|
uint8_t D[2];
|
||||||
|
target_ulong PFN[2];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct CPUMIPSState CPUMIPSState;
|
||||||
|
struct CPUMIPSState {
|
||||||
|
/* General integer registers */
|
||||||
|
target_ulong gpr[32];
|
||||||
|
/* Special registers */
|
||||||
|
target_ulong PC;
|
||||||
|
uint32_t HI, LO;
|
||||||
|
uint32_t DCR; /* ? */
|
||||||
|
#if defined(MIPS_USES_FPU)
|
||||||
|
/* Floating point registers */
|
||||||
|
fpr_t fpr[16];
|
||||||
|
/* Floating point special purpose registers */
|
||||||
|
uint32_t fcr0;
|
||||||
|
uint32_t fcr25;
|
||||||
|
uint32_t fcr26;
|
||||||
|
uint32_t fcr28;
|
||||||
|
uint32_t fcsr;
|
||||||
|
#endif
|
||||||
|
#if defined(MIPS_USES_R4K_TLB)
|
||||||
|
tlb_t tlb[16];
|
||||||
|
#endif
|
||||||
|
uint32_t CP0_index;
|
||||||
|
uint32_t CP0_random;
|
||||||
|
uint32_t CP0_EntryLo0;
|
||||||
|
uint32_t CP0_EntryLo1;
|
||||||
|
uint32_t CP0_Context;
|
||||||
|
uint32_t CP0_PageMask;
|
||||||
|
uint32_t CP0_Wired;
|
||||||
|
uint32_t CP0_BadVAddr;
|
||||||
|
uint32_t CP0_Count;
|
||||||
|
uint32_t CP0_EntryHi;
|
||||||
|
uint32_t CP0_Compare;
|
||||||
|
uint32_t CP0_Status;
|
||||||
|
#define CP0St_CU3 31
|
||||||
|
#define CP0St_CU2 30
|
||||||
|
#define CP0St_CU1 29
|
||||||
|
#define CP0St_CU0 28
|
||||||
|
#define CP0St_RP 27
|
||||||
|
#define CP0St_RE 25
|
||||||
|
#define CP0St_BEV 22
|
||||||
|
#define CP0St_TS 21
|
||||||
|
#define CP0St_SR 20
|
||||||
|
#define CP0St_NMI 19
|
||||||
|
#define CP0St_IM 8
|
||||||
|
#define CP0St_UM 4
|
||||||
|
#define CP0St_ERL 2
|
||||||
|
#define CP0St_EXL 1
|
||||||
|
#define CP0St_IE 0
|
||||||
|
uint32_t CP0_Cause;
|
||||||
|
#define CP0Ca_IV 23
|
||||||
|
uint32_t CP0_EPC;
|
||||||
|
uint32_t CP0_PRid;
|
||||||
|
uint32_t CP0_Config0;
|
||||||
|
#define CP0C0_M 31
|
||||||
|
#define CP0C0_K23 28
|
||||||
|
#define CP0C0_KU 25
|
||||||
|
#define CP0C0_MDU 20
|
||||||
|
#define CP0C0_MM 17
|
||||||
|
#define CP0C0_BM 16
|
||||||
|
#define CP0C0_BE 15
|
||||||
|
#define CP0C0_AT 13
|
||||||
|
#define CP0C0_AR 10
|
||||||
|
#define CP0C0_MT 7
|
||||||
|
#define CP0C0_K0 0
|
||||||
|
uint32_t CP0_Config1;
|
||||||
|
#define CP0C1_MMU 25
|
||||||
|
#define CP0C1_IS 22
|
||||||
|
#define CP0C1_IL 19
|
||||||
|
#define CP0C1_IA 16
|
||||||
|
#define CP0C1_DS 13
|
||||||
|
#define CP0C1_DL 10
|
||||||
|
#define CP0C1_DA 7
|
||||||
|
#define CP0C1_PC 4
|
||||||
|
#define CP0C1_WR 3
|
||||||
|
#define CP0C1_CA 2
|
||||||
|
#define CP0C1_EP 1
|
||||||
|
#define CP0C1_FP 0
|
||||||
|
uint32_t CP0_LLAddr;
|
||||||
|
uint32_t CP0_WatchLo;
|
||||||
|
uint32_t CP0_WatchHi;
|
||||||
|
uint32_t CP0_Debug;
|
||||||
|
#define CPDB_DBD 31
|
||||||
|
#define CP0DB_DM 30
|
||||||
|
#define CP0DB_LSNM 28
|
||||||
|
#define CP0DB_Doze 27
|
||||||
|
#define CP0DB_Halt 26
|
||||||
|
#define CP0DB_CNT 25
|
||||||
|
#define CP0DB_IBEP 24
|
||||||
|
#define CP0DB_DBEP 21
|
||||||
|
#define CP0DB_IEXI 20
|
||||||
|
#define CP0DB_VER 15
|
||||||
|
#define CP0DB_DEC 10
|
||||||
|
#define CP0DB_SSt 8
|
||||||
|
#define CP0DB_DINT 5
|
||||||
|
#define CP0DB_DIB 4
|
||||||
|
#define CP0DB_DDBS 3
|
||||||
|
#define CP0DB_DDBL 2
|
||||||
|
#define CP0DB_DBp 1
|
||||||
|
#define CP0DB_DSS 0
|
||||||
|
uint32_t CP0_DEPC;
|
||||||
|
uint32_t CP0_TagLo;
|
||||||
|
uint32_t CP0_DataLo;
|
||||||
|
uint32_t CP0_ErrorEPC;
|
||||||
|
uint32_t CP0_DESAVE;
|
||||||
|
/* Qemu */
|
||||||
|
#if defined (USE_HOST_FLOAT_REGS) && defined(MIPS_USES_FPU)
|
||||||
|
double ft0, ft1, ft2;
|
||||||
|
#endif
|
||||||
|
struct QEMUTimer *timer; /* Internal timer */
|
||||||
|
int interrupt_request;
|
||||||
|
jmp_buf jmp_env;
|
||||||
|
int exception_index;
|
||||||
|
int error_code;
|
||||||
|
int user_mode_only; /* user mode only simulation */
|
||||||
|
uint32_t hflags; /* CPU State */
|
||||||
|
/* TMASK defines different execution modes */
|
||||||
|
#define MIPS_HFLAGS_TMASK 0x00FF
|
||||||
|
#define MIPS_HFLAG_MODE 0x001F /* execution modes */
|
||||||
|
#define MIPS_HFLAG_UM 0x0001 /* user mode */
|
||||||
|
#define MIPS_HFLAG_ERL 0x0002 /* Error mode */
|
||||||
|
#define MIPS_HFLAG_EXL 0x0004 /* Exception mode */
|
||||||
|
#define MIPS_HFLAG_DM 0x0008 /* Debug mode */
|
||||||
|
#define MIPS_HFLAG_SM 0x0010 /* Supervisor mode */
|
||||||
|
#define MIPS_HFLAG_RE 0x0040 /* Reversed endianness */
|
||||||
|
#define MIPS_HFLAG_DS 0x0080 /* In / out of delay slot */
|
||||||
|
/* Those flags keep the branch state if the translation is interrupted
|
||||||
|
* between the branch instruction and the delay slot
|
||||||
|
*/
|
||||||
|
#define MIPS_HFLAG_BMASK 0x0F00
|
||||||
|
#define MIPS_HFLAG_B 0x0100 /* Unconditional branch */
|
||||||
|
#define MIPS_HFLAG_BC 0x0200 /* Conditional branch */
|
||||||
|
#define MIPS_HFLAG_BL 0x0400 /* Likely branch */
|
||||||
|
#define MIPS_HFLAG_BR 0x0800 /* branch to register (can't link TB) */
|
||||||
|
target_ulong btarget; /* Jump / branch target */
|
||||||
|
int bcond; /* Branch condition (if needed) */
|
||||||
|
struct TranslationBlock *current_tb; /* currently executing TB */
|
||||||
|
/* soft mmu support */
|
||||||
|
/* in order to avoid passing too many arguments to the memory
|
||||||
|
write helpers, we store some rarely used information in the CPU
|
||||||
|
context) */
|
||||||
|
target_ulong mem_write_pc; /* host pc at which the memory was
|
||||||
|
written */
|
||||||
|
unsigned long mem_write_vaddr; /* target virtual addr at which the
|
||||||
|
memory was written */
|
||||||
|
/* 0 = kernel, 1 = user (may have 2 = kernel code, 3 = user code ?) */
|
||||||
|
CPUTLBEntry tlb_read[2][CPU_TLB_SIZE];
|
||||||
|
CPUTLBEntry tlb_write[2][CPU_TLB_SIZE];
|
||||||
|
/* ice debug support */
|
||||||
|
target_ulong breakpoints[MAX_BREAKPOINTS];
|
||||||
|
int nb_breakpoints;
|
||||||
|
int singlestep_enabled; /* XXX: should use CPU single step mode instead */
|
||||||
|
/* user data */
|
||||||
|
void *opaque;
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "cpu-all.h"
|
||||||
|
|
||||||
|
/* Memory access type :
|
||||||
|
* may be needed for precise access rights control and precise exceptions.
|
||||||
|
*/
|
||||||
|
enum {
|
||||||
|
/* 1 bit to define user level / supervisor access */
|
||||||
|
ACCESS_USER = 0x00,
|
||||||
|
ACCESS_SUPER = 0x01,
|
||||||
|
/* 1 bit to indicate direction */
|
||||||
|
ACCESS_STORE = 0x02,
|
||||||
|
/* Type of instruction that generated the access */
|
||||||
|
ACCESS_CODE = 0x10, /* Code fetch access */
|
||||||
|
ACCESS_INT = 0x20, /* Integer load/store access */
|
||||||
|
ACCESS_FLOAT = 0x30, /* floating point load/store access */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Exceptions */
|
||||||
|
enum {
|
||||||
|
EXCP_NONE = -1,
|
||||||
|
EXCP_RESET = 0,
|
||||||
|
EXCP_SRESET,
|
||||||
|
EXCP_DSS,
|
||||||
|
EXCP_DINT,
|
||||||
|
EXCP_NMI,
|
||||||
|
EXCP_MCHECK,
|
||||||
|
EXCP_EXT_INTERRUPT,
|
||||||
|
EXCP_DFWATCH,
|
||||||
|
EXCP_DIB, /* 8 */
|
||||||
|
EXCP_IWATCH,
|
||||||
|
EXCP_AdEL,
|
||||||
|
EXCP_AdES,
|
||||||
|
EXCP_TLBF,
|
||||||
|
EXCP_IBE,
|
||||||
|
EXCP_DBp,
|
||||||
|
EXCP_SYSCALL,
|
||||||
|
EXCP_BREAK,
|
||||||
|
EXCP_CpU, /* 16 */
|
||||||
|
EXCP_RI,
|
||||||
|
EXCP_OVERFLOW,
|
||||||
|
EXCP_TRAP,
|
||||||
|
EXCP_DDBS,
|
||||||
|
EXCP_DWATCH,
|
||||||
|
EXCP_LAE, /* 22 */
|
||||||
|
EXCP_SAE,
|
||||||
|
EXCP_LTLBL,
|
||||||
|
EXCP_TLBL,
|
||||||
|
EXCP_TLBS,
|
||||||
|
EXCP_DBE,
|
||||||
|
EXCP_DDBL,
|
||||||
|
EXCP_MTCP0 = 0x104, /* mtmsr instruction: */
|
||||||
|
/* may change privilege level */
|
||||||
|
EXCP_BRANCH = 0x108, /* branch instruction */
|
||||||
|
EXCP_ERET = 0x10C, /* return from interrupt */
|
||||||
|
EXCP_SYSCALL_USER = 0x110, /* System call in user mode only */
|
||||||
|
EXCP_FLUSH = 0x109,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* MIPS opcodes */
|
||||||
|
#define EXT_SPECIAL 0x100
|
||||||
|
#define EXT_SPECIAL2 0x200
|
||||||
|
#define EXT_REGIMM 0x300
|
||||||
|
#define EXT_CP0 0x400
|
||||||
|
#define EXT_CP1 0x500
|
||||||
|
#define EXT_CP2 0x600
|
||||||
|
#define EXT_CP3 0x700
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* indirect opcode tables */
|
||||||
|
OPC_SPECIAL = 0x00,
|
||||||
|
OPC_BREGIMM = 0x01,
|
||||||
|
OPC_CP0 = 0x10,
|
||||||
|
OPC_CP1 = 0x11,
|
||||||
|
OPC_CP2 = 0x12,
|
||||||
|
OPC_CP3 = 0x13,
|
||||||
|
OPC_SPECIAL2 = 0x1C,
|
||||||
|
/* arithmetic with immediate */
|
||||||
|
OPC_ADDI = 0x08,
|
||||||
|
OPC_ADDIU = 0x09,
|
||||||
|
OPC_SLTI = 0x0A,
|
||||||
|
OPC_SLTIU = 0x0B,
|
||||||
|
OPC_ANDI = 0x0C,
|
||||||
|
OPC_ORI = 0x0D,
|
||||||
|
OPC_XORI = 0x0E,
|
||||||
|
OPC_LUI = 0x0F,
|
||||||
|
/* Jump and branches */
|
||||||
|
OPC_J = 0x02,
|
||||||
|
OPC_JAL = 0x03,
|
||||||
|
OPC_BEQ = 0x04, /* Unconditional if rs = rt = 0 (B) */
|
||||||
|
OPC_BEQL = 0x14,
|
||||||
|
OPC_BNE = 0x05,
|
||||||
|
OPC_BNEL = 0x15,
|
||||||
|
OPC_BLEZ = 0x06,
|
||||||
|
OPC_BLEZL = 0x16,
|
||||||
|
OPC_BGTZ = 0x07,
|
||||||
|
OPC_BGTZL = 0x17,
|
||||||
|
OPC_JALX = 0x1D, /* MIPS 16 only */
|
||||||
|
/* Load and stores */
|
||||||
|
OPC_LB = 0x20,
|
||||||
|
OPC_LH = 0x21,
|
||||||
|
OPC_LWL = 0x22,
|
||||||
|
OPC_LW = 0x23,
|
||||||
|
OPC_LBU = 0x24,
|
||||||
|
OPC_LHU = 0x25,
|
||||||
|
OPC_LWR = 0x26,
|
||||||
|
OPC_SB = 0x28,
|
||||||
|
OPC_SH = 0x29,
|
||||||
|
OPC_SWL = 0x2A,
|
||||||
|
OPC_SW = 0x2B,
|
||||||
|
OPC_SWR = 0x2E,
|
||||||
|
OPC_LL = 0x30,
|
||||||
|
OPC_SC = 0x38,
|
||||||
|
/* Floating point load/store */
|
||||||
|
OPC_LWC1 = 0x31,
|
||||||
|
OPC_LWC2 = 0x32,
|
||||||
|
OPC_LDC1 = 0x35,
|
||||||
|
OPC_LDC2 = 0x36,
|
||||||
|
OPC_SWC1 = 0x39,
|
||||||
|
OPC_SWC2 = 0x3A,
|
||||||
|
OPC_SDC1 = 0x3D,
|
||||||
|
OPC_SDC2 = 0x3E,
|
||||||
|
/* Cache and prefetch */
|
||||||
|
OPC_CACHE = 0x2F,
|
||||||
|
OPC_PREF = 0x33,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* MIPS special opcodes */
|
||||||
|
enum {
|
||||||
|
/* Shifts */
|
||||||
|
OPC_SLL = 0x00 | EXT_SPECIAL,
|
||||||
|
/* NOP is SLL r0, r0, 0 */
|
||||||
|
/* SSNOP is SLL r0, r0, 1 */
|
||||||
|
OPC_SRL = 0x02 | EXT_SPECIAL,
|
||||||
|
OPC_SRA = 0x03 | EXT_SPECIAL,
|
||||||
|
OPC_SLLV = 0x04 | EXT_SPECIAL,
|
||||||
|
OPC_SRLV = 0x06 | EXT_SPECIAL,
|
||||||
|
OPC_SRAV = 0x07 | EXT_SPECIAL,
|
||||||
|
/* Multiplication / division */
|
||||||
|
OPC_MULT = 0x18 | EXT_SPECIAL,
|
||||||
|
OPC_MULTU = 0x19 | EXT_SPECIAL,
|
||||||
|
OPC_DIV = 0x1A | EXT_SPECIAL,
|
||||||
|
OPC_DIVU = 0x1B | EXT_SPECIAL,
|
||||||
|
/* 2 registers arithmetic / logic */
|
||||||
|
OPC_ADD = 0x20 | EXT_SPECIAL,
|
||||||
|
OPC_ADDU = 0x21 | EXT_SPECIAL,
|
||||||
|
OPC_SUB = 0x22 | EXT_SPECIAL,
|
||||||
|
OPC_SUBU = 0x23 | EXT_SPECIAL,
|
||||||
|
OPC_AND = 0x24 | EXT_SPECIAL,
|
||||||
|
OPC_OR = 0x25 | EXT_SPECIAL,
|
||||||
|
OPC_XOR = 0x26 | EXT_SPECIAL,
|
||||||
|
OPC_NOR = 0x27 | EXT_SPECIAL,
|
||||||
|
OPC_SLT = 0x2A | EXT_SPECIAL,
|
||||||
|
OPC_SLTU = 0x2B | EXT_SPECIAL,
|
||||||
|
/* Jumps */
|
||||||
|
OPC_JR = 0x08 | EXT_SPECIAL,
|
||||||
|
OPC_JALR = 0x09 | EXT_SPECIAL,
|
||||||
|
/* Traps */
|
||||||
|
OPC_TGE = 0x30 | EXT_SPECIAL,
|
||||||
|
OPC_TGEU = 0x31 | EXT_SPECIAL,
|
||||||
|
OPC_TLT = 0x32 | EXT_SPECIAL,
|
||||||
|
OPC_TLTU = 0x33 | EXT_SPECIAL,
|
||||||
|
OPC_TEQ = 0x34 | EXT_SPECIAL,
|
||||||
|
OPC_TNE = 0x36 | EXT_SPECIAL,
|
||||||
|
/* HI / LO registers load & stores */
|
||||||
|
OPC_MFHI = 0x10 | EXT_SPECIAL,
|
||||||
|
OPC_MTHI = 0x11 | EXT_SPECIAL,
|
||||||
|
OPC_MFLO = 0x12 | EXT_SPECIAL,
|
||||||
|
OPC_MTLO = 0x13 | EXT_SPECIAL,
|
||||||
|
/* Conditional moves */
|
||||||
|
OPC_MOVZ = 0x0A | EXT_SPECIAL,
|
||||||
|
OPC_MOVN = 0x0B | EXT_SPECIAL,
|
||||||
|
|
||||||
|
OPC_MOVCI = 0x01 | EXT_SPECIAL,
|
||||||
|
|
||||||
|
/* Special */
|
||||||
|
OPC_PMON = 0x05 | EXT_SPECIAL,
|
||||||
|
OPC_SYSCALL = 0x0C | EXT_SPECIAL,
|
||||||
|
OPC_BREAK = 0x0D | EXT_SPECIAL,
|
||||||
|
OPC_SYNC = 0x0F | EXT_SPECIAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* Mutiply & xxx operations */
|
||||||
|
OPC_MADD = 0x00 | EXT_SPECIAL2,
|
||||||
|
OPC_MADDU = 0x01 | EXT_SPECIAL2,
|
||||||
|
OPC_MUL = 0x02 | EXT_SPECIAL2,
|
||||||
|
OPC_MSUB = 0x04 | EXT_SPECIAL2,
|
||||||
|
OPC_MSUBU = 0x05 | EXT_SPECIAL2,
|
||||||
|
/* Misc */
|
||||||
|
OPC_CLZ = 0x20 | EXT_SPECIAL2,
|
||||||
|
OPC_CLO = 0x21 | EXT_SPECIAL2,
|
||||||
|
/* Special */
|
||||||
|
OPC_SDBBP = 0x3F | EXT_SPECIAL2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Branch REGIMM */
|
||||||
|
enum {
|
||||||
|
OPC_BLTZ = 0x00 | EXT_REGIMM,
|
||||||
|
OPC_BLTZL = 0x02 | EXT_REGIMM,
|
||||||
|
OPC_BGEZ = 0x01 | EXT_REGIMM,
|
||||||
|
OPC_BGEZL = 0x03 | EXT_REGIMM,
|
||||||
|
OPC_BLTZAL = 0x10 | EXT_REGIMM,
|
||||||
|
OPC_BLTZALL = 0x12 | EXT_REGIMM,
|
||||||
|
OPC_BGEZAL = 0x11 | EXT_REGIMM,
|
||||||
|
OPC_BGEZALL = 0x13 | EXT_REGIMM,
|
||||||
|
OPC_TGEI = 0x08 | EXT_REGIMM,
|
||||||
|
OPC_TGEIU = 0x09 | EXT_REGIMM,
|
||||||
|
OPC_TLTI = 0x0A | EXT_REGIMM,
|
||||||
|
OPC_TLTIU = 0x0B | EXT_REGIMM,
|
||||||
|
OPC_TEQI = 0x0C | EXT_REGIMM,
|
||||||
|
OPC_TNEI = 0x0E | EXT_REGIMM,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
/* Coprocessor 0 (MMU) */
|
||||||
|
OPC_MFC0 = 0x00 | EXT_CP0,
|
||||||
|
OPC_MTC0 = 0x04 | EXT_CP0,
|
||||||
|
OPC_TLBR = 0x01 | EXT_CP0,
|
||||||
|
OPC_TLBWI = 0x02 | EXT_CP0,
|
||||||
|
OPC_TLBWR = 0x06 | EXT_CP0,
|
||||||
|
OPC_TLBP = 0x08 | EXT_CP0,
|
||||||
|
OPC_ERET = 0x18 | EXT_CP0,
|
||||||
|
OPC_DERET = 0x1F | EXT_CP0,
|
||||||
|
OPC_WAIT = 0x20 | EXT_CP0,
|
||||||
|
};
|
||||||
|
|
||||||
|
int cpu_mips_exec(CPUMIPSState *s);
|
||||||
|
CPUMIPSState *cpu_mips_init(void);
|
||||||
|
uint32_t cpu_mips_get_clock (void);
|
||||||
|
|
||||||
|
#endif /* !defined (__MIPS_CPU_H__) */
|
183
target-mips/exec.h
Normal file
183
target-mips/exec.h
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
#if !defined(__QEMU_MIPS_EXEC_H__)
|
||||||
|
#define __QEMU_MIPS_EXEC_H__
|
||||||
|
|
||||||
|
#define DEBUG_OP
|
||||||
|
|
||||||
|
#include "mips-defs.h"
|
||||||
|
#include "dyngen-exec.h"
|
||||||
|
|
||||||
|
register struct CPUMIPSState *env asm(AREG0);
|
||||||
|
|
||||||
|
#if defined (USE_64BITS_REGS)
|
||||||
|
typedef int64_t host_int_t;
|
||||||
|
typedef uint64_t host_uint_t;
|
||||||
|
#else
|
||||||
|
typedef int32_t host_int_t;
|
||||||
|
typedef uint32_t host_uint_t;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
register host_uint_t T0 asm(AREG1);
|
||||||
|
register host_uint_t T1 asm(AREG2);
|
||||||
|
register host_uint_t T2 asm(AREG3);
|
||||||
|
register host_int_t Ts0 asm(AREG1);
|
||||||
|
register host_int_t Ts1 asm(AREG2);
|
||||||
|
register host_int_t Ts2 asm(AREG3);
|
||||||
|
|
||||||
|
#define PARAM(n) ((uint32_t)PARAM##n)
|
||||||
|
#define SPARAM(n) ((int32_t)PARAM##n)
|
||||||
|
|
||||||
|
#if defined (USE_HOST_FLOAT_REGS)
|
||||||
|
register double FT0 asm(FREG0);
|
||||||
|
register double FT1 asm(FREG1);
|
||||||
|
register double FT2 asm(FREG2);
|
||||||
|
register float FTS0 asm(FREG0);
|
||||||
|
register float FTS1 asm(FREG1);
|
||||||
|
register float FTS2 asm(FREG2);
|
||||||
|
#else
|
||||||
|
#define FT0 (env->ft0.d)
|
||||||
|
#define FT1 (env->ft1.d)
|
||||||
|
#define FT2 (env->ft2.d)
|
||||||
|
#define FTS0 (env->ft0.f)
|
||||||
|
#define FTS1 (env->ft1.f)
|
||||||
|
#define FTS2 (env->ft2.f)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
#define RETURN() __asm__ __volatile__("nop");
|
||||||
|
#else
|
||||||
|
#define RETURN() __asm__ __volatile__("");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "cpu.h"
|
||||||
|
#include "exec-all.h"
|
||||||
|
|
||||||
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
|
||||||
|
#define ldul_user ldl_user
|
||||||
|
#define ldul_kernel ldl_kernel
|
||||||
|
|
||||||
|
#define ACCESS_TYPE 0
|
||||||
|
#define MEMSUFFIX _kernel
|
||||||
|
#define DATA_SIZE 1
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 2
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 4
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 8
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
#undef ACCESS_TYPE
|
||||||
|
#undef MEMSUFFIX
|
||||||
|
|
||||||
|
#define ACCESS_TYPE 1
|
||||||
|
#define MEMSUFFIX _user
|
||||||
|
#define DATA_SIZE 1
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 2
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 4
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 8
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
#undef ACCESS_TYPE
|
||||||
|
#undef MEMSUFFIX
|
||||||
|
|
||||||
|
/* these access are slower, they must be as rare as possible */
|
||||||
|
#define ACCESS_TYPE 2
|
||||||
|
#define MEMSUFFIX _data
|
||||||
|
#define DATA_SIZE 1
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 2
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 4
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
|
||||||
|
#define DATA_SIZE 8
|
||||||
|
#include "softmmu_header.h"
|
||||||
|
#undef ACCESS_TYPE
|
||||||
|
#undef MEMSUFFIX
|
||||||
|
|
||||||
|
#define ldub(p) ldub_data(p)
|
||||||
|
#define ldsb(p) ldsb_data(p)
|
||||||
|
#define lduw(p) lduw_data(p)
|
||||||
|
#define ldsw(p) ldsw_data(p)
|
||||||
|
#define ldl(p) ldl_data(p)
|
||||||
|
#define ldq(p) ldq_data(p)
|
||||||
|
|
||||||
|
#define stb(p, v) stb_data(p, v)
|
||||||
|
#define stw(p, v) stw_data(p, v)
|
||||||
|
#define stl(p, v) stl_data(p, v)
|
||||||
|
#define stq(p, v) stq_data(p, v)
|
||||||
|
|
||||||
|
#endif /* !defined(CONFIG_USER_ONLY) */
|
||||||
|
|
||||||
|
static inline void env_to_regs(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void regs_to_env(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (HOST_LONG_BITS == 32)
|
||||||
|
void do_mult (void);
|
||||||
|
void do_multu (void);
|
||||||
|
void do_madd (void);
|
||||||
|
void do_maddu (void);
|
||||||
|
void do_msub (void);
|
||||||
|
void do_msubu (void);
|
||||||
|
#endif
|
||||||
|
__attribute__ (( regparm(2) ))
|
||||||
|
void do_mfc0(int reg, int sel);
|
||||||
|
__attribute__ (( regparm(2) ))
|
||||||
|
void do_mtc0(int reg, int sel);
|
||||||
|
void do_tlbwi (void);
|
||||||
|
void do_tlbwr (void);
|
||||||
|
void do_tlbp (void);
|
||||||
|
void do_tlbr (void);
|
||||||
|
void do_lwl_raw (void);
|
||||||
|
void do_lwr_raw (void);
|
||||||
|
void do_swl_raw (void);
|
||||||
|
void do_swr_raw (void);
|
||||||
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
void do_lwl_user (void);
|
||||||
|
void do_lwl_kernel (void);
|
||||||
|
void do_lwr_user (void);
|
||||||
|
void do_lwr_kernel (void);
|
||||||
|
void do_swl_user (void);
|
||||||
|
void do_swl_kernel (void);
|
||||||
|
void do_swr_user (void);
|
||||||
|
void do_swr_kernel (void);
|
||||||
|
#endif
|
||||||
|
__attribute__ (( regparm(1) ))
|
||||||
|
void do_pmon (int function);
|
||||||
|
|
||||||
|
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
|
||||||
|
int is_user, int is_softmmu);
|
||||||
|
void do_interrupt (CPUState *env);
|
||||||
|
|
||||||
|
void cpu_loop_exit(void);
|
||||||
|
__attribute__ (( regparm(2) ))
|
||||||
|
void do_raise_exception_err (uint32_t exception, int error_code);
|
||||||
|
__attribute__ (( regparm(1) ))
|
||||||
|
void do_raise_exception (uint32_t exception);
|
||||||
|
|
||||||
|
void cpu_dump_state(CPUState *env, FILE *f,
|
||||||
|
int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
|
||||||
|
int flags);
|
||||||
|
void cpu_mips_irqctrl_init (void);
|
||||||
|
uint32_t cpu_mips_get_random (CPUState *env);
|
||||||
|
uint32_t cpu_mips_get_count (CPUState *env);
|
||||||
|
void cpu_mips_store_count (CPUState *env, uint32_t value);
|
||||||
|
void cpu_mips_store_compare (CPUState *env, uint32_t value);
|
||||||
|
void cpu_mips_clock_init (CPUState *env);
|
||||||
|
|
||||||
|
#endif /* !defined(__QEMU_MIPS_EXEC_H__) */
|
461
target-mips/helper.c
Normal file
461
target-mips/helper.c
Normal file
|
@ -0,0 +1,461 @@
|
||||||
|
/*
|
||||||
|
* MIPS emulation helpers for qemu.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2005 Jocelyn Mayer
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "exec.h"
|
||||||
|
|
||||||
|
/* MIPS32 4K MMU emulation */
|
||||||
|
#if MIPS_USES_4K_TLB
|
||||||
|
static int map_address (CPUState *env, target_ulong *physical, int *prot,
|
||||||
|
target_ulong address, int rw, int access_type)
|
||||||
|
{
|
||||||
|
tlb_t *tlb;
|
||||||
|
target_ulong tag;
|
||||||
|
uint8_t ASID;
|
||||||
|
int i, n;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = -2;
|
||||||
|
tag = (address & 0xFFFFE000);
|
||||||
|
ASID = env->CP0_EntryHi & 0x000000FF;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
tlb = &env->tlb[i];
|
||||||
|
/* Check ASID, virtual page number & size */
|
||||||
|
if ((tlb->G == 1 || tlb->ASID == ASID) &&
|
||||||
|
tlb->VPN == tag && address < tlb->end) {
|
||||||
|
/* TLB match */
|
||||||
|
n = (address >> 12) & 1;
|
||||||
|
/* Check access rights */
|
||||||
|
if ((tlb->V[n] & 2) && (rw == 0 || (tlb->D[n] & 4))) {
|
||||||
|
*physical = tlb->PFN[n] | (address & 0xFFF);
|
||||||
|
*prot = PROT_READ;
|
||||||
|
if (tlb->D[n])
|
||||||
|
*prot |= PROT_WRITE;
|
||||||
|
return 0;
|
||||||
|
} else if (!(tlb->V[n] & 2)) {
|
||||||
|
return -3;
|
||||||
|
} else {
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int get_physical_address (CPUState *env, target_ulong *physical, int *prot,
|
||||||
|
target_ulong address, int rw, int access_type)
|
||||||
|
{
|
||||||
|
int user_mode;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* User mode can only access useg */
|
||||||
|
user_mode = ((env->hflags & MIPS_HFLAG_MODE) == MIPS_HFLAG_UM) ? 1 : 0;
|
||||||
|
#if 0
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "user mode %d h %08x\n",
|
||||||
|
user_mode, env->hflags);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (user_mode && address > 0x7FFFFFFFUL)
|
||||||
|
return -1;
|
||||||
|
ret = 0;
|
||||||
|
if (address < 0x80000000UL) {
|
||||||
|
if (user_mode || !(env->hflags & MIPS_HFLAG_ERL)) {
|
||||||
|
#if MIPS_USES_4K_TLB
|
||||||
|
ret = map_address(env, physical, prot, address, rw);
|
||||||
|
#else
|
||||||
|
*physical = address + 0x40000000UL;
|
||||||
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
*physical = address;
|
||||||
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
|
}
|
||||||
|
} else if (address < 0xA0000000UL) {
|
||||||
|
/* kseg0 */
|
||||||
|
/* XXX: check supervisor mode */
|
||||||
|
*physical = address - 0x80000000UL;
|
||||||
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
|
} else if (address < 0xC0000000UL) {
|
||||||
|
/* kseg1 */
|
||||||
|
/* XXX: check supervisor mode */
|
||||||
|
*physical = address - 0xA0000000UL;
|
||||||
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
|
} else if (address < 0xE0000000UL) {
|
||||||
|
/* kseg2 */
|
||||||
|
#if MIPS_USES_4K_TLB
|
||||||
|
ret = map_address(env, physical, prot, address, rw);
|
||||||
|
#else
|
||||||
|
*physical = address;
|
||||||
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
/* kseg3 */
|
||||||
|
/* XXX: check supervisor mode */
|
||||||
|
/* XXX: debug segment is not emulated */
|
||||||
|
#if MIPS_USES_4K_TLB
|
||||||
|
ret = map_address(env, physical, prot, address, rw);
|
||||||
|
#else
|
||||||
|
*physical = address;
|
||||||
|
*prot = PAGE_READ | PAGE_WRITE;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "%08x %d %d => %08x %d (%d)\n", address, rw,
|
||||||
|
access_type, *physical, *prot, ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(CONFIG_USER_ONLY)
|
||||||
|
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
|
||||||
|
{
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
|
||||||
|
{
|
||||||
|
target_ulong phys_addr;
|
||||||
|
int prot;
|
||||||
|
|
||||||
|
if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
|
||||||
|
return -1;
|
||||||
|
return phys_addr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
|
||||||
|
#define MMUSUFFIX _mmu
|
||||||
|
#define GETPC() (__builtin_return_address(0))
|
||||||
|
|
||||||
|
#define SHIFT 0
|
||||||
|
#include "softmmu_template.h"
|
||||||
|
|
||||||
|
#define SHIFT 1
|
||||||
|
#include "softmmu_template.h"
|
||||||
|
|
||||||
|
#define SHIFT 2
|
||||||
|
#include "softmmu_template.h"
|
||||||
|
|
||||||
|
#define SHIFT 3
|
||||||
|
#include "softmmu_template.h"
|
||||||
|
|
||||||
|
void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
|
||||||
|
{
|
||||||
|
TranslationBlock *tb;
|
||||||
|
CPUState *saved_env;
|
||||||
|
unsigned long pc;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* XXX: hack to restore env in all cases, even if not called from
|
||||||
|
generated code */
|
||||||
|
saved_env = env;
|
||||||
|
env = cpu_single_env;
|
||||||
|
ret = cpu_mips_handle_mmu_fault(env, addr, is_write, is_user, 1);
|
||||||
|
if (ret) {
|
||||||
|
if (retaddr) {
|
||||||
|
/* now we have a real cpu fault */
|
||||||
|
pc = (unsigned long)retaddr;
|
||||||
|
tb = tb_find_pc(pc);
|
||||||
|
if (tb) {
|
||||||
|
/* the PC is inside the translated code. It means that we have
|
||||||
|
a virtual CPU fault */
|
||||||
|
cpu_restore_state(tb, env, pc, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
do_raise_exception_err(env->exception_index, env->error_code);
|
||||||
|
}
|
||||||
|
env = saved_env;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cpu_mips_init_mmu (CPUState *env)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* !defined(CONFIG_USER_ONLY) */
|
||||||
|
|
||||||
|
int cpu_mips_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
|
||||||
|
int is_user, int is_softmmu)
|
||||||
|
{
|
||||||
|
target_ulong physical;
|
||||||
|
int prot;
|
||||||
|
int exception = 0, error_code = 0;
|
||||||
|
int access_type;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (logfile) {
|
||||||
|
cpu_dump_state(env, logfile, fprintf, 0);
|
||||||
|
fprintf(logfile, "%s pc %08x ad %08x rw %d is_user %d smmu %d\n",
|
||||||
|
__func__, env->PC, address, rw, is_user, is_softmmu);
|
||||||
|
}
|
||||||
|
/* data access */
|
||||||
|
/* XXX: put correct access by using cpu_restore_state()
|
||||||
|
correctly */
|
||||||
|
access_type = ACCESS_INT;
|
||||||
|
if (env->user_mode_only) {
|
||||||
|
/* user mode only emulation */
|
||||||
|
ret = -2;
|
||||||
|
goto do_fault;
|
||||||
|
}
|
||||||
|
ret = get_physical_address(env, &physical, &prot,
|
||||||
|
address, rw, access_type);
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "%s address=%08x ret %d physical %08x prot %d\n",
|
||||||
|
__func__, address, ret, physical, prot);
|
||||||
|
}
|
||||||
|
if (ret == 0) {
|
||||||
|
ret = tlb_set_page(env, address & ~0xFFF, physical & ~0xFFF, prot,
|
||||||
|
is_user, is_softmmu);
|
||||||
|
} else if (ret < 0) {
|
||||||
|
do_fault:
|
||||||
|
switch (ret) {
|
||||||
|
default:
|
||||||
|
case -1:
|
||||||
|
/* Reference to kernel address from user mode or supervisor mode */
|
||||||
|
/* Reference to supervisor address from user mode */
|
||||||
|
if (rw)
|
||||||
|
exception = EXCP_AdES;
|
||||||
|
else
|
||||||
|
exception = EXCP_AdEL;
|
||||||
|
break;
|
||||||
|
case -2:
|
||||||
|
/* No TLB match for a mapped address */
|
||||||
|
if (rw)
|
||||||
|
exception = EXCP_TLBS;
|
||||||
|
else
|
||||||
|
exception = EXCP_TLBL;
|
||||||
|
error_code = 1;
|
||||||
|
break;
|
||||||
|
case -3:
|
||||||
|
/* TLB match with no valid bit */
|
||||||
|
if (rw)
|
||||||
|
exception = EXCP_TLBS;
|
||||||
|
else
|
||||||
|
exception = EXCP_TLBL;
|
||||||
|
error_code = 0;
|
||||||
|
break;
|
||||||
|
case -4:
|
||||||
|
/* TLB match but 'D' bit is cleared */
|
||||||
|
exception = EXCP_LTLBL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (ret == -2) {
|
||||||
|
exception = EXCP_AdEL;
|
||||||
|
}
|
||||||
|
/* Raise exception */
|
||||||
|
env->CP0_BadVAddr = address;
|
||||||
|
env->CP0_Context =
|
||||||
|
(env->CP0_Context & 0x00000FFF) | (address & 0xFFFFF000);
|
||||||
|
env->CP0_EntryHi =
|
||||||
|
(env->CP0_EntryHi & 0x00000FFF) | (address & 0xFFFFF000);
|
||||||
|
env->exception_index = exception;
|
||||||
|
env->error_code = error_code;
|
||||||
|
ret = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_interrupt (CPUState *env)
|
||||||
|
{
|
||||||
|
target_ulong pc, offset;
|
||||||
|
int cause = -1;
|
||||||
|
|
||||||
|
if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) {
|
||||||
|
fprintf(logfile, "%s enter: PC %08x EPC %08x cause %d excp %d\n",
|
||||||
|
__func__, env->PC, env->CP0_EPC, cause, env->exception_index);
|
||||||
|
}
|
||||||
|
if (env->exception_index == EXCP_EXT_INTERRUPT &&
|
||||||
|
(env->hflags & MIPS_HFLAG_DM))
|
||||||
|
env->exception_index = EXCP_DINT;
|
||||||
|
offset = 0x180;
|
||||||
|
switch (env->exception_index) {
|
||||||
|
case EXCP_DSS:
|
||||||
|
env->CP0_Debug |= 1 << CP0DB_DSS;
|
||||||
|
/* Debug single step cannot be raised inside a delay slot and
|
||||||
|
* resume will always occur on the next instruction
|
||||||
|
* (but we assume the pc has always been updated during
|
||||||
|
* code translation).
|
||||||
|
*/
|
||||||
|
env->CP0_DEPC = env->PC;
|
||||||
|
goto enter_debug_mode;
|
||||||
|
case EXCP_DINT:
|
||||||
|
env->CP0_Debug |= 1 << CP0DB_DINT;
|
||||||
|
goto set_DEPC;
|
||||||
|
case EXCP_DIB:
|
||||||
|
env->CP0_Debug |= 1 << CP0DB_DIB;
|
||||||
|
goto set_DEPC;
|
||||||
|
case EXCP_DBp:
|
||||||
|
env->CP0_Debug |= 1 << CP0DB_DBp;
|
||||||
|
goto set_DEPC;
|
||||||
|
case EXCP_DDBS:
|
||||||
|
env->CP0_Debug |= 1 << CP0DB_DDBS;
|
||||||
|
goto set_DEPC;
|
||||||
|
case EXCP_DDBL:
|
||||||
|
env->CP0_Debug |= 1 << CP0DB_DDBL;
|
||||||
|
goto set_DEPC;
|
||||||
|
set_DEPC:
|
||||||
|
if (env->hflags & MIPS_HFLAG_DS) {
|
||||||
|
/* If the exception was raised from a delay slot,
|
||||||
|
* come back to the jump
|
||||||
|
*/
|
||||||
|
env->CP0_DEPC = env->PC - 4;
|
||||||
|
} else {
|
||||||
|
env->CP0_DEPC = env->PC;
|
||||||
|
}
|
||||||
|
enter_debug_mode:
|
||||||
|
env->hflags |= MIPS_HFLAG_DM;
|
||||||
|
/* EJTAG probe trap enable is not implemented... */
|
||||||
|
pc = 0xBFC00480;
|
||||||
|
break;
|
||||||
|
case EXCP_RESET:
|
||||||
|
#if defined (MIPS_USES_R4K_TLB)
|
||||||
|
env->CP0_random = MIPS_TLB_NB - 1;
|
||||||
|
#endif
|
||||||
|
env->CP0_Wired = 0;
|
||||||
|
env->CP0_Config0 = MIPS_CONFIG0;
|
||||||
|
#if defined (MIPS_CONFIG1)
|
||||||
|
env->CP0_Config1 = MIPS_CONFIG1;
|
||||||
|
#endif
|
||||||
|
#if defined (MIPS_CONFIG2)
|
||||||
|
env->CP0_Config2 = MIPS_CONFIG2;
|
||||||
|
#endif
|
||||||
|
#if defined (MIPS_CONFIG3)
|
||||||
|
env->CP0_Config3 = MIPS_CONFIG3;
|
||||||
|
#endif
|
||||||
|
env->CP0_WatchLo = 0;
|
||||||
|
env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV);
|
||||||
|
goto set_error_EPC;
|
||||||
|
case EXCP_SRESET:
|
||||||
|
env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) |
|
||||||
|
(1 << CP0St_SR);
|
||||||
|
env->CP0_WatchLo = 0;
|
||||||
|
goto set_error_EPC;
|
||||||
|
case EXCP_NMI:
|
||||||
|
env->CP0_Status = (1 << CP0St_CU0) | (1 << CP0St_BEV) |
|
||||||
|
(1 << CP0St_NMI);
|
||||||
|
set_error_EPC:
|
||||||
|
env->hflags = MIPS_HFLAG_ERL;
|
||||||
|
if (env->hflags & MIPS_HFLAG_DS) {
|
||||||
|
/* If the exception was raised from a delay slot,
|
||||||
|
* come back to the jump
|
||||||
|
*/
|
||||||
|
env->CP0_ErrorEPC = env->PC - 4;
|
||||||
|
} else {
|
||||||
|
env->CP0_ErrorEPC = env->PC;
|
||||||
|
}
|
||||||
|
pc = 0xBFC00000;
|
||||||
|
break;
|
||||||
|
case EXCP_MCHECK:
|
||||||
|
cause = 24;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_EXT_INTERRUPT:
|
||||||
|
cause = 0;
|
||||||
|
if (env->CP0_Cause & (1 << CP0Ca_IV))
|
||||||
|
offset = 0x200;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_DWATCH:
|
||||||
|
cause = 23;
|
||||||
|
/* XXX: TODO: manage defered watch exceptions */
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_AdEL:
|
||||||
|
case EXCP_AdES:
|
||||||
|
cause = 4;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_TLBL:
|
||||||
|
case EXCP_TLBF:
|
||||||
|
cause = 2;
|
||||||
|
if (env->error_code == 1 && !(env->hflags & MIPS_HFLAG_EXL))
|
||||||
|
offset = 0x000;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_IBE:
|
||||||
|
cause = 6;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_DBE:
|
||||||
|
cause = 7;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_SYSCALL:
|
||||||
|
cause = 8;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_BREAK:
|
||||||
|
cause = 9;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_RI:
|
||||||
|
cause = 10;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_CpU:
|
||||||
|
cause = 11;
|
||||||
|
/* XXX: fill in the faulty unit number */
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_OVERFLOW:
|
||||||
|
cause = 12;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_TRAP:
|
||||||
|
cause = 13;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_LTLBL:
|
||||||
|
cause = 1;
|
||||||
|
goto set_EPC;
|
||||||
|
case EXCP_TLBS:
|
||||||
|
cause = 3;
|
||||||
|
set_EPC:
|
||||||
|
if (env->CP0_Status & (1 << CP0St_BEV)) {
|
||||||
|
pc = 0xBFC00200;
|
||||||
|
} else {
|
||||||
|
pc = 0x80000000;
|
||||||
|
}
|
||||||
|
env->hflags |= MIPS_HFLAG_EXL;
|
||||||
|
pc += offset;
|
||||||
|
env->CP0_Cause = (env->CP0_Cause & ~0x7C) | (cause << 2);
|
||||||
|
if (env->hflags & MIPS_HFLAG_DS) {
|
||||||
|
/* If the exception was raised from a delay slot,
|
||||||
|
* come back to the jump
|
||||||
|
*/
|
||||||
|
env->CP0_EPC = env->PC - 4;
|
||||||
|
env->CP0_Cause |= 0x80000000;
|
||||||
|
} else {
|
||||||
|
env->CP0_EPC = env->PC;
|
||||||
|
env->CP0_Cause &= ~0x80000000;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "Invalid MIPS exception %d. Exiting\n",
|
||||||
|
env->exception_index);
|
||||||
|
}
|
||||||
|
printf("Invalid MIPS exception %d. Exiting\n", env->exception_index);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
env->PC = pc;
|
||||||
|
if (logfile && env->exception_index != EXCP_EXT_INTERRUPT) {
|
||||||
|
fprintf(logfile, "%s: PC %08x EPC %08x cause %d excp %d\n"
|
||||||
|
" S %08x C %08x A %08x D %08x\n",
|
||||||
|
__func__, env->PC, env->CP0_EPC, cause, env->exception_index,
|
||||||
|
env->CP0_Status, env->CP0_Cause, env->CP0_BadVAddr,
|
||||||
|
env->CP0_DEPC);
|
||||||
|
}
|
||||||
|
env->exception_index = EXCP_NONE;
|
||||||
|
}
|
58
target-mips/mips-defs.h
Normal file
58
target-mips/mips-defs.h
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
#if !defined (__QEMU_MIPS_DEFS_H__)
|
||||||
|
#define __QEMU_MIPS_DEFS_H__
|
||||||
|
|
||||||
|
/* If we want to use 64 bits host regs... */
|
||||||
|
//#define USE_64BITS_REGS
|
||||||
|
/* If we want to use host float regs... */
|
||||||
|
//#define USE_HOST_FLOAT_REGS
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MIPS_R4Kc = 0x00018000,
|
||||||
|
MIPS_R4Kp = 0x00018300,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Emulate MIPS R4Kc for now */
|
||||||
|
#define MIPS_CPU MIPS_R4Kc
|
||||||
|
|
||||||
|
#if (MIPS_CPU == MIPS_R4Kc)
|
||||||
|
/* 32 bits target */
|
||||||
|
#define TARGET_LONG_BITS 32
|
||||||
|
/* real pages are variable size... */
|
||||||
|
#define TARGET_PAGE_BITS 12
|
||||||
|
/* Uses MIPS R4Kx ehancements to MIPS32 architecture */
|
||||||
|
#define MIPS_USES_R4K_EXT
|
||||||
|
/* Uses MIPS R4Kc TLB model */
|
||||||
|
#define MIPS_USES_R4K_TLB
|
||||||
|
#define MIPS_TLB_NB 16
|
||||||
|
/* Have config1, runs in big-endian mode, uses TLB */
|
||||||
|
#define MIPS_CONFIG0 \
|
||||||
|
((1 << CP0C0_M) | (0x000 << CP0C0_K23) | (0x000 << CP0C0_KU) | \
|
||||||
|
(1 << CP0C0_BE) | (0x001 << CP0C0_MT) | (0x010 << CP0C0_K0))
|
||||||
|
/* 16 TLBs, 64 sets Icache, 16 bytes Icache line, 2-way Icache,
|
||||||
|
* 64 sets Dcache, 16 bytes Dcache line, 2-way Dcache,
|
||||||
|
* no performance counters, watch registers present, no code compression,
|
||||||
|
* EJTAG present, no FPU
|
||||||
|
*/
|
||||||
|
#define MIPS_CONFIG1 \
|
||||||
|
((15 << CP0C1_MMU) | \
|
||||||
|
(0x000 << CP0C1_IS) | (0x3 << CP0C1_IL) | (0x01 << CP0C1_IA) | \
|
||||||
|
(0x000 << CP0C1_DS) | (0x3 << CP0C1_DL) | (0x01 << CP0C1_DA) | \
|
||||||
|
(0 << CP0C1_PC) | (1 << CP0C1_WR) | (0 << CP0C1_CA) | \
|
||||||
|
(1 << CP0C1_EP) | (0 << CP0C1_FP))
|
||||||
|
#elif defined (MIPS_CPU == MIPS_R4Kp)
|
||||||
|
/* 32 bits target */
|
||||||
|
#define TARGET_LONG_BITS 32
|
||||||
|
/* real pages are variable size... */
|
||||||
|
#define TARGET_PAGE_BITS 12
|
||||||
|
/* Uses MIPS R4Kx ehancements to MIPS32 architecture */
|
||||||
|
#define MIPS_USES_R4K_EXT
|
||||||
|
/* Uses MIPS R4Km FPM MMU model */
|
||||||
|
#define MIPS_USES_R4K_FPM
|
||||||
|
#else
|
||||||
|
#error "MIPS CPU not defined"
|
||||||
|
/* Remainder for other flags */
|
||||||
|
//#define TARGET_MIPS64
|
||||||
|
//define MIPS_USES_FPU
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* !defined (__QEMU_MIPS_DEFS_H__) */
|
641
target-mips/op.c
Normal file
641
target-mips/op.c
Normal file
|
@ -0,0 +1,641 @@
|
||||||
|
/*
|
||||||
|
* MIPS emulation micro-operations for qemu.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2005 Jocelyn Mayer
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "exec.h"
|
||||||
|
|
||||||
|
#define REG 1
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 2
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 3
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 4
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 5
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 6
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 7
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 8
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 9
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 10
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 11
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 12
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 13
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 14
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 15
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 16
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 17
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 18
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 19
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 20
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 21
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 22
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 23
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 24
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 25
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 26
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 27
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 28
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 29
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 30
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
#define REG 31
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef REG
|
||||||
|
|
||||||
|
#define TN T0
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef TN
|
||||||
|
#define TN T1
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef TN
|
||||||
|
#define TN T2
|
||||||
|
#include "op_template.c"
|
||||||
|
#undef TN
|
||||||
|
|
||||||
|
void op_dup_T0 (void)
|
||||||
|
{
|
||||||
|
T2 = T0;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_load_HI (void)
|
||||||
|
{
|
||||||
|
T0 = env->HI;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_store_HI (void)
|
||||||
|
{
|
||||||
|
env->HI = T0;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_load_LO (void)
|
||||||
|
{
|
||||||
|
T0 = env->LO;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_store_LO (void)
|
||||||
|
{
|
||||||
|
env->LO = T0;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load and store */
|
||||||
|
#define MEMSUFFIX _raw
|
||||||
|
#include "op_mem.c"
|
||||||
|
#undef MEMSUFFIX
|
||||||
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
#define MEMSUFFIX _user
|
||||||
|
#include "op_mem.c"
|
||||||
|
#undef MEMSUFFIX
|
||||||
|
|
||||||
|
#define MEMSUFFIX _kernel
|
||||||
|
#include "op_mem.c"
|
||||||
|
#undef MEMSUFFIX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Arithmetic */
|
||||||
|
void op_add (void)
|
||||||
|
{
|
||||||
|
T0 += T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_addo (void)
|
||||||
|
{
|
||||||
|
target_ulong tmp;
|
||||||
|
|
||||||
|
tmp = T0;
|
||||||
|
T0 += T1;
|
||||||
|
if ((T0 >> 31) ^ (T1 >> 31) ^ (tmp >> 31)) {
|
||||||
|
CALL_FROM_TB1(do_raise_exception, EXCP_OVERFLOW);
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_sub (void)
|
||||||
|
{
|
||||||
|
T0 -= T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_subo (void)
|
||||||
|
{
|
||||||
|
target_ulong tmp;
|
||||||
|
|
||||||
|
tmp = T0;
|
||||||
|
T0 = (int32_t)T0 - (int32_t)T1;
|
||||||
|
if (!((T0 >> 31) ^ (T1 >> 31) ^ (tmp >> 31))) {
|
||||||
|
CALL_FROM_TB1(do_raise_exception, EXCP_OVERFLOW);
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_mul (void)
|
||||||
|
{
|
||||||
|
T0 = (int32_t)T0 * (int32_t)T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_div (void)
|
||||||
|
{
|
||||||
|
if (T1 != 0) {
|
||||||
|
env->LO = (int32_t)T0 / (int32_t)T1;
|
||||||
|
env->HI = (int32_t)T0 % (int32_t)T1;
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_divu (void)
|
||||||
|
{
|
||||||
|
if (T1 != 0) {
|
||||||
|
env->LO = T0 / T1;
|
||||||
|
env->HI = T0 % T1;
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Logical */
|
||||||
|
void op_and (void)
|
||||||
|
{
|
||||||
|
T0 &= T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_nor (void)
|
||||||
|
{
|
||||||
|
T0 = ~(T0 | T1);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_or (void)
|
||||||
|
{
|
||||||
|
T0 |= T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_xor (void)
|
||||||
|
{
|
||||||
|
T0 ^= T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_sll (void)
|
||||||
|
{
|
||||||
|
T0 = T0 << T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_sra (void)
|
||||||
|
{
|
||||||
|
T0 = (int32_t)T0 >> T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_srl (void)
|
||||||
|
{
|
||||||
|
T0 = T0 >> T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_sllv (void)
|
||||||
|
{
|
||||||
|
T0 = T1 << (T0 & 0x1F);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_srav (void)
|
||||||
|
{
|
||||||
|
T0 = (int32_t)T1 >> (T0 & 0x1F);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_srlv (void)
|
||||||
|
{
|
||||||
|
T0 = T1 >> (T0 & 0x1F);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_clo (void)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (T0 == (target_ulong)-1) {
|
||||||
|
T0 = 32;
|
||||||
|
} else {
|
||||||
|
for (n = 0; n < 32; n++) {
|
||||||
|
if (!(T0 & (1 << 31)))
|
||||||
|
break;
|
||||||
|
T0 = T0 << 1;
|
||||||
|
}
|
||||||
|
T0 = n;
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_clz (void)
|
||||||
|
{
|
||||||
|
int n;
|
||||||
|
|
||||||
|
if (T0 == 0) {
|
||||||
|
T0 = 32;
|
||||||
|
} else {
|
||||||
|
for (n = 0; n < 32; n++) {
|
||||||
|
if (T0 & (1 << 31))
|
||||||
|
break;
|
||||||
|
T0 = T0 << 1;
|
||||||
|
}
|
||||||
|
T0 = n;
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 64 bits arithmetic */
|
||||||
|
#if (HOST_LONG_BITS == 64)
|
||||||
|
static inline uint64_t get_HILO (void)
|
||||||
|
{
|
||||||
|
return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_HILO (uint64_t HILO)
|
||||||
|
{
|
||||||
|
env->LO = HILO & 0xFFFFFFFF;
|
||||||
|
env->HI = HILO >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_mult (void)
|
||||||
|
{
|
||||||
|
set_HILO((int64_t)T0 * (int64_t)T1);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_multu (void)
|
||||||
|
{
|
||||||
|
set_HILO((uint64_t)T0 * (uint64_t)T1);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_madd (void)
|
||||||
|
{
|
||||||
|
int64_t tmp;
|
||||||
|
|
||||||
|
tmp = ((int64_t)T0 * (int64_t)T1);
|
||||||
|
set_HILO((int64_t)get_HILO() + tmp);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_maddu (void)
|
||||||
|
{
|
||||||
|
uint64_t tmp;
|
||||||
|
|
||||||
|
tmp = ((uint64_t)T0 * (uint64_t)T1);
|
||||||
|
set_HILO(get_HILO() + tmp);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_msub (void)
|
||||||
|
{
|
||||||
|
int64_t tmp;
|
||||||
|
|
||||||
|
tmp = ((int64_t)T0 * (int64_t)T1);
|
||||||
|
set_HILO((int64_t)get_HILO() - tmp);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_msubu (void)
|
||||||
|
{
|
||||||
|
uint64_t tmp;
|
||||||
|
|
||||||
|
tmp = ((uint64_t)T0 * (uint64_t)T1);
|
||||||
|
set_HILO(get_HILO() - tmp);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void op_mult (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_mult);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_multu (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_multu);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_madd (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_madd);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_maddu (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_maddu);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_msub (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_msub);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_msubu (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_msubu);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Conditional moves */
|
||||||
|
void op_movn (void)
|
||||||
|
{
|
||||||
|
if (T1 != 0)
|
||||||
|
env->gpr[PARAM1] = T0;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_movz (void)
|
||||||
|
{
|
||||||
|
if (T1 == 0)
|
||||||
|
env->gpr[PARAM1] = T0;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tests */
|
||||||
|
#define OP_COND(name, cond) \
|
||||||
|
void glue(op_, name) (void) \
|
||||||
|
{ \
|
||||||
|
if (cond) { \
|
||||||
|
T0 = 1; \
|
||||||
|
} else { \
|
||||||
|
T0 = 0; \
|
||||||
|
} \
|
||||||
|
RETURN(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
OP_COND(eq, T0 == T1);
|
||||||
|
OP_COND(ne, T0 != T1);
|
||||||
|
OP_COND(ge, (int32_t)T0 >= (int32_t)T1);
|
||||||
|
OP_COND(geu, T0 >= T1);
|
||||||
|
OP_COND(lt, (int32_t)T0 < (int32_t)T1);
|
||||||
|
OP_COND(ltu, T0 < T1);
|
||||||
|
OP_COND(gez, (int32_t)T0 >= 0);
|
||||||
|
OP_COND(gtz, (int32_t)T0 > 0);
|
||||||
|
OP_COND(lez, (int32_t)T0 <= 0);
|
||||||
|
OP_COND(ltz, (int32_t)T0 < 0);
|
||||||
|
|
||||||
|
/* Branchs */
|
||||||
|
//#undef USE_DIRECT_JUMP
|
||||||
|
#define EIP env->PC
|
||||||
|
|
||||||
|
/* Branch to register */
|
||||||
|
void op_save_breg_target (void)
|
||||||
|
{
|
||||||
|
env->btarget = T2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_restore_breg_target (void)
|
||||||
|
{
|
||||||
|
T2 = env->btarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_breg (void)
|
||||||
|
{
|
||||||
|
env->PC = T2;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unconditional branch */
|
||||||
|
void op_branch (void)
|
||||||
|
{
|
||||||
|
JUMP_TB(branch, PARAM1, 0, PARAM2);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_save_btarget (void)
|
||||||
|
{
|
||||||
|
env->btarget = PARAM1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Conditional branch */
|
||||||
|
void op_set_bcond (void)
|
||||||
|
{
|
||||||
|
T2 = T0;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_save_bcond (void)
|
||||||
|
{
|
||||||
|
env->bcond = T2;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_restore_bcond (void)
|
||||||
|
{
|
||||||
|
T2 = env->bcond;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_bcond (void)
|
||||||
|
{
|
||||||
|
if (T2) {
|
||||||
|
JUMP_TB(bcond, PARAM1, 0, PARAM2);
|
||||||
|
} else {
|
||||||
|
JUMP_TB(bcond, PARAM1, 1, PARAM3);
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Likely branch (used to skip the delay slot) */
|
||||||
|
void op_blikely (void)
|
||||||
|
{
|
||||||
|
/* If the test is false, skip the delay slot */
|
||||||
|
if (T2 == 0) {
|
||||||
|
env->hflags = PARAM3;
|
||||||
|
JUMP_TB(blikely, PARAM1, 1, PARAM2);
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CP0 functions */
|
||||||
|
void op_mfc0 (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB2(do_mfc0, PARAM1, PARAM2);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_mtc0 (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB2(do_mtc0, PARAM1, PARAM2);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(MIPS_USES_R4K_TLB)
|
||||||
|
void op_tlbwi (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_tlbwi);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_tlbwr (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_tlbwr);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_tlbp (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_tlbp);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_tlbr (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(do_tlbr);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Specials */
|
||||||
|
void op_pmon (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB1(do_pmon, PARAM1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_trap (void)
|
||||||
|
{
|
||||||
|
if (T0) {
|
||||||
|
CALL_FROM_TB1(do_raise_exception, EXCP_TRAP);
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_set_lladdr (void)
|
||||||
|
{
|
||||||
|
env->CP0_LLAddr = T2;
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_eret (void);
|
||||||
|
void op_eret (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(debug_eret);
|
||||||
|
if (env->hflags & MIPS_HFLAG_ERL)
|
||||||
|
env->PC = env->CP0_ErrorEPC;
|
||||||
|
else
|
||||||
|
env->PC = env->CP0_EPC;
|
||||||
|
env->CP0_LLAddr = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_deret (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(debug_eret);
|
||||||
|
env->PC = env->CP0_DEPC;
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_save_state (void)
|
||||||
|
{
|
||||||
|
env->hflags = PARAM1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_save_pc (void)
|
||||||
|
{
|
||||||
|
env->PC = PARAM1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_raise_exception (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB1(do_raise_exception, PARAM1);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_raise_exception_err (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB2(do_raise_exception_err, PARAM1, PARAM2);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void op_exit_tb (void)
|
||||||
|
{
|
||||||
|
EXIT_TB();
|
||||||
|
}
|
||||||
|
|
634
target-mips/op_helper.c
Normal file
634
target-mips/op_helper.c
Normal file
|
@ -0,0 +1,634 @@
|
||||||
|
/*
|
||||||
|
* MIPS emulation helpers for qemu.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2005 Jocelyn Mayer
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
#include <math.h>
|
||||||
|
#include "exec.h"
|
||||||
|
|
||||||
|
#define MIPS_DEBUG_DISAS
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
/* Exceptions processing helpers */
|
||||||
|
void cpu_loop_exit(void)
|
||||||
|
{
|
||||||
|
longjmp(env->jmp_env, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ (( regparm(2) ))
|
||||||
|
void do_raise_exception_err (uint32_t exception, int error_code)
|
||||||
|
{
|
||||||
|
#if 1
|
||||||
|
if (logfile && exception < 0x100)
|
||||||
|
fprintf(logfile, "%s: %d %d\n", __func__, exception, error_code);
|
||||||
|
#endif
|
||||||
|
env->exception_index = exception;
|
||||||
|
env->error_code = error_code;
|
||||||
|
T0 = 0;
|
||||||
|
cpu_loop_exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ (( regparm(1) ))
|
||||||
|
void do_raise_exception (uint32_t exception)
|
||||||
|
{
|
||||||
|
do_raise_exception_err(exception, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MEMSUFFIX _raw
|
||||||
|
#include "op_helper_mem.c"
|
||||||
|
#undef MEMSUFFIX
|
||||||
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
|
#define MEMSUFFIX _user
|
||||||
|
#include "op_helper_mem.c"
|
||||||
|
#undef MEMSUFFIX
|
||||||
|
#define MEMSUFFIX _kernel
|
||||||
|
#include "op_helper_mem.c"
|
||||||
|
#undef MEMSUFFIX
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* 64 bits arithmetic for 32 bits hosts */
|
||||||
|
#if (HOST_LONG_BITS == 32)
|
||||||
|
static inline uint64_t get_HILO (void)
|
||||||
|
{
|
||||||
|
return ((uint64_t)env->HI << 32) | (uint64_t)env->LO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void set_HILO (uint64_t HILO)
|
||||||
|
{
|
||||||
|
env->LO = HILO & 0xFFFFFFFF;
|
||||||
|
env->HI = HILO >> 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_mult (void)
|
||||||
|
{
|
||||||
|
set_HILO((int64_t)T0 * (int64_t)T1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_multu (void)
|
||||||
|
{
|
||||||
|
set_HILO((uint64_t)T0 * (uint64_t)T1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_madd (void)
|
||||||
|
{
|
||||||
|
int64_t tmp;
|
||||||
|
|
||||||
|
tmp = ((int64_t)T0 * (int64_t)T1);
|
||||||
|
set_HILO((int64_t)get_HILO() + tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_maddu (void)
|
||||||
|
{
|
||||||
|
uint64_t tmp;
|
||||||
|
|
||||||
|
tmp = ((uint64_t)T0 * (uint64_t)T1);
|
||||||
|
set_HILO(get_HILO() + tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_msub (void)
|
||||||
|
{
|
||||||
|
int64_t tmp;
|
||||||
|
|
||||||
|
tmp = ((int64_t)T0 * (int64_t)T1);
|
||||||
|
set_HILO((int64_t)get_HILO() - tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_msubu (void)
|
||||||
|
{
|
||||||
|
uint64_t tmp;
|
||||||
|
|
||||||
|
tmp = ((uint64_t)T0 * (uint64_t)T1);
|
||||||
|
set_HILO(get_HILO() - tmp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* CP0 helpers */
|
||||||
|
__attribute__ (( regparm(2) ))
|
||||||
|
void do_mfc0 (int reg, int sel)
|
||||||
|
{
|
||||||
|
const unsigned char *rn;
|
||||||
|
|
||||||
|
if (sel != 0 && reg != 16 && reg != 28) {
|
||||||
|
rn = "invalid";
|
||||||
|
goto print;
|
||||||
|
}
|
||||||
|
switch (reg) {
|
||||||
|
case 0:
|
||||||
|
T0 = env->CP0_index;
|
||||||
|
rn = "Index";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
T0 = cpu_mips_get_random(env);
|
||||||
|
rn = "Random";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
T0 = env->CP0_EntryLo0;
|
||||||
|
rn = "EntryLo0";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
T0 = env->CP0_EntryLo1;
|
||||||
|
rn = "EntryLo1";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
T0 = env->CP0_Context;
|
||||||
|
rn = "Context";
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
T0 = env->CP0_PageMask;
|
||||||
|
rn = "PageMask";
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
T0 = env->CP0_Wired;
|
||||||
|
rn = "Wired";
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
T0 = env->CP0_BadVAddr;
|
||||||
|
rn = "BadVaddr";
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
T0 = cpu_mips_get_count(env);
|
||||||
|
rn = "Count";
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
T0 = env->CP0_EntryHi;
|
||||||
|
rn = "EntryHi";
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
T0 = env->CP0_Compare;
|
||||||
|
rn = "Compare";
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
T0 = env->CP0_Status;
|
||||||
|
if (env->hflags & MIPS_HFLAG_UM)
|
||||||
|
T0 |= CP0St_UM;
|
||||||
|
if (env->hflags & MIPS_HFLAG_ERL)
|
||||||
|
T0 |= CP0St_ERL;
|
||||||
|
if (env->hflags & MIPS_HFLAG_EXL)
|
||||||
|
T0 |= CP0St_EXL;
|
||||||
|
rn = "Status";
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
T0 = env->CP0_Cause;
|
||||||
|
rn = "Cause";
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
T0 = env->CP0_EPC;
|
||||||
|
rn = "EPC";
|
||||||
|
break;
|
||||||
|
case 15:
|
||||||
|
T0 = env->CP0_PRid;
|
||||||
|
rn = "PRid";
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
switch (sel) {
|
||||||
|
case 0:
|
||||||
|
T0 = env->CP0_Config0;
|
||||||
|
rn = "Config";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
T0 = env->CP0_Config1;
|
||||||
|
rn = "Config1";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rn = "Unknown config register";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
T0 = env->CP0_LLAddr >> 4;
|
||||||
|
rn = "LLAddr";
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
T0 = env->CP0_WatchLo;
|
||||||
|
rn = "WatchLo";
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
T0 = env->CP0_WatchHi;
|
||||||
|
rn = "WatchHi";
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
T0 = env->CP0_Debug;
|
||||||
|
if (env->hflags & MIPS_HFLAG_DM)
|
||||||
|
T0 |= 1 << CP0DB_DM;
|
||||||
|
rn = "Debug";
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
T0 = env->CP0_DEPC;
|
||||||
|
rn = "DEPC";
|
||||||
|
break;
|
||||||
|
case 28:
|
||||||
|
switch (sel) {
|
||||||
|
case 0:
|
||||||
|
T0 = env->CP0_TagLo;
|
||||||
|
rn = "TagLo";
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
T0 = env->CP0_DataLo;
|
||||||
|
rn = "DataLo";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rn = "unknown sel";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
T0 = env->CP0_ErrorEPC;
|
||||||
|
rn = "ErrorEPC";
|
||||||
|
break;
|
||||||
|
case 31:
|
||||||
|
T0 = env->CP0_DESAVE;
|
||||||
|
rn = "DESAVE";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
rn = "unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print:
|
||||||
|
#if defined MIPS_DEBUG_DISAS
|
||||||
|
if (loglevel & CPU_LOG_TB_IN_ASM) {
|
||||||
|
fprintf(logfile, "%08x mfc0 %s => %08x (%d %d)\n",
|
||||||
|
env->PC, rn, T0, reg, sel);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ (( regparm(2) ))
|
||||||
|
void do_mtc0 (int reg, int sel)
|
||||||
|
{
|
||||||
|
const unsigned char *rn;
|
||||||
|
uint32_t val, old, mask;
|
||||||
|
int i, raise;
|
||||||
|
|
||||||
|
if (sel != 0 && reg != 16 && reg != 28) {
|
||||||
|
val = -1;
|
||||||
|
old = -1;
|
||||||
|
rn = "invalid";
|
||||||
|
goto print;
|
||||||
|
}
|
||||||
|
switch (reg) {
|
||||||
|
case 0:
|
||||||
|
val = (env->CP0_index & 0x80000000) | (T0 & 0x0000000F);
|
||||||
|
old = env->CP0_index;
|
||||||
|
env->CP0_index = val;
|
||||||
|
rn = "Index";
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
val = T0 & 0x03FFFFFFF;
|
||||||
|
old = env->CP0_EntryLo0;
|
||||||
|
env->CP0_EntryLo0 = val;
|
||||||
|
rn = "EntryLo0";
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
val = T0 & 0x03FFFFFFF;
|
||||||
|
old = env->CP0_EntryLo1;
|
||||||
|
env->CP0_EntryLo1 = val;
|
||||||
|
rn = "EntryLo1";
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
val = (env->CP0_Context & 0xFF000000) | (T0 & 0x00FFFFF0);
|
||||||
|
old = env->CP0_Context;
|
||||||
|
env->CP0_Context = val;
|
||||||
|
rn = "Context";
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
val = T0 & 0x01FFE000;
|
||||||
|
old = env->CP0_PageMask;
|
||||||
|
env->CP0_PageMask = val;
|
||||||
|
rn = "PageMask";
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
val = T0 & 0x0000000F;
|
||||||
|
old = env->CP0_Wired;
|
||||||
|
env->CP0_Wired = val;
|
||||||
|
rn = "Wired";
|
||||||
|
break;
|
||||||
|
case 9:
|
||||||
|
val = T0;
|
||||||
|
old = cpu_mips_get_count(env);
|
||||||
|
cpu_mips_store_count(env, val);
|
||||||
|
rn = "Count";
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
val = T0 & 0xFFFFF0FF;
|
||||||
|
old = env->CP0_EntryHi;
|
||||||
|
env->CP0_EntryHi = val;
|
||||||
|
rn = "EntryHi";
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
val = T0;
|
||||||
|
old = env->CP0_Compare;
|
||||||
|
cpu_mips_store_compare(env, val);
|
||||||
|
rn = "Compare";
|
||||||
|
break;
|
||||||
|
case 12:
|
||||||
|
val = T0 & 0xFA78FF01;
|
||||||
|
if (T0 & (1 << CP0St_UM))
|
||||||
|
env->hflags |= MIPS_HFLAG_UM;
|
||||||
|
else
|
||||||
|
env->hflags &= ~MIPS_HFLAG_UM;
|
||||||
|
if (T0 & (1 << CP0St_ERL))
|
||||||
|
env->hflags |= MIPS_HFLAG_ERL;
|
||||||
|
else
|
||||||
|
env->hflags &= ~MIPS_HFLAG_ERL;
|
||||||
|
if (T0 & (1 << CP0St_EXL))
|
||||||
|
env->hflags |= MIPS_HFLAG_EXL;
|
||||||
|
else
|
||||||
|
env->hflags &= ~MIPS_HFLAG_EXL;
|
||||||
|
old = env->CP0_Status;
|
||||||
|
env->CP0_Status = val;
|
||||||
|
/* If we unmasked an asserted IRQ, raise it */
|
||||||
|
mask = 0x0000FC00;
|
||||||
|
if (loglevel & CPU_LOG_TB_IN_ASM) {
|
||||||
|
fprintf(logfile, "Status %08x => %08x Cause %08x (%08x %08x %08x)\n",
|
||||||
|
old, val, env->CP0_Cause, old & mask, val & mask,
|
||||||
|
env->CP0_Cause & mask);
|
||||||
|
}
|
||||||
|
#if 1
|
||||||
|
if ((val & (1 << CP0St_IE)) && !(old & (1 << CP0St_IE)) &&
|
||||||
|
!(env->hflags & MIPS_HFLAG_EXL) &&
|
||||||
|
!(env->hflags & MIPS_HFLAG_ERL) &&
|
||||||
|
!(env->hflags & MIPS_HFLAG_DM) &&
|
||||||
|
(env->CP0_Cause & mask)) {
|
||||||
|
if (logfile)
|
||||||
|
fprintf(logfile, "Raise pending IRQs\n");
|
||||||
|
env->interrupt_request |= CPU_INTERRUPT_HARD;
|
||||||
|
do_raise_exception(EXCP_EXT_INTERRUPT);
|
||||||
|
} else if (!(val & 0x00000001) && (old & 0x00000001)) {
|
||||||
|
env->interrupt_request &= ~CPU_INTERRUPT_HARD;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
rn = "Status";
|
||||||
|
break;
|
||||||
|
case 13:
|
||||||
|
val = (env->CP0_Cause & 0xB000F87C) | (T0 & 0x000C00300);
|
||||||
|
old = env->CP0_Cause;
|
||||||
|
env->CP0_Cause = val;
|
||||||
|
#if 0
|
||||||
|
/* Check if we ever asserted a software IRQ */
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
mask = 0x100 << i;
|
||||||
|
if ((val & mask) & !(old & mask))
|
||||||
|
mips_set_irq(i);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
rn = "Cause";
|
||||||
|
break;
|
||||||
|
case 14:
|
||||||
|
val = T0;
|
||||||
|
old = env->CP0_EPC;
|
||||||
|
env->CP0_EPC = val;
|
||||||
|
rn = "EPC";
|
||||||
|
break;
|
||||||
|
case 16:
|
||||||
|
switch (sel) {
|
||||||
|
case 0:
|
||||||
|
#if defined(MIPS_USES_R4K_TLB)
|
||||||
|
val = (env->CP0_Config0 & 0x8017FF80) | (T0 & 0x7E000001);
|
||||||
|
#else
|
||||||
|
val = (env->CP0_Config0 & 0xFE17FF80) | (T0 & 0x00000001);
|
||||||
|
#endif
|
||||||
|
old = env->CP0_Config0;
|
||||||
|
env->CP0_Config0 = val;
|
||||||
|
rn = "Config0";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
val = -1;
|
||||||
|
old = -1;
|
||||||
|
rn = "bad config selector";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 18:
|
||||||
|
val = T0;
|
||||||
|
old = env->CP0_WatchLo;
|
||||||
|
env->CP0_WatchLo = val;
|
||||||
|
rn = "WatchLo";
|
||||||
|
break;
|
||||||
|
case 19:
|
||||||
|
val = T0 & 0x40FF0FF8;
|
||||||
|
old = env->CP0_WatchHi;
|
||||||
|
env->CP0_WatchHi = val;
|
||||||
|
rn = "WatchHi";
|
||||||
|
break;
|
||||||
|
case 23:
|
||||||
|
val = (env->CP0_Debug & 0x8C03FC1F) | (T0 & 0x13300120);
|
||||||
|
if (T0 & (1 << CP0DB_DM))
|
||||||
|
env->hflags |= MIPS_HFLAG_DM;
|
||||||
|
else
|
||||||
|
env->hflags &= ~MIPS_HFLAG_DM;
|
||||||
|
old = env->CP0_Debug;
|
||||||
|
env->CP0_Debug = val;
|
||||||
|
rn = "Debug";
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
val = T0;
|
||||||
|
old = env->CP0_DEPC;
|
||||||
|
env->CP0_DEPC = val;
|
||||||
|
rn = "DEPC";
|
||||||
|
break;
|
||||||
|
case 28:
|
||||||
|
switch (sel) {
|
||||||
|
case 0:
|
||||||
|
val = T0 & 0xFFFFFCF6;
|
||||||
|
old = env->CP0_TagLo;
|
||||||
|
env->CP0_TagLo = val;
|
||||||
|
rn = "TagLo";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
val = -1;
|
||||||
|
old = -1;
|
||||||
|
rn = "invalid sel";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 30:
|
||||||
|
val = T0;
|
||||||
|
old = env->CP0_ErrorEPC;
|
||||||
|
env->CP0_ErrorEPC = val;
|
||||||
|
rn = "EPC";
|
||||||
|
break;
|
||||||
|
case 31:
|
||||||
|
val = T0;
|
||||||
|
old = env->CP0_DESAVE;
|
||||||
|
env->CP0_DESAVE = val;
|
||||||
|
rn = "DESAVE";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
val = -1;
|
||||||
|
old = -1;
|
||||||
|
rn = "unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
print:
|
||||||
|
#if defined MIPS_DEBUG_DISAS
|
||||||
|
if (loglevel & CPU_LOG_TB_IN_ASM) {
|
||||||
|
fprintf(logfile, "%08x mtc0 %s %08x => %08x (%d %d %08x)\n",
|
||||||
|
env->PC, rn, T0, val, reg, sel, old);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TLB management */
|
||||||
|
#if defined(MIPS_USES_R4K_TLB)
|
||||||
|
__attribute__ (( regparm(1) ))
|
||||||
|
static void invalidate_tb (int idx)
|
||||||
|
{
|
||||||
|
tlb_t *tlb;
|
||||||
|
target_ulong addr, end;
|
||||||
|
|
||||||
|
tlb = &env->tlb[idx];
|
||||||
|
if (tlb->V[0]) {
|
||||||
|
addr = tlb->PFN[0];
|
||||||
|
end = addr + (tlb->end - tlb->VPN);
|
||||||
|
tb_invalidate_page_range(addr, end);
|
||||||
|
}
|
||||||
|
if (tlb->V[1]) {
|
||||||
|
addr = tlb->PFN[1];
|
||||||
|
end = addr + (tlb->end - tlb->VPN);
|
||||||
|
tb_invalidate_page_range(addr, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ (( regparm(1) ))
|
||||||
|
static void fill_tb (int idx)
|
||||||
|
{
|
||||||
|
tlb_t *tlb;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
/* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
|
||||||
|
tlb = &env->tlb[idx];
|
||||||
|
tlb->VPN = env->CP0_EntryHi & 0xFFFFE000;
|
||||||
|
tlb->ASID = env->CP0_EntryHi & 0x000000FF;
|
||||||
|
size = env->CP0_PageMask >> 13;
|
||||||
|
size = 4 * (size + 1);
|
||||||
|
tlb->end = tlb->VPN + (1 << (8 + size));
|
||||||
|
tlb->G = env->CP0_EntryLo0 & env->CP0_EntryLo1 & 1;
|
||||||
|
tlb->V[0] = env->CP0_EntryLo0 & 2;
|
||||||
|
tlb->D[0] = env->CP0_EntryLo0 & 4;
|
||||||
|
tlb->C[0] = (env->CP0_EntryLo0 >> 3) & 0x7;
|
||||||
|
tlb->PFN[0] = (env->CP0_EntryLo0 >> 6) << 12;
|
||||||
|
tlb->V[1] = env->CP0_EntryLo1 & 2;
|
||||||
|
tlb->D[1] = env->CP0_EntryLo1 & 4;
|
||||||
|
tlb->C[1] = (env->CP0_EntryLo1 >> 3) & 0x7;
|
||||||
|
tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_tlbwi (void)
|
||||||
|
{
|
||||||
|
invalidate_tb(env->CP0_index & 0xF);
|
||||||
|
fill_tb(env->CP0_index & 0xF);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_tlbwr (void)
|
||||||
|
{
|
||||||
|
int r = cpu_mips_get_random(env);
|
||||||
|
|
||||||
|
invalidate_tb(r);
|
||||||
|
fill_tb(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_tlbp (void)
|
||||||
|
{
|
||||||
|
tlb_t *tlb;
|
||||||
|
target_ulong tag;
|
||||||
|
uint8_t ASID;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
tag = (env->CP0_EntryHi & 0xFFFFE000);
|
||||||
|
ASID = env->CP0_EntryHi & 0x000000FF;
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
tlb = &env->tlb[i];
|
||||||
|
/* Check ASID, virtual page number & size */
|
||||||
|
if ((tlb->G == 1 || tlb->ASID == ASID) && tlb->VPN == tag) {
|
||||||
|
/* TLB match */
|
||||||
|
env->CP0_index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (i == 16) {
|
||||||
|
env->CP0_index |= 0x80000000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void do_tlbr (void)
|
||||||
|
{
|
||||||
|
tlb_t *tlb;
|
||||||
|
int size;
|
||||||
|
|
||||||
|
tlb = &env->tlb[env->CP0_index & 0xF];
|
||||||
|
env->CP0_EntryHi = tlb->VPN | tlb->ASID;
|
||||||
|
size = (tlb->end - tlb->VPN) >> 12;
|
||||||
|
env->CP0_PageMask = (size - 1) << 13;
|
||||||
|
env->CP0_EntryLo0 = tlb->V[0] | tlb->D[0] | (tlb->C[0] << 3) |
|
||||||
|
(tlb->PFN[0] >> 6);
|
||||||
|
env->CP0_EntryLo1 = tlb->V[1] | tlb->D[1] | (tlb->C[1] << 3) |
|
||||||
|
(tlb->PFN[1] >> 6);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
__attribute__ (( regparm(1) ))
|
||||||
|
void op_dump_ldst (const unsigned char *func)
|
||||||
|
{
|
||||||
|
if (loglevel)
|
||||||
|
fprintf(logfile, "%s => %08x %08x\n", __func__, T0, T1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump_sc (void)
|
||||||
|
{
|
||||||
|
if (loglevel) {
|
||||||
|
fprintf(logfile, "%s %08x at %08x (%08x)\n", __func__,
|
||||||
|
T1, T0, env->CP0_LLAddr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void debug_eret (void)
|
||||||
|
{
|
||||||
|
if (loglevel) {
|
||||||
|
fprintf(logfile, "ERET: pc %08x EPC %08x ErrorEPC %08x (%d)\n",
|
||||||
|
env->PC, env->CP0_EPC, env->CP0_ErrorEPC,
|
||||||
|
env->hflags & MIPS_HFLAG_ERL ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__ (( regparm(1) ))
|
||||||
|
void do_pmon (int function)
|
||||||
|
{
|
||||||
|
function /= 2;
|
||||||
|
switch (function) {
|
||||||
|
case 2: /* TODO: char inbyte(int waitflag); */
|
||||||
|
if (env->gpr[4] == 0)
|
||||||
|
env->gpr[2] = -1;
|
||||||
|
/* Fall through */
|
||||||
|
case 11: /* TODO: char inbyte (void); */
|
||||||
|
env->gpr[2] = -1;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 12:
|
||||||
|
printf("%c", env->gpr[4] & 0xFF);
|
||||||
|
break;
|
||||||
|
case 17:
|
||||||
|
break;
|
||||||
|
case 158:
|
||||||
|
{
|
||||||
|
unsigned char *fmt = (void *)env->gpr[4];
|
||||||
|
printf("%s", fmt);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
143
target-mips/op_helper_mem.c
Normal file
143
target-mips/op_helper_mem.c
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
void glue(do_lwl, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
target_ulong sav = T0;
|
||||||
|
#endif
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
tmp = glue(ldl, MEMSUFFIX)(T0 & ~3);
|
||||||
|
/* XXX: this is valid only in big-endian mode
|
||||||
|
* should be reverted for little-endian...
|
||||||
|
*/
|
||||||
|
switch (T0 & 3) {
|
||||||
|
case 0:
|
||||||
|
T0 = tmp;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
T0 = (tmp << 8) | (T1 & 0x000000FF);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
T0 = (tmp << 16) | (T1 & 0x0000FFFF);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
T0 = (tmp << 24) | (T1 & 0x00FFFFFF);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "%s: %08x - %08x %08x => %08x\n",
|
||||||
|
__func__, sav, tmp, T1, T0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(do_lwr, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
target_ulong sav = T0;
|
||||||
|
#endif
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
tmp = glue(ldl, MEMSUFFIX)(T0 & ~3);
|
||||||
|
/* XXX: this is valid only in big-endian mode
|
||||||
|
* should be reverted for little-endian...
|
||||||
|
*/
|
||||||
|
switch (T0 & 3) {
|
||||||
|
case 0:
|
||||||
|
T0 = (tmp >> 24) | (T1 & 0xFFFFFF00);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
T0 = (tmp >> 16) | (T1 & 0xFFFF0000);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
T0 = (tmp >> 8) | (T1 & 0xFF000000);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
T0 = tmp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "%s: %08x - %08x %08x => %08x\n",
|
||||||
|
__func__, sav, tmp, T1, T0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(do_swl, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
target_ulong sav;
|
||||||
|
#endif
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
tmp = glue(ldl, MEMSUFFIX)(T0 & ~3);
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
sav = tmp;
|
||||||
|
#endif
|
||||||
|
/* XXX: this is valid only in big-endian mode
|
||||||
|
* should be reverted for little-endian...
|
||||||
|
*/
|
||||||
|
switch (T0 & 3) {
|
||||||
|
case 0:
|
||||||
|
tmp = T1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
tmp = (tmp & 0xFF000000) | (T1 >> 8);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tmp = (tmp & 0xFFFF0000) | (T1 >> 16);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
tmp = (tmp & 0xFFFFFF00) | (T1 >> 24);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
glue(stl, MEMSUFFIX)(T0 & ~3, tmp);
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "%s: %08x - %08x %08x => %08x\n",
|
||||||
|
__func__, T0, sav, T1, tmp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(do_swr, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
target_ulong sav;
|
||||||
|
#endif
|
||||||
|
uint32_t tmp;
|
||||||
|
|
||||||
|
tmp = glue(ldl, MEMSUFFIX)(T0 & ~3);
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
sav = tmp;
|
||||||
|
#endif
|
||||||
|
/* XXX: this is valid only in big-endian mode
|
||||||
|
* should be reverted for little-endian...
|
||||||
|
*/
|
||||||
|
switch (T0 & 3) {
|
||||||
|
case 0:
|
||||||
|
tmp = (tmp & 0x00FFFFFF) | (T1 << 24);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
tmp = (tmp & 0x0000FFFF) | (T1 << 16);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
tmp = (tmp & 0x000000FF) | (T1 << 8);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
tmp = T1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
glue(stl, MEMSUFFIX)(T0 & ~3, tmp);
|
||||||
|
#if defined (DEBUG_OP)
|
||||||
|
if (logfile) {
|
||||||
|
fprintf(logfile, "%s: %08x - %08x %08x => %08x\n",
|
||||||
|
__func__, T0, sav, T1, tmp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
RETURN();
|
||||||
|
}
|
113
target-mips/op_mem.c
Normal file
113
target-mips/op_mem.c
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* MIPS emulation memory micro-operations for qemu.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2005 Jocelyn Mayer
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Standard loads and stores */
|
||||||
|
void glue(op_lb, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
T0 = glue(ldsb, MEMSUFFIX)(T0);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_lbu, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
T0 = glue(ldub, MEMSUFFIX)(T0);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_sb, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
glue(stb, MEMSUFFIX)(T0, T1);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_lh, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
T0 = glue(ldsw, MEMSUFFIX)(T0);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_lhu, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
T0 = glue(lduw, MEMSUFFIX)(T0);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_sh, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
glue(stw, MEMSUFFIX)(T0, T1);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_lw, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
T0 = glue(ldl, MEMSUFFIX)(T0);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_sw, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
glue(stl, MEMSUFFIX)(T0, T1);
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "half" load and stores */
|
||||||
|
void glue(op_lwl, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(glue(do_lwl, MEMSUFFIX));
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_lwr, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(glue(do_lwr, MEMSUFFIX));
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_swl, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(glue(do_swl, MEMSUFFIX));
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_swr, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(glue(do_swr, MEMSUFFIX));
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_ll, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
T1 = T0;
|
||||||
|
T0 = glue(ldl, MEMSUFFIX)(T0);
|
||||||
|
env->CP0_LLAddr = T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_sc, MEMSUFFIX) (void)
|
||||||
|
{
|
||||||
|
CALL_FROM_TB0(dump_sc);
|
||||||
|
if (T0 == env->CP0_LLAddr) {
|
||||||
|
glue(stl, MEMSUFFIX)(T0, T1);
|
||||||
|
T0 = 1;
|
||||||
|
} else {
|
||||||
|
T0 = 0;
|
||||||
|
}
|
||||||
|
RETURN();
|
||||||
|
}
|
65
target-mips/op_template.c
Normal file
65
target-mips/op_template.c
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* MIPS emulation micro-operations templates for reg load & store for qemu.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2004-2005 Jocelyn Mayer
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if defined(REG)
|
||||||
|
void glue(op_load_gpr_T0_gpr, REG) (void)
|
||||||
|
{
|
||||||
|
T0 = env->gpr[REG];
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_store_T0_gpr_gpr, REG) (void)
|
||||||
|
{
|
||||||
|
env->gpr[REG] = T0;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_load_gpr_T1_gpr, REG) (void)
|
||||||
|
{
|
||||||
|
T1 = env->gpr[REG];
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_store_T1_gpr_gpr, REG) (void)
|
||||||
|
{
|
||||||
|
env->gpr[REG] = T1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue(op_load_gpr_T2_gpr, REG) (void)
|
||||||
|
{
|
||||||
|
T2 = env->gpr[REG];
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined (TN)
|
||||||
|
void glue(op_set_, TN) (void)
|
||||||
|
{
|
||||||
|
TN = PARAM1;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
|
||||||
|
void glue (op_reset_, TN) (void)
|
||||||
|
{
|
||||||
|
TN = 0;
|
||||||
|
RETURN();
|
||||||
|
}
|
||||||
|
#endif
|
1505
target-mips/translate.c
Normal file
1505
target-mips/translate.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -300,6 +300,8 @@ int cpu_restore_state(TranslationBlock *tb,
|
||||||
}
|
}
|
||||||
env->access_type = type;
|
env->access_type = type;
|
||||||
}
|
}
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
env->PC = gen_opc_pc[j];
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
13
vl.c
13
vl.c
|
@ -2348,6 +2348,17 @@ int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
void cpu_save(QEMUFile *f, void *opaque)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int cpu_load(QEMUFile *f, void *opaque, int version_id)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined(TARGET_SPARC)
|
#elif defined(TARGET_SPARC)
|
||||||
void cpu_save(QEMUFile *f, void *opaque)
|
void cpu_save(QEMUFile *f, void *opaque)
|
||||||
{
|
{
|
||||||
|
@ -3058,6 +3069,8 @@ void register_machines(void)
|
||||||
qemu_register_machine(&heathrow_machine);
|
qemu_register_machine(&heathrow_machine);
|
||||||
qemu_register_machine(&core99_machine);
|
qemu_register_machine(&core99_machine);
|
||||||
qemu_register_machine(&prep_machine);
|
qemu_register_machine(&prep_machine);
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
qemu_register_machine(&mips_machine);
|
||||||
#elif defined(TARGET_SPARC)
|
#elif defined(TARGET_SPARC)
|
||||||
#ifdef TARGET_SPARC64
|
#ifdef TARGET_SPARC64
|
||||||
qemu_register_machine(&sun4u_machine);
|
qemu_register_machine(&sun4u_machine);
|
||||||
|
|
5
vl.h
5
vl.h
|
@ -138,6 +138,8 @@ extern int win2k_install_hack;
|
||||||
/* XXX: make it dynamic */
|
/* XXX: make it dynamic */
|
||||||
#if defined (TARGET_PPC)
|
#if defined (TARGET_PPC)
|
||||||
#define BIOS_SIZE (512 * 1024)
|
#define BIOS_SIZE (512 * 1024)
|
||||||
|
#elif defined(TARGET_MIPS)
|
||||||
|
#define BIOS_SIZE (128 * 1024)
|
||||||
#else
|
#else
|
||||||
#define BIOS_SIZE ((256 + 64) * 1024)
|
#define BIOS_SIZE ((256 + 64) * 1024)
|
||||||
#endif
|
#endif
|
||||||
|
@ -715,6 +717,9 @@ extern QEMUMachine prep_machine;
|
||||||
extern QEMUMachine core99_machine;
|
extern QEMUMachine core99_machine;
|
||||||
extern QEMUMachine heathrow_machine;
|
extern QEMUMachine heathrow_machine;
|
||||||
|
|
||||||
|
/* mips_r4k.c */
|
||||||
|
extern QEMUMachine mips_machine;
|
||||||
|
|
||||||
#ifdef TARGET_PPC
|
#ifdef TARGET_PPC
|
||||||
ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq);
|
ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq);
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue