target-mips: add TLBINV support
For Standard TLB configuration (Config.MT=1): TLBINV invalidates a set of TLB entries based on ASID. The virtual address is ignored in the entry match. TLB entries which have their G bit set to 1 are not modified. TLBINVF causes all entries to be invalidated. Single TLB entry can be marked as invalid on TLB entry write by having EntryHi.EHINV set to 1. Signed-off-by: Leon Alrae <leon.alrae@imgtec.com> Reviewed-by: Yongbok Kim <yongbok.kim@imgtec.com>
This commit is contained in:
parent
92ceb440d4
commit
9456c2fbcd
@ -2410,6 +2410,8 @@ const struct mips_opcode mips_builtin_opcodes[] =
|
|||||||
{"tlbp", "", 0x42000008, 0xffffffff, INSN_TLB, 0, I1 },
|
{"tlbp", "", 0x42000008, 0xffffffff, INSN_TLB, 0, I1 },
|
||||||
{"tlbr", "", 0x42000001, 0xffffffff, INSN_TLB, 0, I1 },
|
{"tlbr", "", 0x42000001, 0xffffffff, INSN_TLB, 0, I1 },
|
||||||
{"tlbwi", "", 0x42000002, 0xffffffff, INSN_TLB, 0, I1 },
|
{"tlbwi", "", 0x42000002, 0xffffffff, INSN_TLB, 0, I1 },
|
||||||
|
{"tlbinv", "", 0x42000003, 0xffffffff, INSN_TLB, 0, I32 },
|
||||||
|
{"tlbinvf", "", 0x42000004, 0xffffffff, INSN_TLB, 0, I32 },
|
||||||
{"tlbwr", "", 0x42000006, 0xffffffff, INSN_TLB, 0, I1 },
|
{"tlbwr", "", 0x42000006, 0xffffffff, INSN_TLB, 0, I1 },
|
||||||
{"tlti", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, 0, I2 },
|
{"tlti", "s,j", 0x040a0000, 0xfc1f0000, RD_s|TRAP, 0, I2 },
|
||||||
{"tlt", "s,t", 0x00000032, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 },
|
{"tlt", "s,t", 0x00000032, 0xfc00ffff, RD_s|RD_t|TRAP, 0, I2 },
|
||||||
|
@ -34,6 +34,7 @@ struct r4k_tlb_t {
|
|||||||
uint_fast16_t XI1:1;
|
uint_fast16_t XI1:1;
|
||||||
uint_fast16_t RI0:1;
|
uint_fast16_t RI0:1;
|
||||||
uint_fast16_t RI1:1;
|
uint_fast16_t RI1:1;
|
||||||
|
uint_fast16_t EHINV:1;
|
||||||
target_ulong PFN[2];
|
target_ulong PFN[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -47,6 +48,8 @@ struct CPUMIPSTLBContext {
|
|||||||
void (*helper_tlbwr)(struct CPUMIPSState *env);
|
void (*helper_tlbwr)(struct CPUMIPSState *env);
|
||||||
void (*helper_tlbp)(struct CPUMIPSState *env);
|
void (*helper_tlbp)(struct CPUMIPSState *env);
|
||||||
void (*helper_tlbr)(struct CPUMIPSState *env);
|
void (*helper_tlbr)(struct CPUMIPSState *env);
|
||||||
|
void (*helper_tlbinv)(struct CPUMIPSState *env);
|
||||||
|
void (*helper_tlbinvf)(struct CPUMIPSState *env);
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
r4k_tlb_t tlb[MIPS_TLB_MAX];
|
r4k_tlb_t tlb[MIPS_TLB_MAX];
|
||||||
@ -282,6 +285,7 @@ struct CPUMIPSState {
|
|||||||
target_ulong CP0_BadVAddr;
|
target_ulong CP0_BadVAddr;
|
||||||
int32_t CP0_Count;
|
int32_t CP0_Count;
|
||||||
target_ulong CP0_EntryHi;
|
target_ulong CP0_EntryHi;
|
||||||
|
#define CP0EnHi_EHINV 10
|
||||||
int32_t CP0_Compare;
|
int32_t CP0_Compare;
|
||||||
int32_t CP0_Status;
|
int32_t CP0_Status;
|
||||||
#define CP0St_CU3 31
|
#define CP0St_CU3 31
|
||||||
@ -393,6 +397,7 @@ struct CPUMIPSState {
|
|||||||
uint32_t CP0_Config4;
|
uint32_t CP0_Config4;
|
||||||
uint32_t CP0_Config4_rw_bitmask;
|
uint32_t CP0_Config4_rw_bitmask;
|
||||||
#define CP0C4_M 31
|
#define CP0C4_M 31
|
||||||
|
#define CP0C4_IE 29
|
||||||
#define CP0C4_KScrExist 16
|
#define CP0C4_KScrExist 16
|
||||||
uint32_t CP0_Config5;
|
uint32_t CP0_Config5;
|
||||||
uint32_t CP0_Config5_rw_bitmask;
|
uint32_t CP0_Config5_rw_bitmask;
|
||||||
@ -529,6 +534,8 @@ void r4k_helper_tlbwi(CPUMIPSState *env);
|
|||||||
void r4k_helper_tlbwr(CPUMIPSState *env);
|
void r4k_helper_tlbwr(CPUMIPSState *env);
|
||||||
void r4k_helper_tlbp(CPUMIPSState *env);
|
void r4k_helper_tlbp(CPUMIPSState *env);
|
||||||
void r4k_helper_tlbr(CPUMIPSState *env);
|
void r4k_helper_tlbr(CPUMIPSState *env);
|
||||||
|
void r4k_helper_tlbinv(CPUMIPSState *env);
|
||||||
|
void r4k_helper_tlbinvf(CPUMIPSState *env);
|
||||||
|
|
||||||
void mips_cpu_unassigned_access(CPUState *cpu, hwaddr addr,
|
void mips_cpu_unassigned_access(CPUState *cpu, hwaddr addr,
|
||||||
bool is_write, bool is_exec, int unused,
|
bool is_write, bool is_exec, int unused,
|
||||||
|
@ -83,7 +83,7 @@ int r4k_map_address (CPUMIPSState *env, hwaddr *physical, int *prot,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Check ASID, virtual page number & size */
|
/* Check ASID, virtual page number & size */
|
||||||
if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
|
if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag && !tlb->EHINV) {
|
||||||
/* TLB match */
|
/* TLB match */
|
||||||
int n = !!(address & mask & ~(mask >> 1));
|
int n = !!(address & mask & ~(mask >> 1));
|
||||||
/* Check access rights */
|
/* Check access rights */
|
||||||
|
@ -342,6 +342,8 @@ DEF_HELPER_1(tlbwi, void, env)
|
|||||||
DEF_HELPER_1(tlbwr, void, env)
|
DEF_HELPER_1(tlbwr, void, env)
|
||||||
DEF_HELPER_1(tlbp, void, env)
|
DEF_HELPER_1(tlbp, void, env)
|
||||||
DEF_HELPER_1(tlbr, void, env)
|
DEF_HELPER_1(tlbr, void, env)
|
||||||
|
DEF_HELPER_1(tlbinv, void, env)
|
||||||
|
DEF_HELPER_1(tlbinvf, void, env)
|
||||||
DEF_HELPER_1(di, tl, env)
|
DEF_HELPER_1(di, tl, env)
|
||||||
DEF_HELPER_1(ei, tl, env)
|
DEF_HELPER_1(ei, tl, env)
|
||||||
DEF_HELPER_1(eret, void, env)
|
DEF_HELPER_1(eret, void, env)
|
||||||
|
@ -1361,10 +1361,14 @@ void helper_mtc0_count(CPUMIPSState *env, target_ulong arg1)
|
|||||||
|
|
||||||
void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1)
|
void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1)
|
||||||
{
|
{
|
||||||
target_ulong old, val;
|
target_ulong old, val, mask;
|
||||||
|
mask = (TARGET_PAGE_MASK << 1) | 0xFF;
|
||||||
|
if (((env->CP0_Config4 >> CP0C4_IE) & 0x3) >= 2) {
|
||||||
|
mask |= 1 << CP0EnHi_EHINV;
|
||||||
|
}
|
||||||
|
|
||||||
/* 1k pages not implemented */
|
/* 1k pages not implemented */
|
||||||
val = arg1 & ((TARGET_PAGE_MASK << 1) | 0xFF);
|
val = arg1 & mask;
|
||||||
#if defined(TARGET_MIPS64)
|
#if defined(TARGET_MIPS64)
|
||||||
val &= env->SEGMask;
|
val &= env->SEGMask;
|
||||||
#endif
|
#endif
|
||||||
@ -1858,6 +1862,11 @@ static void r4k_fill_tlb(CPUMIPSState *env, int idx)
|
|||||||
|
|
||||||
/* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
|
/* XXX: detect conflicting TLBs and raise a MCHECK exception when needed */
|
||||||
tlb = &env->tlb->mmu.r4k.tlb[idx];
|
tlb = &env->tlb->mmu.r4k.tlb[idx];
|
||||||
|
if (env->CP0_EntryHi & (1 << CP0EnHi_EHINV)) {
|
||||||
|
tlb->EHINV = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tlb->EHINV = 0;
|
||||||
tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1);
|
tlb->VPN = env->CP0_EntryHi & (TARGET_PAGE_MASK << 1);
|
||||||
#if defined(TARGET_MIPS64)
|
#if defined(TARGET_MIPS64)
|
||||||
tlb->VPN &= env->SEGMask;
|
tlb->VPN &= env->SEGMask;
|
||||||
@ -1879,6 +1888,31 @@ static void r4k_fill_tlb(CPUMIPSState *env, int idx)
|
|||||||
tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
|
tlb->PFN[1] = (env->CP0_EntryLo1 >> 6) << 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void r4k_helper_tlbinv(CPUMIPSState *env)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
r4k_tlb_t *tlb;
|
||||||
|
uint8_t ASID = env->CP0_EntryHi & 0xFF;
|
||||||
|
|
||||||
|
for (idx = 0; idx < env->tlb->nb_tlb; idx++) {
|
||||||
|
tlb = &env->tlb->mmu.r4k.tlb[idx];
|
||||||
|
if (!tlb->G && tlb->ASID == ASID) {
|
||||||
|
tlb->EHINV = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cpu_mips_tlb_flush(env, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void r4k_helper_tlbinvf(CPUMIPSState *env)
|
||||||
|
{
|
||||||
|
int idx;
|
||||||
|
|
||||||
|
for (idx = 0; idx < env->tlb->nb_tlb; idx++) {
|
||||||
|
env->tlb->mmu.r4k.tlb[idx].EHINV = 1;
|
||||||
|
}
|
||||||
|
cpu_mips_tlb_flush(env, 1);
|
||||||
|
}
|
||||||
|
|
||||||
void r4k_helper_tlbwi(CPUMIPSState *env)
|
void r4k_helper_tlbwi(CPUMIPSState *env)
|
||||||
{
|
{
|
||||||
r4k_tlb_t *tlb;
|
r4k_tlb_t *tlb;
|
||||||
@ -1940,7 +1974,7 @@ void r4k_helper_tlbp(CPUMIPSState *env)
|
|||||||
tag &= env->SEGMask;
|
tag &= env->SEGMask;
|
||||||
#endif
|
#endif
|
||||||
/* Check ASID, virtual page number & size */
|
/* Check ASID, virtual page number & size */
|
||||||
if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag) {
|
if ((tlb->G == 1 || tlb->ASID == ASID) && VPN == tag && !tlb->EHINV) {
|
||||||
/* TLB match */
|
/* TLB match */
|
||||||
env->CP0_Index = i;
|
env->CP0_Index = i;
|
||||||
break;
|
break;
|
||||||
@ -1984,16 +2018,23 @@ void r4k_helper_tlbr(CPUMIPSState *env)
|
|||||||
|
|
||||||
r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb);
|
r4k_mips_tlb_flush_extra(env, env->tlb->nb_tlb);
|
||||||
|
|
||||||
env->CP0_EntryHi = tlb->VPN | tlb->ASID;
|
if (tlb->EHINV) {
|
||||||
env->CP0_PageMask = tlb->PageMask;
|
env->CP0_EntryHi = 1 << CP0EnHi_EHINV;
|
||||||
env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
|
env->CP0_PageMask = 0;
|
||||||
|
env->CP0_EntryLo0 = 0;
|
||||||
|
env->CP0_EntryLo1 = 0;
|
||||||
|
} else {
|
||||||
|
env->CP0_EntryHi = tlb->VPN | tlb->ASID;
|
||||||
|
env->CP0_PageMask = tlb->PageMask;
|
||||||
|
env->CP0_EntryLo0 = tlb->G | (tlb->V0 << 1) | (tlb->D0 << 2) |
|
||||||
((target_ulong)tlb->RI0 << CP0EnLo_RI) |
|
((target_ulong)tlb->RI0 << CP0EnLo_RI) |
|
||||||
((target_ulong)tlb->XI0 << CP0EnLo_XI) |
|
((target_ulong)tlb->XI0 << CP0EnLo_XI) |
|
||||||
(tlb->C0 << 3) | (tlb->PFN[0] >> 6);
|
(tlb->C0 << 3) | (tlb->PFN[0] >> 6);
|
||||||
env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
|
env->CP0_EntryLo1 = tlb->G | (tlb->V1 << 1) | (tlb->D1 << 2) |
|
||||||
((target_ulong)tlb->RI1 << CP0EnLo_RI) |
|
((target_ulong)tlb->RI1 << CP0EnLo_RI) |
|
||||||
((target_ulong)tlb->XI1 << CP0EnLo_XI) |
|
((target_ulong)tlb->XI1 << CP0EnLo_XI) |
|
||||||
(tlb->C1 << 3) | (tlb->PFN[1] >> 6);
|
(tlb->C1 << 3) | (tlb->PFN[1] >> 6);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void helper_tlbwi(CPUMIPSState *env)
|
void helper_tlbwi(CPUMIPSState *env)
|
||||||
@ -2016,6 +2057,16 @@ void helper_tlbr(CPUMIPSState *env)
|
|||||||
env->tlb->helper_tlbr(env);
|
env->tlb->helper_tlbr(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void helper_tlbinv(CPUMIPSState *env)
|
||||||
|
{
|
||||||
|
env->tlb->helper_tlbinv(env);
|
||||||
|
}
|
||||||
|
|
||||||
|
void helper_tlbinvf(CPUMIPSState *env)
|
||||||
|
{
|
||||||
|
env->tlb->helper_tlbinvf(env);
|
||||||
|
}
|
||||||
|
|
||||||
/* Specials */
|
/* Specials */
|
||||||
target_ulong helper_di(CPUMIPSState *env)
|
target_ulong helper_di(CPUMIPSState *env)
|
||||||
{
|
{
|
||||||
|
@ -896,6 +896,8 @@ enum {
|
|||||||
enum {
|
enum {
|
||||||
OPC_TLBR = 0x01 | OPC_C0,
|
OPC_TLBR = 0x01 | OPC_C0,
|
||||||
OPC_TLBWI = 0x02 | OPC_C0,
|
OPC_TLBWI = 0x02 | OPC_C0,
|
||||||
|
OPC_TLBINV = 0x03 | OPC_C0,
|
||||||
|
OPC_TLBINVF = 0x04 | OPC_C0,
|
||||||
OPC_TLBWR = 0x06 | OPC_C0,
|
OPC_TLBWR = 0x06 | OPC_C0,
|
||||||
OPC_TLBP = 0x08 | OPC_C0,
|
OPC_TLBP = 0x08 | OPC_C0,
|
||||||
OPC_RFE = 0x10 | OPC_C0,
|
OPC_RFE = 0x10 | OPC_C0,
|
||||||
@ -1172,6 +1174,7 @@ typedef struct DisasContext {
|
|||||||
bool ulri;
|
bool ulri;
|
||||||
int kscrexist;
|
int kscrexist;
|
||||||
bool rxi;
|
bool rxi;
|
||||||
|
int ie;
|
||||||
} DisasContext;
|
} DisasContext;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -7516,6 +7519,24 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt,
|
|||||||
goto die;
|
goto die;
|
||||||
gen_helper_tlbwi(cpu_env);
|
gen_helper_tlbwi(cpu_env);
|
||||||
break;
|
break;
|
||||||
|
case OPC_TLBINV:
|
||||||
|
opn = "tlbinv";
|
||||||
|
if (ctx->ie >= 2) {
|
||||||
|
if (!env->tlb->helper_tlbinv) {
|
||||||
|
goto die;
|
||||||
|
}
|
||||||
|
gen_helper_tlbinv(cpu_env);
|
||||||
|
} /* treat as nop if TLBINV not supported */
|
||||||
|
break;
|
||||||
|
case OPC_TLBINVF:
|
||||||
|
opn = "tlbinvf";
|
||||||
|
if (ctx->ie >= 2) {
|
||||||
|
if (!env->tlb->helper_tlbinvf) {
|
||||||
|
goto die;
|
||||||
|
}
|
||||||
|
gen_helper_tlbinvf(cpu_env);
|
||||||
|
} /* treat as nop if TLBINV not supported */
|
||||||
|
break;
|
||||||
case OPC_TLBWR:
|
case OPC_TLBWR:
|
||||||
opn = "tlbwr";
|
opn = "tlbwr";
|
||||||
if (!env->tlb->helper_tlbwr)
|
if (!env->tlb->helper_tlbwr)
|
||||||
@ -17478,6 +17499,7 @@ gen_intermediate_code_internal(MIPSCPU *cpu, TranslationBlock *tb,
|
|||||||
ctx.bstate = BS_NONE;
|
ctx.bstate = BS_NONE;
|
||||||
ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff;
|
ctx.kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff;
|
||||||
ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1;
|
ctx.rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1;
|
||||||
|
ctx.ie = (env->CP0_Config4 >> CP0C4_IE) & 3;
|
||||||
/* Restore delay slot state from the tb context. */
|
/* Restore delay slot state from the tb context. */
|
||||||
ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
|
ctx.hflags = (uint32_t)tb->flags; /* FIXME: maybe use 64 bits here? */
|
||||||
ctx.ulri = env->CP0_Config3 & (1 << CP0C3_ULRI);
|
ctx.ulri = env->CP0_Config3 & (1 << CP0C3_ULRI);
|
||||||
|
@ -657,6 +657,8 @@ static void r4k_mmu_init (CPUMIPSState *env, const mips_def_t *def)
|
|||||||
env->tlb->helper_tlbwr = r4k_helper_tlbwr;
|
env->tlb->helper_tlbwr = r4k_helper_tlbwr;
|
||||||
env->tlb->helper_tlbp = r4k_helper_tlbp;
|
env->tlb->helper_tlbp = r4k_helper_tlbp;
|
||||||
env->tlb->helper_tlbr = r4k_helper_tlbr;
|
env->tlb->helper_tlbr = r4k_helper_tlbr;
|
||||||
|
env->tlb->helper_tlbinv = r4k_helper_tlbinv;
|
||||||
|
env->tlb->helper_tlbinvf = r4k_helper_tlbinvf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmu_init (CPUMIPSState *env, const mips_def_t *def)
|
static void mmu_init (CPUMIPSState *env, const mips_def_t *def)
|
||||||
|
Loading…
Reference in New Issue
Block a user