Thank you, Richard, > > Can QEMU be instructed to emulate > > the FPU only for Linux user space programs as opposed to hardware emulation? > > Yes, that can be done. I would suggest something like > > /* > * Hardware traps to the operating system for emulation. > * For user-only, qemu is the operating system, so we > * emulate the trap and emulate by simply emulating the > * instruction directly. > */ > #ifdef CONFIG_USER_ONLY > # define CP0C1_FP_USER_ONLY (1 << CP0C1_FP) > #else > # define CP0C1_FP_USER_ONLY 0 > #endif > > and include that in your initialization of CP0_Config1.
I've made both CP0C1_FP and the LL/SC emulation conditional on user-only, and adjusted the patch after the review by Maciej, as shown below. Aleksandar, Aurelien, Maciej -- are you happy with this initial v2 patch? Fredrik Signed-off-by: Fredrik Noring <nor...@nocrew.org> --- include/elf.h | 3 ++ linux-user/mips/target_elf.h | 3 ++ target/mips/mips-defs.h | 2 + target/mips/translate.c | 31 ++++++++++++++++++++++++++- target/mips/translate_init.inc.c | 44 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 1 deletion(-) --- a/include/elf.h +++ b/include/elf.h @@ -48,6 +48,8 @@ typedef int64_t Elf64_Sxword; #define EF_MIPS_ARCH_32R6 0x90000000 /* MIPS32r6 code. */ #define EF_MIPS_ARCH_64R6 0xa0000000 /* MIPS64r6 code. */ +#define EF_MIPS_MACH_5900 0x00920000 + /* The ABI of a file. */ #define EF_MIPS_ABI_O32 0x00001000 /* O32 ABI. */ #define EF_MIPS_ABI_O64 0x00002000 /* O32 extended for 64 bit. */ @@ -62,6 +64,7 @@ typedef int64_t Elf64_Sxword; #define EF_MIPS_FP64 0x00000200 #define EF_MIPS_NAN2008 0x00000400 #define EF_MIPS_ARCH 0xf0000000 +#define EF_MIPS_MACH 0x00ff0000 /* These constants define the different elf file types */ #define ET_NONE 0 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -12,6 +12,9 @@ static inline const char *cpu_get_model(uint32_t eflags) if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { return "mips32r6-generic"; } + if ((eflags & EF_MIPS_MACH) == EF_MIPS_MACH_5900) { + return "R5900"; + } return "24Kf"; } #endif --- a/target/mips/mips-defs.h +++ b/target/mips/mips-defs.h @@ -52,6 +52,7 @@ #define ASE_MSA 0x01000000 /* Chip specific instructions. */ +#define INSN_R5900 0x10000000 #define INSN_LOONGSON2E 0x20000000 #define INSN_LOONGSON2F 0x40000000 #define INSN_VR54XX 0x80000000 @@ -62,6 +63,7 @@ #define CPU_MIPS3 (CPU_MIPS2 | ISA_MIPS3) #define CPU_MIPS4 (CPU_MIPS3 | ISA_MIPS4) #define CPU_VR54XX (CPU_MIPS4 | INSN_VR54XX) +#define CPU_R5900 (CPU_MIPS4 | INSN_R5900) #define CPU_LOONGSON2E (CPU_MIPS3 | INSN_LOONGSON2E) #define CPU_LOONGSON2F (CPU_MIPS3 | INSN_LOONGSON2F) --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -3618,6 +3618,31 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); } +static void gen_mul_r5900 (DisasContext *ctx, uint32_t opc, + int rd, int rs, int rt) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + switch (opc) { + case OPC_MULT: + case OPC_MULTU: + tcg_gen_mul_tl(cpu_gpr[rd], t0, t1); + break; + default: + MIPS_INVAL("mul R5900"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} + static void gen_mul_vr54xx (DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { @@ -17357,7 +17382,11 @@ static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) break; case OPC_MULT: case OPC_MULTU: - if (sa) { + if (ctx->insn_flags & INSN_R5900) { + gen_muldiv(ctx, op1, 0, rs, rt); + if (rd != 0) + gen_mul_r5900(ctx, op1, rd, rs, rt); + } else if (sa) { check_insn(ctx, INSN_VR54XX); op1 = MASK_MUL_VR54XX(ctx->opcode); gen_mul_vr54xx(ctx, op1, rd, rs, rt); --- a/target/mips/translate_init.inc.c +++ b/target/mips/translate_init.inc.c @@ -410,6 +410,50 @@ const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS32R5 | ASE_MSA, .mmu_type = MMU_TYPE_R4000, }, + { + .name = "R5900", + .CP0_PRid = 0x00003800, + /* No L2 cache, icache size 32k, dcache size 32k, uncached coherency. */ + .CP0_Config0 = (1 << 17) | (0x3 << 9) | (0x3 << 6) | (0x2 << CP0C0_K0), + /* Note: Config1 is only used internally, the R5900 has only Config0. */ + .CP0_Status_rw_bitmask = 0xF4C79C1F, +#ifdef CONFIG_USER_ONLY + /* + * R5900 hardware traps to the Linux kernel for IEEE 754-1985 and LL/SC + * emulation. For user-only, qemu is the kernel, so we emulate the traps + * by simply emulating the instructions directly. + */ + .CP0_Config1 = (1 << CP0C1_FP) | (47 << CP0C1_MMU), + .CP0_LLAddr_rw_bitmask = 0xFFFFFFFF, + .CP0_LLAddr_shift = 4, + .CP1_fcr0 = (0x38 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr31 = 0, + .CP1_fcr31_rw_bitmask = 0x0183FFFF, +#else + /* + * The R5900 COP1 FPU implements single-precision floating-point + * operations but is not entirely IEEE 754-1985 compatible. In + * particular, + * + * - NaN (not a number) and plus/minus infinities are not supported; + * - exception mechanisms are not fully supported; + * - denormalized numbers are not supported; + * - rounding towards nearest and plus/minus infinities are not supported; + * - computed results usually differs in the least significant bit; + * - saturating instructions can differ more than the least significant bit. + * + * Since only rounding towards zero is supported, the two least + * significant bits of FCR31 are hardwired to 01. + * + * FPU emulation is disabled here until it is implemented. + */ + .CP0_Config1 = (47 << CP0C1_MMU), +#endif /* CONFIG_USER_ONLY */ + .SEGBITS = 19, + .PABITS = 20, + .insn_flags = CPU_R5900, + .mmu_type = MMU_TYPE_R4000, + }, { /* A generic CPU supporting MIPS32 Release 6 ISA. FIXME: Support IEEE 754-2008 FP.