target/openrisc: Reorg tlb lookup

While openrisc has a split i/d tlb, qemu does not.  Perform a
lookup on both i & d tlbs in parallel and put the composite
rights into qemu's tlb.  This avoids ping-ponging the qemu tlb
between EXEC and READ.

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
Signed-off-by: Stafford Horne <shorne@gmail.com>
This commit is contained in:
Richard Henderson 2018-05-22 23:14:02 -07:00 committed by Stafford Horne
parent 1cc9e5d896
commit f0655423ca
2 changed files with 90 additions and 172 deletions

View File

@ -237,14 +237,6 @@ enum {
UXE = (1 << 7), UXE = (1 << 7),
}; };
/* check if tlb available */
enum {
TLBRET_INVALID = -3,
TLBRET_NOMATCH = -2,
TLBRET_BADADDR = -1,
TLBRET_MATCH = 0
};
typedef struct OpenRISCTLBEntry { typedef struct OpenRISCTLBEntry {
uint32_t mr; uint32_t mr;
uint32_t tr; uint32_t tr;

View File

@ -29,148 +29,78 @@
#endif #endif
#ifndef CONFIG_USER_ONLY #ifndef CONFIG_USER_ONLY
static inline int get_phys_nommu(hwaddr *physical, int *prot, static inline void get_phys_nommu(hwaddr *phys_addr, int *prot,
target_ulong address) target_ulong address)
{ {
*physical = address; *phys_addr = address;
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
return TLBRET_MATCH;
} }
static int get_phys_code(OpenRISCCPU *cpu, hwaddr *physical, int *prot, static int get_phys_mmu(OpenRISCCPU *cpu, hwaddr *phys_addr, int *prot,
target_ulong address, int rw, bool supervisor) target_ulong addr, int need, bool super)
{ {
int vpn = address >> TARGET_PAGE_BITS; int idx = (addr >> TARGET_PAGE_BITS) & TLB_MASK;
int idx = vpn & TLB_MASK; uint32_t imr = cpu->env.tlb.itlb[idx].mr;
int right = 0; uint32_t itr = cpu->env.tlb.itlb[idx].tr;
uint32_t mr = cpu->env.tlb.itlb[idx].mr; uint32_t dmr = cpu->env.tlb.dtlb[idx].mr;
uint32_t tr = cpu->env.tlb.itlb[idx].tr; uint32_t dtr = cpu->env.tlb.dtlb[idx].tr;
int right, match, valid;
if ((mr >> TARGET_PAGE_BITS) != vpn) { /* If the ITLB and DTLB indexes map to the same page, we want to
return TLBRET_NOMATCH; load all permissions all at once. If the destination pages do
} not match, zap the one we don't need. */
if (!(mr & 1)) { if (unlikely((itr ^ dtr) & TARGET_PAGE_MASK)) {
return TLBRET_INVALID; if (need & PAGE_EXEC) {
} dmr = dtr = 0;
if (supervisor) { } else {
if (tr & SXE) { imr = itr = 0;
right |= PAGE_EXEC;
}
} else {
if (tr & UXE) {
right |= PAGE_EXEC;
} }
} }
if ((rw & 2) && ((right & PAGE_EXEC) == 0)) {
return TLBRET_BADADDR;
}
*physical = (tr & TARGET_PAGE_MASK) | (address & ~TARGET_PAGE_MASK); /* Check if either of the entries matches the source address. */
match = (imr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_EXEC;
match |= (dmr ^ addr) & TARGET_PAGE_MASK ? 0 : PAGE_READ | PAGE_WRITE;
/* Check if either of the entries is valid. */
valid = imr & 1 ? PAGE_EXEC : 0;
valid |= dmr & 1 ? PAGE_READ | PAGE_WRITE : 0;
valid &= match;
/* Collect the permissions from the entries. */
right = itr & (super ? SXE : UXE) ? PAGE_EXEC : 0;
right |= dtr & (super ? SRE : URE) ? PAGE_READ : 0;
right |= dtr & (super ? SWE : UWE) ? PAGE_WRITE : 0;
right &= valid;
/* Note that above we validated that itr and dtr match on page.
So oring them together changes nothing without having to
check which one we needed. We also want to store to these
variables even on failure, as it avoids compiler warnings. */
*phys_addr = ((itr | dtr) & TARGET_PAGE_MASK) | (addr & ~TARGET_PAGE_MASK);
*prot = right; *prot = right;
return TLBRET_MATCH;
}
static int get_phys_data(OpenRISCCPU *cpu, hwaddr *physical, int *prot, qemu_log_mask(CPU_LOG_MMU,
target_ulong address, int rw, bool supervisor) "MMU lookup: need %d match %d valid %d right %d -> %s\n",
{ need, match, valid, right, (need & right) ? "OK" : "FAIL");
int vpn = address >> TARGET_PAGE_BITS;
int idx = vpn & TLB_MASK;
int right = 0;
uint32_t mr = cpu->env.tlb.dtlb[idx].mr;
uint32_t tr = cpu->env.tlb.dtlb[idx].tr;
if ((mr >> TARGET_PAGE_BITS) != vpn) { /* Check the collective permissions are present. */
return TLBRET_NOMATCH; if (likely(need & right)) {
return 0; /* success! */
} }
if (!(mr & 1)) {
return TLBRET_INVALID; /* Determine what kind of failure we have. */
} if (need & valid) {
if (supervisor) { return need & PAGE_EXEC ? EXCP_IPF : EXCP_DPF;
if (tr & SRE) {
right |= PAGE_READ;
}
if (tr & SWE) {
right |= PAGE_WRITE;
}
} else { } else {
if (tr & URE) { return need & PAGE_EXEC ? EXCP_ITLBMISS : EXCP_DTLBMISS;
right |= PAGE_READ;
}
if (tr & UWE) {
right |= PAGE_WRITE;
}
} }
if (!(rw & 1) && ((right & PAGE_READ) == 0)) {
return TLBRET_BADADDR;
}
if ((rw & 1) && ((right & PAGE_WRITE) == 0)) {
return TLBRET_BADADDR;
}
*physical = (tr & TARGET_PAGE_MASK) | (address & ~TARGET_PAGE_MASK);
*prot = right;
return TLBRET_MATCH;
}
static int get_phys_addr(OpenRISCCPU *cpu, hwaddr *physical,
int *prot, target_ulong address, int rw)
{
bool supervisor = (cpu->env.sr & SR_SM) != 0;
int ret;
/* Assume nommu results for a moment. */
ret = get_phys_nommu(physical, prot, address);
/* Overwrite with TLB lookup if enabled. */
if (rw == MMU_INST_FETCH) {
if (cpu->env.sr & SR_IME) {
ret = get_phys_code(cpu, physical, prot, address, rw, supervisor);
}
} else {
if (cpu->env.sr & SR_DME) {
ret = get_phys_data(cpu, physical, prot, address, rw, supervisor);
}
}
return ret;
} }
#endif #endif
static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu, static void raise_mmu_exception(OpenRISCCPU *cpu, target_ulong address,
target_ulong address, int exception)
int rw, int tlb_error)
{ {
CPUState *cs = CPU(cpu); CPUState *cs = CPU(cpu);
int exception = 0;
switch (tlb_error) {
default:
if (rw == 2) {
exception = EXCP_IPF;
} else {
exception = EXCP_DPF;
}
break;
#ifndef CONFIG_USER_ONLY
case TLBRET_BADADDR:
if (rw == 2) {
exception = EXCP_IPF;
} else {
exception = EXCP_DPF;
}
break;
case TLBRET_INVALID:
case TLBRET_NOMATCH:
/* No TLB match for a mapped address */
if (rw == 2) {
exception = EXCP_ITLBMISS;
} else {
exception = EXCP_DTLBMISS;
}
break;
#endif
}
cs->exception_index = exception; cs->exception_index = exception;
cpu->env.eear = address; cpu->env.eear = address;
@ -182,7 +112,7 @@ int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
{ {
#ifdef CONFIG_USER_ONLY #ifdef CONFIG_USER_ONLY
OpenRISCCPU *cpu = OPENRISC_CPU(cs); OpenRISCCPU *cpu = OPENRISC_CPU(cs);
cpu_openrisc_raise_mmu_exception(cpu, address, rw, 0); raise_mmu_exception(cpu, address, EXCP_DPF);
return 1; return 1;
#else #else
g_assert_not_reached(); g_assert_not_reached();
@ -193,27 +123,32 @@ int openrisc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size,
hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{ {
OpenRISCCPU *cpu = OPENRISC_CPU(cs); OpenRISCCPU *cpu = OPENRISC_CPU(cs);
int prot, excp, sr = cpu->env.sr;
hwaddr phys_addr; hwaddr phys_addr;
int prot;
int miss;
/* Check memory for any kind of address, since during debug the switch (sr & (SR_DME | SR_IME)) {
gdb can ask for anything, check data tlb for address */ case SR_DME | SR_IME:
miss = get_phys_addr(cpu, &phys_addr, &prot, addr, 0); /* The mmu is definitely enabled. */
excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
PAGE_EXEC | PAGE_READ | PAGE_WRITE,
(sr & SR_SM) != 0);
return excp ? -1 : phys_addr;
/* Check instruction tlb */ default:
if (miss) { /* The mmu is partially enabled, and we don't really have
miss = get_phys_addr(cpu, &phys_addr, &prot, addr, MMU_INST_FETCH); a "real" access type. Begin by trying the mmu, but if
} that fails try again without. */
excp = get_phys_mmu(cpu, &phys_addr, &prot, addr,
PAGE_EXEC | PAGE_READ | PAGE_WRITE,
(sr & SR_SM) != 0);
if (!excp) {
return phys_addr;
}
/* fallthru */
/* Last, fall back to a plain address */ case 0:
if (miss) { /* The mmu is definitely disabled; lookups never fail. */
miss = get_phys_nommu(&phys_addr, &prot, addr); get_phys_nommu(&phys_addr, &prot, addr);
}
if (miss) {
return -1;
} else {
return phys_addr; return phys_addr;
} }
} }
@ -222,37 +157,28 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size,
MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) MMUAccessType access_type, int mmu_idx, uintptr_t retaddr)
{ {
OpenRISCCPU *cpu = OPENRISC_CPU(cs); OpenRISCCPU *cpu = OPENRISC_CPU(cs);
int ret, prot = 0; int prot, excp;
hwaddr physical = 0; hwaddr phys_addr;
if (mmu_idx == MMU_NOMMU_IDX) { if (mmu_idx == MMU_NOMMU_IDX) {
ret = get_phys_nommu(&physical, &prot, addr); /* The mmu is disabled; lookups never fail. */
get_phys_nommu(&phys_addr, &prot, addr);
excp = 0;
} else { } else {
bool super = mmu_idx == MMU_SUPERVISOR_IDX; bool super = mmu_idx == MMU_SUPERVISOR_IDX;
if (access_type == MMU_INST_FETCH) { int need = (access_type == MMU_INST_FETCH ? PAGE_EXEC
ret = get_phys_code(cpu, &physical, &prot, addr, 2, super); : access_type == MMU_DATA_STORE ? PAGE_WRITE
} else { : PAGE_READ);
ret = get_phys_data(cpu, &physical, &prot, addr, excp = get_phys_mmu(cpu, &phys_addr, &prot, addr, need, super);
access_type == MMU_DATA_STORE, super);
}
} }
if (ret == TLBRET_MATCH) { if (unlikely(excp)) {
tlb_set_page(cs, addr & TARGET_PAGE_MASK, raise_mmu_exception(cpu, addr, excp);
physical & TARGET_PAGE_MASK, prot,
mmu_idx, TARGET_PAGE_SIZE);
} else if (ret < 0) {
int rw;
if (access_type == MMU_INST_FETCH) {
rw = 2;
} else if (access_type == MMU_DATA_STORE) {
rw = 1;
} else {
rw = 0;
}
cpu_openrisc_raise_mmu_exception(cpu, addr, rw, ret);
/* Raise Exception. */
cpu_loop_exit_restore(cs, retaddr); cpu_loop_exit_restore(cs, retaddr);
} }
tlb_set_page(cs, addr & TARGET_PAGE_MASK,
phys_addr & TARGET_PAGE_MASK, prot,
mmu_idx, TARGET_PAGE_SIZE);
} }
#endif #endif