diff --git a/hw/sh7750.c b/hw/sh7750.c index a76e6d4d3c..0ff3e6d778 100644 --- a/hw/sh7750.c +++ b/hw/sh7750.c @@ -30,6 +30,7 @@ #include "sh7750_regs.h" #include "sh7750_regnames.h" #include "sh_intc.h" +#include "cpu.h" #define NB_DEVICES 4 @@ -532,10 +533,113 @@ static struct intc_group groups_pci[] = { #define SH_CPU_SH7750_ALL (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7750R) #define SH_CPU_SH7751_ALL (SH_CPU_SH7751 | SH_CPU_SH7751R) +/********************************************************************** + Memory mapped cache and TLB +**********************************************************************/ + +#define MM_REGION_MASK 0x07000000 +#define MM_ICACHE_ADDR (0) +#define MM_ICACHE_DATA (1) +#define MM_ITLB_ADDR (2) +#define MM_ITLB_DATA (3) +#define MM_OCACHE_ADDR (4) +#define MM_OCACHE_DATA (5) +#define MM_UTLB_ADDR (6) +#define MM_UTLB_DATA (7) +#define MM_REGION_TYPE(addr) ((addr & MM_REGION_MASK) >> 24) + +static uint32_t invalid_read(void *opaque, target_phys_addr_t addr) +{ + assert(0); + + return 0; +} + +static uint32_t sh7750_mmct_readl(void *opaque, target_phys_addr_t addr) +{ + uint32_t ret = 0; + + switch (MM_REGION_TYPE(addr)) { + case MM_ICACHE_ADDR: + case MM_ICACHE_DATA: + /* do nothing */ + break; + case MM_ITLB_ADDR: + case MM_ITLB_DATA: + /* XXXXX */ + assert(0); + break; + case MM_OCACHE_ADDR: + case MM_OCACHE_DATA: + /* do nothing */ + break; + case MM_UTLB_ADDR: + case MM_UTLB_DATA: + /* XXXXX */ + assert(0); + break; + default: + assert(0); + } + + return ret; +} + +static void invalid_write(void *opaque, target_phys_addr_t addr, + uint32_t mem_value) +{ + assert(0); +} + +static void sh7750_mmct_writel(void *opaque, target_phys_addr_t addr, + uint32_t mem_value) +{ + SH7750State *s = opaque; + + switch (MM_REGION_TYPE(addr)) { + case MM_ICACHE_ADDR: + case MM_ICACHE_DATA: + /* do nothing */ + break; + case MM_ITLB_ADDR: + case MM_ITLB_DATA: + /* XXXXX */ + assert(0); + break; + case MM_OCACHE_ADDR: + case MM_OCACHE_DATA: + /* do nothing */ + break; + case MM_UTLB_ADDR: + cpu_sh4_write_mmaped_utlb_addr(s->cpu, addr, mem_value); + break; + case MM_UTLB_DATA: + /* XXXXX */ + assert(0); + break; + default: + assert(0); + break; + } +} + +static CPUReadMemoryFunc *sh7750_mmct_read[] = { + invalid_read, + invalid_read, + sh7750_mmct_readl +}; + +static CPUWriteMemoryFunc *sh7750_mmct_write[] = { + invalid_write, + invalid_write, + sh7750_mmct_writel +}; + SH7750State *sh7750_init(CPUSH4State * cpu) { SH7750State *s; int sh7750_io_memory; + int sh7750_mm_cache_and_tlb; /* memory mapped cache and tlb */ int cpu_model = SH_CPU_SH7751R; /* for now */ s = qemu_mallocz(sizeof(SH7750State)); @@ -546,6 +650,12 @@ SH7750State *sh7750_init(CPUSH4State * cpu) sh7750_mem_write, s); cpu_register_physical_memory(0x1c000000, 0x04000000, sh7750_io_memory); + sh7750_mm_cache_and_tlb = cpu_register_io_memory(0, + sh7750_mmct_read, + sh7750_mmct_write, s); + cpu_register_physical_memory(0xf0000000, 0x08000000, + sh7750_mm_cache_and_tlb); + sh_intc_init(&s->intc, NR_SOURCES, _INTC_ARRAY(mask_registers), _INTC_ARRAY(prio_registers)); diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h index 601cb247cc..021aced344 100644 --- a/target-sh4/cpu.h +++ b/target-sh4/cpu.h @@ -124,6 +124,8 @@ CPUSH4State *cpu_sh4_init(const char *cpu_model); int cpu_sh4_exec(CPUSH4State * s); int cpu_sh4_signal_handler(int host_signum, void *pinfo, void *puc); +void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value); #include "softfloat.h" diff --git a/target-sh4/helper.c b/target-sh4/helper.c index 6be544cf85..06df9d1f35 100644 --- a/target-sh4/helper.c +++ b/target-sh4/helper.c @@ -282,6 +282,29 @@ static int find_tlb_entry(CPUState * env, target_ulong address, return match; } +static int same_tlb_entry_exists(const tlb_t * haystack, uint8_t nbtlb, + const tlb_t * needle) +{ + int i; + for (i = 0; i < nbtlb; i++) + if (!memcmp(&haystack[i], needle, sizeof(tlb_t))) + return 1; + return 0; +} + +static void increment_urc(CPUState * env) +{ + uint8_t urb, urc; + + /* Increment URC */ + urb = ((env->mmucr) >> 18) & 0x3f; + urc = ((env->mmucr) >> 10) & 0x3f; + urc++; + if (urc == urb || urc == UTLB_SIZE - 1) + urc = 0; + env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10); +} + /* Find itlb entry - update itlb from utlb if necessary and asked for Return entry, MMU_ITLB_MISS, MMU_ITLB_MULTIPLE or MMU_DTLB_MULTIPLE Update the itlb from utlb if update is not 0 @@ -313,15 +336,8 @@ int find_itlb_entry(CPUState * env, target_ulong address, Return entry, MMU_DTLB_MISS, MMU_DTLB_MULTIPLE */ int find_utlb_entry(CPUState * env, target_ulong address, int use_asid) { - uint8_t urb, urc; - - /* Increment URC */ - urb = ((env->mmucr) >> 18) & 0x3f; - urc = ((env->mmucr) >> 10) & 0x3f; - urc++; - if (urc == urb || urc == UTLB_SIZE - 1) - urc = 0; - env->mmucr = (env->mmucr & 0xffff03ff) | (urc << 10); + /* per utlb access */ + increment_urc(env); /* Return entry */ return find_tlb_entry(env, address, env->utlb, UTLB_SIZE, use_asid); @@ -407,8 +423,21 @@ int get_physical_address(CPUState * env, target_ulong * physical, return (rw & PAGE_WRITE) ? MMU_DTLB_MISS_WRITE : MMU_DTLB_MISS_READ; } - /* Mask upper 3 bits */ - *physical = address & 0x1FFFFFFF; + if (address >= 0x80000000 && address < 0xc0000000) { + /* Mask upper 3 bits for P1 and P2 areas */ + *physical = address & 0x1fffffff; + } else if (address >= 0xfc000000) { + /* + * Mask upper 3 bits for control registers in P4 area, + * to unify access to control registers via P0-P3 area. + * The addresses for cache store queue, TLB address array + * are not masked. + */ + *physical = address & 0x1fffffff; + } else { + /* access to cache store queue, or TLB address array. */ + *physical = address; + } *prot = PAGE_READ | PAGE_WRITE; return MMU_OK; } @@ -543,4 +572,75 @@ void cpu_load_tlb(CPUState * env) entry->tc = (uint8_t)cpu_ptea_tc(env->ptea); } +void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, target_phys_addr_t addr, + uint32_t mem_value) +{ + int associate = addr & 0x0000080; + uint32_t vpn = (mem_value & 0xfffffc00) >> 10; + uint8_t d = (uint8_t)((mem_value & 0x00000200) >> 9); + uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); + uint8_t asid = (uint8_t)(mem_value & 0x000000ff); + + if (associate) { + int i; + tlb_t * utlb_match_entry = NULL; + int needs_tlb_flush = 0; + + /* search UTLB */ + for (i = 0; i < UTLB_SIZE; i++) { + tlb_t * entry = &s->utlb[i]; + if (!entry->v) + continue; + + if (entry->vpn == vpn && entry->asid == asid) { + if (utlb_match_entry) { + /* Multiple TLB Exception */ + s->exception_index = 0x140; + s->tea = addr; + break; + } + if (entry->v && !v) + needs_tlb_flush = 1; + entry->v = v; + entry->d = d; + utlb_match_entry = entry; + } + increment_urc(s); /* per utlb access */ + } + + /* search ITLB */ + for (i = 0; i < ITLB_SIZE; i++) { + tlb_t * entry = &s->itlb[i]; + if (entry->vpn == vpn && entry->asid == asid) { + if (entry->v && !v) + needs_tlb_flush = 1; + if (utlb_match_entry) + *entry = *utlb_match_entry; + else + entry->v = v; + break; + } + } + + if (needs_tlb_flush) + tlb_flush_page(s, vpn << 10); + + } else { + int index = (addr & 0x00003f00) >> 8; + tlb_t * entry = &s->utlb[index]; + if (entry->v) { + /* Overwriting valid entry in utlb. */ + target_ulong address = entry->vpn << 10; + if (!same_tlb_entry_exists(s->itlb, ITLB_SIZE, entry)) { + tlb_flush_page(s, address); + } + } + entry->asid = asid; + entry->vpn = vpn; + entry->d = d; + entry->v = v; + increment_urc(s); + } +} + #endif