target-mips: add Config5.FRE support allowing Status.FR=0 emulation

This relatively small architectural feature adds the following:

FIR.FREP: Read-only. If FREP=1, then Config5.FRE and Config5.UFE are
available.

Config5.FRE: When enabled all single-precision FP arithmetic instructions,
LWC1/LWXC1/MTC1, SWC1/SWXC1/MFC1 cause a Reserved Instructions
exception.

Config5.UFE: Allows user to write/read Config5.FRE using CTC1/CFC1
instructions.

Enable the feature in MIPS64R6-generic CPU.

Backports commit 7c979afd11b09a16634699dd6344e3ba10c9677e from qemu
This commit is contained in:
Leon Alrae 2018-02-13 12:53:12 -05:00 committed by Lioncash
parent 428ffed744
commit c54458b638
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7
4 changed files with 80 additions and 24 deletions

View file

@ -101,6 +101,7 @@ struct CPUMIPSFPUContext {
float_status fp_status; float_status fp_status;
/* fpu implementation/revision register (fir) */ /* fpu implementation/revision register (fir) */
uint32_t fcr0; uint32_t fcr0;
#define FCR0_FREP 29
#define FCR0_UFRP 28 #define FCR0_UFRP 28
#define FCR0_F64 22 #define FCR0_F64 22
#define FCR0_L 21 #define FCR0_L 21
@ -463,6 +464,8 @@ struct CPUMIPSState {
#define CP0C5_CV 29 #define CP0C5_CV 29
#define CP0C5_EVA 28 #define CP0C5_EVA 28
#define CP0C5_MSAEn 27 #define CP0C5_MSAEn 27
#define CP0C5_UFE 9
#define CP0C5_FRE 8
#define CP0C5_SBRI 6 #define CP0C5_SBRI 6
#define CP0C5_UFR 2 #define CP0C5_UFR 2
#define CP0C5_NFExists 0 #define CP0C5_NFExists 0
@ -515,7 +518,7 @@ struct CPUMIPSState {
#define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */ #define EXCP_INST_NOTAVAIL 0x2 /* No valid instruction word for BadInstr */
uint32_t hflags; /* CPU State */ uint32_t hflags; /* CPU State */
/* TMASK defines different execution modes */ /* TMASK defines different execution modes */
#define MIPS_HFLAG_TMASK 0x15807FF #define MIPS_HFLAG_TMASK 0x35807FF
#define MIPS_HFLAG_MODE 0x00007 /* execution modes */ #define MIPS_HFLAG_MODE 0x00007 /* execution modes */
/* The KSU flags must be the lowest bits in hflags. The flag order /* The KSU flags must be the lowest bits in hflags. The flag order
must be the same as defined for CP0 Status. This allows to use must be the same as defined for CP0 Status. This allows to use
@ -561,7 +564,8 @@ struct CPUMIPSState {
#define MIPS_HFLAG_HWRENA_ULR 0x200000 /* ULR bit from HWREna is set. */ #define MIPS_HFLAG_HWRENA_ULR 0x200000 /* ULR bit from HWREna is set. */
#define MIPS_HFLAG_SBRI 0x400000 /* R6 SDBBP causes RI excpt. in user mode */ #define MIPS_HFLAG_SBRI 0x400000 /* R6 SDBBP causes RI excpt. in user mode */
#define MIPS_HFLAG_FBNSLOT 0x800000 /* Forbidden slot */ #define MIPS_HFLAG_FBNSLOT 0x800000 /* Forbidden slot */
#define MIPS_HFLAG_MSA 0x1000000 #define MIPS_HFLAG_MSA 0x1000000 /* FRE enabled */
#define MIPS_HFLAG_FRE 0x2000000 /* FRE enabled */
target_ulong btarget; /* Jump / branch target */ target_ulong btarget; /* Jump / branch target */
target_ulong bcond; /* Branch condition (if needed) */ target_ulong bcond; /* Branch condition (if needed) */
@ -847,7 +851,7 @@ static inline void compute_hflags(CPUMIPSState *env)
env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 |
MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU | MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU |
MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 | MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 |
MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA); MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE);
if (!(env->CP0_Status & (1 << CP0St_EXL)) && if (!(env->CP0_Status & (1 << CP0St_EXL)) &&
!(env->CP0_Status & (1 << CP0St_ERL)) && !(env->CP0_Status & (1 << CP0St_ERL)) &&
!(env->hflags & MIPS_HFLAG_DM)) { !(env->hflags & MIPS_HFLAG_DM)) {
@ -928,6 +932,11 @@ static inline void compute_hflags(CPUMIPSState *env)
env->hflags |= MIPS_HFLAG_MSA; env->hflags |= MIPS_HFLAG_MSA;
} }
} }
if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
if (env->CP0_Config5 & (1 << CP0C5_FRE)) {
env->hflags |= MIPS_HFLAG_FRE;
}
}
} }
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY

