SH: Improve movca.l/ocbi emulation.
Author: Vladimir Prus <vladimir@codesourcery.com>
    Fix movcal.l/ocbi emulation.
        * target-sh4/cpu.h (memory_content): New.
        (CPUSH4State): New fields movcal_backup and movcal_backup_tail.
        * target-sh4/helper.h (helper_movcal)
        (helper_discard_movcal_backup, helper_ocbi): New.
        * target-sh4/op_helper.c (helper_movcal)
        (helper_discard_movcal_backup, helper_ocbi): New.
        * target-sh4/translate.c (DisasContext): New field has_movcal.
        (sh4_defs): Update CVS for SH7785.
        (cpu_sh4_init): Initialize env->movcal_backup_tail.
        (_decode_opc): Discard movca.l-backup.
        Make use of helper_movcal and helper_ocbi.
        (gen_intermediate_code_internal): Initialize has_movcal to 1.
Thanks to Shin-ichiro KAWASAKI and Paul Mundt for valuable feedback.
Acked-by: Edgar E. Iglesias <edgar.iglesias@gmail.com>
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6966 c046a42c-6fe2-441c-8c8c-71466251a162
			
			
This commit is contained in:
		
							parent
							
								
									714fa308a3
								
							
						
					
					
						commit
						852d481faf
					
				@ -100,6 +100,12 @@ enum sh_features {
 | 
				
			|||||||
    SH_FEATURE_BCR3_AND_BCR4 = 2,
 | 
					    SH_FEATURE_BCR3_AND_BCR4 = 2,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct memory_content {
 | 
				
			||||||
 | 
					    uint32_t address;
 | 
				
			||||||
 | 
					    uint32_t value;
 | 
				
			||||||
 | 
					    struct memory_content *next;
 | 
				
			||||||
 | 
					} memory_content;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct CPUSH4State {
 | 
					typedef struct CPUSH4State {
 | 
				
			||||||
    int id;			/* CPU model */
 | 
					    int id;			/* CPU model */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -148,6 +154,8 @@ typedef struct CPUSH4State {
 | 
				
			|||||||
    tlb_t itlb[ITLB_SIZE];	/* instruction translation table */
 | 
					    tlb_t itlb[ITLB_SIZE];	/* instruction translation table */
 | 
				
			||||||
    void *intc_handle;
 | 
					    void *intc_handle;
 | 
				
			||||||
    int intr_at_halt;		/* SR_BL ignored during sleep */
 | 
					    int intr_at_halt;		/* SR_BL ignored during sleep */
 | 
				
			||||||
 | 
					    memory_content *movcal_backup;
 | 
				
			||||||
 | 
					    memory_content **movcal_backup_tail;
 | 
				
			||||||
} CPUSH4State;
 | 
					} CPUSH4State;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CPUSH4State *cpu_sh4_init(const char *cpu_model);
 | 
					CPUSH4State *cpu_sh4_init(const char *cpu_model);
 | 
				
			||||||
@ -162,6 +170,8 @@ void sh4_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
 | 
				
			|||||||
void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr,
 | 
					void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr,
 | 
				
			||||||
				    uint32_t mem_value);
 | 
									    uint32_t mem_value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void cpu_set_tls(CPUSH4State *env, target_ulong newtls)
 | 
					static inline void cpu_set_tls(CPUSH4State *env, target_ulong newtls)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  env->gbr = newtls;
 | 
					  env->gbr = newtls;
 | 
				
			||||||
@ -293,6 +303,8 @@ static inline void cpu_pc_from_tb(CPUState *env, TranslationBlock *tb)
 | 
				
			|||||||
    env->flags = tb->flags;
 | 
					    env->flags = tb->flags;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TB_FLAG_PENDING_MOVCA  (1 << 4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
 | 
					static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
 | 
				
			||||||
                                        target_ulong *cs_base, int *flags)
 | 
					                                        target_ulong *cs_base, int *flags)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -302,7 +314,8 @@ static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc,
 | 
				
			|||||||
                    | DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME))   /* Bits  0- 3 */
 | 
					                    | DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME))   /* Bits  0- 3 */
 | 
				
			||||||
            | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR))  /* Bits 19-21 */
 | 
					            | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR))  /* Bits 19-21 */
 | 
				
			||||||
            | (env->sr & (SR_MD | SR_RB))                      /* Bits 29-30 */
 | 
					            | (env->sr & (SR_MD | SR_RB))                      /* Bits 29-30 */
 | 
				
			||||||
            | (env->sr & SR_FD);                               /* Bit 15 */
 | 
					            | (env->sr & SR_FD)                                /* Bit 15 */
 | 
				
			||||||
 | 
					            | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 4 */
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif				/* _CPU_SH4_H */
 | 
					#endif				/* _CPU_SH4_H */
 | 
				
			||||||
 | 
				
			|||||||