View file

@ -2306,6 +2306,16 @@ target_ulong helper_cfc1(CPUMIPSState *env, uint32_t reg)
} }
} }
break; break;
case 5:
/* FRE Support - read Config5.FRE bit */
if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) {
if (env->CP0_Config5 & (1 << CP0C5_UFE)) {
arg1 = (env->CP0_Config5 >> CP0C5_FRE) & 1;
} else {
helper_raise_exception(env, EXCP_RI);
}
}
break;
case 25: case 25:
arg1 = ((env->active_fpu.fcr31 >> 24) & 0xfe) | ((env->active_fpu.fcr31 >> 23) & 0x1); arg1 = ((env->active_fpu.fcr31 >> 24) & 0xfe) | ((env->active_fpu.fcr31 >> 23) & 0x1);
break; break;
@ -2350,6 +2360,30 @@ void helper_ctc1(CPUMIPSState *env, target_ulong arg1, uint32_t fs, uint32_t rt)
helper_raise_exception(env, EXCP_RI); helper_raise_exception(env, EXCP_RI);
} }
break; break;
case 5:
/* FRE Support - clear Config5.FRE bit */
if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) {
return;
}
if (env->CP0_Config5 & (1 << CP0C5_UFE)) {
env->CP0_Config5 &= ~(1 << CP0C5_FRE);
compute_hflags(env);
} else {
helper_raise_exception(env, EXCP_RI);
}
break;
case 6:
/* FRE Support - set Config5.FRE bit */
if (!((env->active_fpu.fcr0 & (1 << FCR0_FREP)) && (rt == 0))) {
return;
}
if (env->CP0_Config5 & (1 << CP0C5_UFE)) {
env->CP0_Config5 |= (1 << CP0C5_FRE);
compute_hflags(env);
} else {
helper_raise_exception(env, EXCP_RI);
}
break;
case 25: case 25:
if ((env->insn_flags & ISA_MIPS32R6) || (arg1 & 0xffffff00)) { if ((env->insn_flags & ISA_MIPS32R6) || (arg1 & 0xffffff00)) {
return; return;

View file

@ -1548,20 +1548,22 @@ static inline void gen_store_srsgpr (DisasContext *s, int from, int to)
} }
/* Tests */ /* Tests */
static inline void gen_save_pc(target_ulong pc) static inline void gen_save_pc(DisasContext *ctx, target_ulong pc)
{ {
tcg_gen_movi_tl(cpu_PC, pc); TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
tcg_gen_movi_tl(tcg_ctx, *(TCGv *)tcg_ctx->cpu_PC, pc);
} }
static inline void save_cpu_state(DisasContext *ctx, int do_save_pc) static inline void save_cpu_state(DisasContext *ctx, int do_save_pc)
{ {
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
LOG_DISAS("hflags %08x saved %08x\n", ctx->hflags, ctx->saved_hflags); LOG_DISAS("hflags %08x saved %08x\n", ctx->hflags, ctx->saved_hflags);
if (do_save_pc && ctx->pc != ctx->saved_pc) { if (do_save_pc && ctx->pc != ctx->saved_pc) {
gen_save_pc(ctx->pc); gen_save_pc(ctx, ctx->pc);
ctx->saved_pc = ctx->pc; ctx->saved_pc = ctx->pc;
} }
if (ctx->hflags != ctx->saved_hflags) { if (ctx->hflags != ctx->saved_hflags) {
tcg_gen_movi_i32(hflags, ctx->hflags); tcg_gen_movi_i32(tcg_ctx, tcg_ctx->hflags, ctx->hflags);
ctx->saved_hflags = ctx->hflags; ctx->saved_hflags = ctx->hflags;
switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) { switch (ctx->hflags & MIPS_HFLAG_BMASK_BASE) {
case MIPS_HFLAG_BR: case MIPS_HFLAG_BR:
@ -1569,7 +1571,7 @@ static inline void save_cpu_state(DisasContext *ctx, int do_save_pc)
case MIPS_HFLAG_BC: case MIPS_HFLAG_BC:
case MIPS_HFLAG_BL: case MIPS_HFLAG_BL:
case MIPS_HFLAG_B: case MIPS_HFLAG_B:
tcg_gen_movi_tl(btarget, ctx->btarget); tcg_gen_movi_tl(tcg_ctx, *(TCGv *)tcg_ctx->btarget, ctx->btarget);
break; break;
} }
} }
@ -1591,31 +1593,41 @@ static inline void restore_cpu_state(CPUMIPSState *env, DisasContext *ctx)
static inline void generate_exception_err(DisasContext *ctx, int excp, int err) static inline void generate_exception_err(DisasContext *ctx, int excp, int err)
{ {
TCGv_i32 texcp = tcg_const_i32(excp); TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
TCGv_i32 terr = tcg_const_i32(err); TCGv_i32 texcp = tcg_const_i32(tcg_ctx, excp);
TCGv_i32 terr = tcg_const_i32(tcg_ctx, err);
save_cpu_state(ctx, 1); save_cpu_state(ctx, 1);
gen_helper_raise_exception_err(cpu_env, texcp, terr); gen_helper_raise_exception_err(tcg_ctx, tcg_ctx->cpu_env, texcp, terr);
tcg_temp_free_i32(terr); tcg_temp_free_i32(tcg_ctx, terr);
tcg_temp_free_i32(texcp); tcg_temp_free_i32(tcg_ctx, texcp);
} }
static inline void generate_exception(DisasContext *ctx, int excp) static inline void generate_exception(DisasContext *ctx, int excp)
{ {
TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
save_cpu_state(ctx, 1); save_cpu_state(ctx, 1);
gen_helper_0e0i(raise_exception, excp); gen_helper_0e0i(tcg_ctx, raise_exception, excp);
} }
/* Floating point register moves. */ /* Floating point register moves. */
static void gen_load_fpr32(DisasContext *s, TCGv_i32 t, int reg) static void gen_load_fpr32(DisasContext *ctx, TCGv_i32 t, int reg)
{ {
TCGContext *tcg_ctx = s->uc->tcg_ctx; TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
if (ctx->hflags & MIPS_HFLAG_FRE) {
generate_exception(ctx, EXCP_RI);
}
tcg_gen_extrl_i64_i32(tcg_ctx, t, tcg_ctx->fpu_f64[reg]); tcg_gen_extrl_i64_i32(tcg_ctx, t, tcg_ctx->fpu_f64[reg]);
} }
static void gen_store_fpr32(DisasContext *s, TCGv_i32 t, int reg) static void gen_store_fpr32(DisasContext *ctx, TCGv_i32 t, int reg)
{ {
TCGContext *tcg_ctx = s->uc->tcg_ctx; TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
TCGv_i64 t64 = tcg_temp_new_i64(tcg_ctx); TCGv_i64 t64;
if (ctx->hflags & MIPS_HFLAG_FRE) {
generate_exception(ctx, EXCP_RI);
}
t64 = tcg_temp_new_i64(tcg_ctx);
tcg_gen_extu_i32_i64(tcg_ctx, t64, t); tcg_gen_extu_i32_i64(tcg_ctx, t64, t);
tcg_gen_deposit_i64(tcg_ctx, tcg_ctx->fpu_f64[reg], tcg_ctx->fpu_f64[reg], t64, 0, 32); tcg_gen_deposit_i64(tcg_ctx, tcg_ctx->fpu_f64[reg], tcg_ctx->fpu_f64[reg], t64, 0, 32);
tcg_temp_free_i64(tcg_ctx, t64); tcg_temp_free_i64(tcg_ctx, t64);
@ -8523,7 +8535,8 @@ static void gen_movci (DisasContext *ctx, int rd, int rs, int cc, int tf)
gen_set_label(tcg_ctx, l1); gen_set_label(tcg_ctx, l1);
} }
static inline void gen_movcf_s (DisasContext *ctx, int fs, int fd, int cc, int tf) static inline void gen_movcf_s(DisasContext *ctx, int fs, int fd, int cc,
int tf)
{ {
TCGContext *tcg_ctx = ctx->uc->tcg_ctx; TCGContext *tcg_ctx = ctx->uc->tcg_ctx;
int cond; int cond;

View file

@ -739,7 +739,7 @@ static const mips_def_t mips_defs[] =
(3 << CP0C4_IE) | (1 << CP0C4_M), (3 << CP0C4_IE) | (1 << CP0C4_M),
0, 0,
0, 0,
(1 << CP0C5_SBRI), (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE),
0, 0,
0, 0,
0, 0,
@ -749,9 +749,9 @@ static const mips_def_t mips_defs[] =
0x30D8FFFF, 0x30D8FFFF,
0, 0,
0, 0,
(1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_FREP) | (1 << FCR0_F64) | (1 << FCR0_L) |
(1 << FCR0_D) | (1 << FCR0_S) | (0x00 << FCR0_PRID) | (1 << FCR0_W) | (1 << FCR0_D) | (1 << FCR0_S) |
(0x0 << FCR0_REV), (0x00 << FCR0_PRID) | (0x0 << FCR0_REV),
0, 0,
42, 42,
/* The architectural limit is 59, but we have hardcoded 36 bit /* The architectural limit is 59, but we have hardcoded 36 bit