@ -644,4 +644,48 @@ void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int n;
 | 
				
			||||||
 | 
					    int use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* check area */
 | 
				
			||||||
 | 
					    if (env->sr & SR_MD) {
 | 
				
			||||||
 | 
					        /* For previledged mode, P2 and P4 area is not cachable. */
 | 
				
			||||||
 | 
					        if ((0xA0000000 <= addr && addr < 0xC0000000) || 0xE0000000 <= addr)
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        /* For user mode, only U0 area is cachable. */
 | 
				
			||||||
 | 
					        if (0x80000000 <= addr)
 | 
				
			||||||
 | 
					            return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					     * TODO : Evaluate CCR and check if the cache is on or off.
 | 
				
			||||||
 | 
					     *        Now CCR is not in CPUSH4State, but in SH7750State.
 | 
				
			||||||
 | 
					     *        When you move the ccr inot CPUSH4State, the code will be
 | 
				
			||||||
 | 
					     *        as follows.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					#if 0
 | 
				
			||||||
 | 
					    /* check if operand cache is enabled or not. */
 | 
				
			||||||
 | 
					    if (!(env->ccr & 1))
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* if MMU is off, no check for TLB. */
 | 
				
			||||||
 | 
					    if (env->mmucr & MMUCR_AT)
 | 
				
			||||||
 | 
					        return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /* check TLB */
 | 
				
			||||||
 | 
					    n = find_tlb_entry(env, addr, env->itlb, ITLB_SIZE, use_asid);
 | 
				
			||||||
 | 
					    if (n >= 0)
 | 
				
			||||||
 | 
					        return env->itlb[n].c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    n = find_tlb_entry(env, addr, env->utlb, UTLB_SIZE, use_asid);
 | 
				
			||||||
 | 
					    if (n >= 0)
 | 
				
			||||||
 | 
					        return env->utlb[n].c;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,10 @@ DEF_HELPER_0(debug, void)
 | 
				
			|||||||
DEF_HELPER_1(sleep, void, i32)
 | 
					DEF_HELPER_1(sleep, void, i32)
 | 
				
			||||||
DEF_HELPER_1(trapa, void, i32)
 | 
					DEF_HELPER_1(trapa, void, i32)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEF_HELPER_2(movcal, void, i32, i32)
 | 
				
			||||||
 | 
					DEF_HELPER_0(discard_movcal_backup, void)
 | 
				
			||||||
 | 
					DEF_HELPER_1(ocbi, void, i32)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEF_HELPER_2(addv, i32, i32, i32)
 | 
					DEF_HELPER_2(addv, i32, i32, i32)
 | 
				
			||||||
DEF_HELPER_2(addc, i32, i32, i32)
 | 
					DEF_HELPER_2(addc, i32, i32, i32)
 | 
				
			||||||
DEF_HELPER_2(subv, i32, i32, i32)
 | 
					DEF_HELPER_2(subv, i32, i32, i32)
 | 
				
			||||||
 | 
				
			|||||||
@ -18,6 +18,7 @@
 | 
				
			|||||||
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
 | 
					 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA  02110-1301 USA
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
#include <assert.h>
 | 
					#include <assert.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
#include "exec.h"
 | 
					#include "exec.h"
 | 
				
			||||||
#include "helper.h"
 | 
					#include "helper.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -122,6 +123,57 @@ void helper_trapa(uint32_t tra)
 | 
				
			|||||||
    cpu_loop_exit();
 | 
					    cpu_loop_exit();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void helper_movcal(uint32_t address, uint32_t value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (cpu_sh4_is_cached (env, address))
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						memory_content *r = malloc (sizeof(memory_content));
 | 
				
			||||||
 | 
						r->address = address;
 | 
				
			||||||
 | 
						r->value = value;
 | 
				
			||||||
 | 
						r->next = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*(env->movcal_backup_tail) = r;
 | 
				
			||||||
 | 
						env->movcal_backup_tail = &(r->next);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void helper_discard_movcal_backup(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    memory_content *current = env->movcal_backup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while(current)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						memory_content *next = current->next;
 | 
				
			||||||
 | 
						free (current);
 | 
				
			||||||
 | 
						env->movcal_backup = current = next;
 | 
				
			||||||
 | 
						if (current == 0)
 | 
				
			||||||
 | 
						    env->movcal_backup_tail = &(env->movcal_backup);
 | 
				
			||||||
 | 
					    } 
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void helper_ocbi(uint32_t address)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    memory_content **current = &(env->movcal_backup);
 | 
				
			||||||
 | 
					    while (*current)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						uint32_t a = (*current)->address;
 | 
				
			||||||
 | 
						if ((a & ~0x1F) == (address & ~0x1F))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						    memory_content *next = (*current)->next;
 | 
				
			||||||
 | 
						    stl(a, (*current)->value);
 | 
				
			||||||
 | 
						    
 | 
				
			||||||
 | 
						    if (next == 0)
 | 
				
			||||||
 | 
						    {
 | 
				
			||||||
 | 
							env->movcal_backup_tail = current;
 | 
				
			||||||
 | 
						    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						    free (*current);
 | 
				
			||||||
 | 
						    *current = next;
 | 
				
			||||||
 | 
						    break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uint32_t helper_addc(uint32_t arg0, uint32_t arg1)
 | 
					uint32_t helper_addc(uint32_t arg0, uint32_t arg1)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    uint32_t tmp0, tmp1;
 | 
					    uint32_t tmp0, tmp1;
 | 
				
			||||||
 | 
				
			|||||||
@ -50,6 +50,7 @@ typedef struct DisasContext {
 | 
				
			|||||||
    uint32_t delayed_pc;
 | 
					    uint32_t delayed_pc;
 | 
				
			||||||
    int singlestep_enabled;
 | 
					    int singlestep_enabled;
 | 
				
			||||||
    uint32_t features;
 | 
					    uint32_t features;
 | 
				
			||||||
 | 
					    int has_movcal;
 | 
				
			||||||
} DisasContext;
 | 
					} DisasContext;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if defined(CONFIG_USER_ONLY)
 | 
					#if defined(CONFIG_USER_ONLY)
 | 
				
			||||||
@ -283,6 +284,7 @@ CPUSH4State *cpu_sh4_init(const char *cpu_model)
 | 
				
			|||||||
    env = qemu_mallocz(sizeof(CPUSH4State));
 | 
					    env = qemu_mallocz(sizeof(CPUSH4State));
 | 
				
			||||||
    env->features = def->features;
 | 
					    env->features = def->features;
 | 
				
			||||||
    cpu_exec_init(env);
 | 
					    cpu_exec_init(env);
 | 
				
			||||||
 | 
					    env->movcal_backup_tail = &(env->movcal_backup);
 | 
				
			||||||
    sh4_translate_init();
 | 
					    sh4_translate_init();
 | 
				
			||||||
    env->cpu_model_str = cpu_model;
 | 
					    env->cpu_model_str = cpu_model;
 | 
				
			||||||
    cpu_sh4_reset(env);
 | 
					    cpu_sh4_reset(env);
 | 
				
			||||||
@ -495,6 +497,37 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static void _decode_opc(DisasContext * ctx)
 | 
					static void _decode_opc(DisasContext * ctx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					    /* This code tries to make movcal emulation sufficiently
 | 
				
			||||||
 | 
					       accurate for Linux purposes.  This instruction writes
 | 
				
			||||||
 | 
					       memory, and prior to that, always allocates a cache line.
 | 
				
			||||||
 | 
					       It is used in two contexts:
 | 
				
			||||||
 | 
					       - in memcpy, where data is copied in blocks, the first write
 | 
				
			||||||
 | 
					       of to a block uses movca.l for performance.
 | 
				
			||||||
 | 
					       - in arch/sh/mm/cache-sh4.c, movcal.l + ocbi combination is used
 | 
				
			||||||
 | 
					       to flush the cache. Here, the data written by movcal.l is never
 | 
				
			||||||
 | 
					       written to memory, and the data written is just bogus.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       To simulate this, we simulate movcal.l, we store the value to memory,
 | 
				
			||||||
 | 
					       but we also remember the previous content. If we see ocbi, we check
 | 
				
			||||||
 | 
					       if movcal.l for that address was done previously. If so, the write should
 | 
				
			||||||
 | 
					       not have hit the memory, so we restore the previous content.
 | 
				
			||||||
 | 
					       When we see an instruction that is neither movca.l
 | 
				
			||||||
 | 
					       nor ocbi, the previous content is discarded.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       To optimize, we only try to flush stores when we're at the start of
 | 
				
			||||||
 | 
					       TB, or if we already saw movca.l in this TB and did not flush stores
 | 
				
			||||||
 | 
					       yet.  */
 | 
				
			||||||
 | 
					    if (ctx->has_movcal)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						  int opcode = ctx->opcode & 0xf0ff;
 | 
				
			||||||
 | 
						  if (opcode != 0x0093 /* ocbi */
 | 
				
			||||||
 | 
						      && opcode != 0x00c3 /* movca.l */)
 | 
				
			||||||
 | 
						      {
 | 
				
			||||||
 | 
							  gen_helper_discard_movcal_backup ();
 | 
				
			||||||
 | 
							  ctx->has_movcal = 0;
 | 
				
			||||||
 | 
						      }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#if 0
 | 
					#if 0
 | 
				
			||||||
    fprintf(stderr, "Translating opcode 0x%04x\n", ctx->opcode);
 | 
					    fprintf(stderr, "Translating opcode 0x%04x\n", ctx->opcode);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@ -1545,7 +1578,13 @@ static void _decode_opc(DisasContext * ctx)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return;
 | 
						return;
 | 
				
			||||||
    case 0x00c3:		/* movca.l R0,@Rm */
 | 
					    case 0x00c3:		/* movca.l R0,@Rm */
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            TCGv val = tcg_temp_new();
 | 
				
			||||||
 | 
					            tcg_gen_qemu_ld32u(val, REG(B11_8), ctx->memidx);
 | 
				
			||||||
 | 
					            gen_helper_movcal (REG(B11_8), val);            
 | 
				
			||||||
            tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
 | 
					            tcg_gen_qemu_st32(REG(0), REG(B11_8), ctx->memidx);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ctx->has_movcal = 1;
 | 
				
			||||||
	return;
 | 
						return;
 | 
				
			||||||
    case 0x40a9:
 | 
					    case 0x40a9:
 | 
				
			||||||
	/* MOVUA.L @Rm,R0 (Rm) -> R0
 | 
						/* MOVUA.L @Rm,R0 (Rm) -> R0
 | 
				
			||||||
@ -1594,9 +1633,7 @@ static void _decode_opc(DisasContext * ctx)
 | 
				
			|||||||
	    break;
 | 
						    break;
 | 
				
			||||||
    case 0x0093:		/* ocbi @Rn */
 | 
					    case 0x0093:		/* ocbi @Rn */
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
	    TCGv dummy = tcg_temp_new();
 | 
						    gen_helper_ocbi (REG(B11_8));
 | 
				
			||||||
	    tcg_gen_qemu_ld32s(dummy, REG(B11_8), ctx->memidx);
 | 
					 | 
				
			||||||
	    tcg_temp_free(dummy);
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return;
 | 
						return;
 | 
				
			||||||
    case 0x00a3:		/* ocbp @Rn */
 | 
					    case 0x00a3:		/* ocbp @Rn */
 | 
				
			||||||
@ -1876,6 +1913,7 @@ gen_intermediate_code_internal(CPUState * env, TranslationBlock * tb,
 | 
				
			|||||||
    ctx.tb = tb;
 | 
					    ctx.tb = tb;
 | 
				
			||||||
    ctx.singlestep_enabled = env->singlestep_enabled;
 | 
					    ctx.singlestep_enabled = env->singlestep_enabled;
 | 
				
			||||||
    ctx.features = env->features;
 | 
					    ctx.features = env->features;
 | 
				
			||||||
 | 
					    ctx.has_movcal = (tb->flags & TB_FLAG_PENDING_MOVCA);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef DEBUG_DISAS
 | 
					#ifdef DEBUG_DISAS
 | 
				
			||||||
    qemu_log_mask(CPU_LOG_TB_CPU,
 | 
					    qemu_log_mask(CPU_LOG_TB_CPU,
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user