X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=target-sparc%2Fhelper.c;h=efab885b8375e6534f244d67f375c7c07aa7ceed;hb=7ec7f28019dff4ab00ff907f7b4e7261da593279;hp=920432cb195ef8e53b30916ab9aa471ed8776884;hpb=6b7432786948250bb4b1da7770e94552a5b06af3;p=qemu.git diff --git a/target-sparc/helper.c b/target-sparc/helper.c index 920432cb1..efab885b8 100644 --- a/target-sparc/helper.c +++ b/target-sparc/helper.c @@ -21,33 +21,24 @@ #include #include #include -#include #include "cpu.h" -#include "exec-all.h" #include "qemu-common.h" //#define DEBUG_MMU //#define DEBUG_FEATURES +#ifdef DEBUG_MMU +#define DPRINTF_MMU(fmt, ...) \ + do { printf("MMU: " fmt , ## __VA_ARGS__); } while (0) +#else +#define DPRINTF_MMU(fmt, ...) do {} while (0) +#endif + static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model); /* Sparc MMU emulation */ -/* thread support */ - -static spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; - -void cpu_lock(void) -{ - spin_lock(&global_cpu_lock); -} - -void cpu_unlock(void) -{ - spin_unlock(&global_cpu_lock); -} - #if defined(CONFIG_USER_ONLY) int cpu_sparc_handle_mmu_fault(CPUState *env1, target_ulong address, int rw, @@ -102,19 +93,19 @@ static const int perm_table[2][8] = { static int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot, int *access_index, - target_ulong address, int rw, int mmu_idx) + target_ulong address, int rw, int mmu_idx, + target_ulong *page_size) { int access_perms = 0; target_phys_addr_t pde_ptr; uint32_t pde; - target_ulong virt_addr; int error_code = 0, is_dirty, is_user; unsigned long page_offset; is_user = mmu_idx == MMU_USER_IDX; - virt_addr = address & TARGET_PAGE_MASK; if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */ + *page_size = TARGET_PAGE_SIZE; // Boot mode: instruction fetches are taken from PROM if (rw == 2 && (env->mmuregs[0] & env->def->mmu_bm)) { *physical = env->prom_addr | (address & 0x7ffffULL); @@ -174,22 +165,28 @@ static int get_physical_address(CPUState *env, target_phys_addr_t *physical, case 3: /* Reserved */ return (3 << 8) | (4 << 2); case 2: /* L3 PTE */ - virt_addr = address & TARGET_PAGE_MASK; page_offset = (address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1); } + *page_size = TARGET_PAGE_SIZE; break; case 2: /* L2 PTE */ - virt_addr = address & ~0x3ffff; page_offset = address & 0x3ffff; + *page_size = 0x40000; } break; case 2: /* L1 PTE */ - virt_addr = address & ~0xffffff; page_offset = address & 0xffffff; + *page_size = 0x1000000; } } + /* check access */ + access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; + error_code = access_table[*access_index][access_perms]; + if (error_code && !((env->mmuregs[0] & MMU_NF) && is_user)) + return error_code; + /* update page modified and dirty bits */ is_dirty = (rw & 1) && !(pde & PG_MODIFIED_MASK); if (!(pde & PG_ACCESSED_MASK) || is_dirty) { @@ -198,11 +195,6 @@ static int get_physical_address(CPUState *env, target_phys_addr_t *physical, pde |= PG_MODIFIED_MASK; stl_phys_notdirty(pde_ptr, pde); } - /* check access */ - access_perms = (pde & PTE_ACCESS_MASK) >> PTE_ACCESS_SHIFT; - error_code = access_table[*access_index][access_perms]; - if (error_code && !((env->mmuregs[0] & MMU_NF) && is_user)) - return error_code; /* the page can be put in the TLB */ *prot = perm_table[is_user][access_perms]; @@ -224,10 +216,11 @@ int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, { target_phys_addr_t paddr; target_ulong vaddr; - int error_code = 0, prot, ret = 0, access_index; + target_ulong page_size; + int error_code = 0, prot, access_index; error_code = get_physical_address(env, &paddr, &prot, &access_index, - address, rw, mmu_idx); + address, rw, mmu_idx, &page_size); if (error_code == 0) { vaddr = address & TARGET_PAGE_MASK; paddr &= TARGET_PAGE_MASK; @@ -235,8 +228,8 @@ int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, printf("Translate at " TARGET_FMT_lx " -> " TARGET_FMT_plx ", vaddr " TARGET_FMT_lx "\n", address, paddr, vaddr); #endif - ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu); - return ret; + tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size); + return 0; } if (env->mmuregs[3]) /* Fault status register */ @@ -251,8 +244,8 @@ int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, // switching to normal mode. vaddr = address & TARGET_PAGE_MASK; prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; - ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu); - return ret; + tlb_set_page(env, vaddr, paddr, prot, mmu_idx, TARGET_PAGE_SIZE); + return 0; } else { if (rw & 2) env->exception_index = TT_TFAULT; @@ -325,47 +318,45 @@ target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev) return 0; } -#ifdef DEBUG_MMU -void dump_mmu(CPUState *env) +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) { target_ulong va, va1, va2; unsigned int n, m, o; target_phys_addr_t pde_ptr, pa; uint32_t pde; - printf("MMU dump:\n"); pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2); pde = ldl_phys(pde_ptr); - printf("Root ptr: " TARGET_FMT_plx ", ctx: %d\n", - (target_phys_addr_t)env->mmuregs[1] << 4, env->mmuregs[2]); + (*cpu_fprintf)(f, "Root ptr: " TARGET_FMT_plx ", ctx: %d\n", + (target_phys_addr_t)env->mmuregs[1] << 4, env->mmuregs[2]); for (n = 0, va = 0; n < 256; n++, va += 16 * 1024 * 1024) { pde = mmu_probe(env, va, 2); if (pde) { pa = cpu_get_phys_page_debug(env, va); - printf("VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx - " PDE: " TARGET_FMT_lx "\n", va, pa, pde); + (*cpu_fprintf)(f, "VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx + " PDE: " TARGET_FMT_lx "\n", va, pa, pde); for (m = 0, va1 = va; m < 64; m++, va1 += 256 * 1024) { pde = mmu_probe(env, va1, 1); if (pde) { pa = cpu_get_phys_page_debug(env, va1); - printf(" VA: " TARGET_FMT_lx ", PA: " TARGET_FMT_plx - " PDE: " TARGET_FMT_lx "\n", va1, pa, pde); + (*cpu_fprintf)(f, " VA: " TARGET_FMT_lx ", PA: " + TARGET_FMT_plx " PDE: " TARGET_FMT_lx "\n", + va1, pa, pde); for (o = 0, va2 = va1; o < 64; o++, va2 += 4 * 1024) { pde = mmu_probe(env, va2, 0); if (pde) { pa = cpu_get_phys_page_debug(env, va2); - printf(" VA: " TARGET_FMT_lx ", PA: " - TARGET_FMT_plx " PTE: " TARGET_FMT_lx "\n", - va2, pa, pde); + (*cpu_fprintf)(f, " VA: " TARGET_FMT_lx ", PA: " + TARGET_FMT_plx " PTE: " + TARGET_FMT_lx "\n", + va2, pa, pde); } } } } } } - printf("MMU dump ends\n"); } -#endif /* DEBUG_MMU */ #else /* !TARGET_SPARC64 */ @@ -379,11 +370,6 @@ static inline target_phys_addr_t ultrasparc_truncate_physical(uint64_t x) * UltraSparc IIi I/DMMUs */ -static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask) -{ - return (x & mask) == (y & mask); -} - // Returns true if TTE tag is valid and matches virtual address value in context // requires virtual address mask value calculated from TTE entry size static inline int ultrasparc_tag_match(SparcTLBEntry *tlb, @@ -392,7 +378,7 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb, { uint64_t mask; - switch ((tlb->tte >> 61) & 3) { + switch (TTE_PGSIZE(tlb->tte)) { default: case 0x0: // 8k mask = 0xffffffffffffe000ULL; @@ -410,8 +396,8 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb, // valid, context match, virtual address match? if (TTE_IS_VALID(tlb->tte) && - compare_masked(context, tlb->tag, 0x1fff) && - compare_masked(address, tlb->tag, mask)) + (TTE_IS_GLOBAL(tlb->tte) || tlb_compare_context(tlb, context)) + && compare_masked(address, tlb->tag, mask)) { // decode physical address *physical = ((tlb->tte & mask) | (address & ~mask)) & 0x1ffffffe000ULL; @@ -423,10 +409,14 @@ static inline int ultrasparc_tag_match(SparcTLBEntry *tlb, static int get_physical_address_data(CPUState *env, target_phys_addr_t *physical, int *prot, - target_ulong address, int rw, int is_user) + target_ulong address, int rw, int mmu_idx) { unsigned int i; uint64_t context; + uint64_t sfsr = 0; + + int is_user = (mmu_idx == MMU_USER_IDX || + mmu_idx == MMU_USER_SECONDARY_IDX); if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */ *physical = ultrasparc_truncate_physical(address); @@ -434,47 +424,109 @@ static int get_physical_address_data(CPUState *env, return 0; } - context = env->dmmu.mmu_primary_context & 0x1fff; + switch(mmu_idx) { + case MMU_USER_IDX: + case MMU_KERNEL_IDX: + context = env->dmmu.mmu_primary_context & 0x1fff; + sfsr |= SFSR_CT_PRIMARY; + break; + case MMU_USER_SECONDARY_IDX: + case MMU_KERNEL_SECONDARY_IDX: + context = env->dmmu.mmu_secondary_context & 0x1fff; + sfsr |= SFSR_CT_SECONDARY; + break; + case MMU_NUCLEUS_IDX: + sfsr |= SFSR_CT_NUCLEUS; + /* FALLTHRU */ + default: + context = 0; + break; + } + + if (rw == 1) { + sfsr |= SFSR_WRITE_BIT; + } else if (rw == 4) { + sfsr |= SFSR_NF_BIT; + } for (i = 0; i < 64; i++) { // ctx match, vaddr match, valid? - if (ultrasparc_tag_match(&env->dtlb[i], - address, context, physical) - ) { + if (ultrasparc_tag_match(&env->dtlb[i], address, context, physical)) { + int do_fault = 0; + // access ok? - if (((env->dtlb[i].tte & 0x4) && is_user) || - (!(env->dtlb[i].tte & 0x2) && (rw == 1))) { - uint8_t fault_type = 0; + /* multiple bits in SFSR.FT may be set on TT_DFAULT */ + if (TTE_IS_PRIV(env->dtlb[i].tte) && is_user) { + do_fault = 1; + sfsr |= SFSR_FT_PRIV_BIT; /* privilege violation */ + + DPRINTF_MMU("DFAULT at %" PRIx64 " context %" PRIx64 + " mmu_idx=%d tl=%d\n", + address, context, mmu_idx, env->tl); + } + if (rw == 4) { + if (TTE_IS_SIDEEFFECT(env->dtlb[i].tte)) { + do_fault = 1; + sfsr |= SFSR_FT_NF_E_BIT; + } + } else { + if (TTE_IS_NFO(env->dtlb[i].tte)) { + do_fault = 1; + sfsr |= SFSR_FT_NFO_BIT; + } + } + + if (do_fault) { + /* faults above are reported with TT_DFAULT. */ + env->exception_index = TT_DFAULT; + } else if (!TTE_IS_W_OK(env->dtlb[i].tte) && (rw == 1)) { + do_fault = 1; + env->exception_index = TT_DPROT; + + DPRINTF_MMU("DPROT at %" PRIx64 " context %" PRIx64 + " mmu_idx=%d tl=%d\n", + address, context, mmu_idx, env->tl); + } - if ((env->dtlb[i].tte & 0x4) && is_user) { - fault_type |= 1; /* privilege violation */ + if (!do_fault) { + *prot = PAGE_READ; + if (TTE_IS_W_OK(env->dtlb[i].tte)) { + *prot |= PAGE_WRITE; } - if (env->dmmu.sfsr & 1) /* Fault status register */ - env->dmmu.sfsr = 2; /* overflow (not read before - another fault) */ + TTE_SET_USED(env->dtlb[i].tte); - env->dmmu.sfsr |= (is_user << 3) | ((rw == 1) << 2) | 1; + return 0; + } - env->dmmu.sfsr |= (fault_type << 7); + if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */ + sfsr |= SFSR_OW_BIT; /* overflow (not read before + another fault) */ + } - env->dmmu.sfar = address; /* Fault address register */ - env->exception_index = TT_DFAULT; -#ifdef DEBUG_MMU - printf("DFAULT at 0x%" PRIx64 "\n", address); -#endif - return 1; + if (env->pstate & PS_PRIV) { + sfsr |= SFSR_PR_BIT; } - *prot = PAGE_READ; - if (env->dtlb[i].tte & 0x2) - *prot |= PAGE_WRITE; - TTE_SET_USED(env->dtlb[i].tte); - return 0; + + /* FIXME: ASI field in SFSR must be set */ + env->dmmu.sfsr = sfsr | SFSR_VALID_BIT; + + env->dmmu.sfar = address; /* Fault address register */ + + env->dmmu.tag_access = (address & ~0x1fffULL) | context; + + return 1; } } -#ifdef DEBUG_MMU - printf("DMISS at 0x%" PRIx64 "\n", address); -#endif + + DPRINTF_MMU("DMISS at %" PRIx64 " context %" PRIx64 "\n", + address, context); + + /* + * On MMU misses: + * - UltraSPARC IIi: SFSR and SFAR unmodified + * - JPS1: SFAR updated and some fields of SFSR updated + */ env->dmmu.tag_access = (address & ~0x1fffULL) | context; env->exception_index = TT_DMISS; return 1; @@ -482,11 +534,14 @@ static int get_physical_address_data(CPUState *env, static int get_physical_address_code(CPUState *env, target_phys_addr_t *physical, int *prot, - target_ulong address, int is_user) + target_ulong address, int mmu_idx) { unsigned int i; uint64_t context; + int is_user = (mmu_idx == MMU_USER_IDX || + mmu_idx == MMU_USER_SECONDARY_IDX); + if ((env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0) { /* IMMU disabled */ *physical = ultrasparc_truncate_physical(address); @@ -494,23 +549,43 @@ static int get_physical_address_code(CPUState *env, return 0; } - context = env->dmmu.mmu_primary_context & 0x1fff; + if (env->tl == 0) { + /* PRIMARY context */ + context = env->dmmu.mmu_primary_context & 0x1fff; + } else { + /* NUCLEUS context */ + context = 0; + } for (i = 0; i < 64; i++) { // ctx match, vaddr match, valid? if (ultrasparc_tag_match(&env->itlb[i], - address, context, physical) - ) { + address, context, physical)) { // access ok? - if ((env->itlb[i].tte & 0x4) && is_user) { - if (env->immu.sfsr) /* Fault status register */ - env->immu.sfsr = 2; /* overflow (not read before - another fault) */ - env->immu.sfsr |= (is_user << 3) | 1; + if (TTE_IS_PRIV(env->itlb[i].tte) && is_user) { + /* Fault status register */ + if (env->immu.sfsr & SFSR_VALID_BIT) { + env->immu.sfsr = SFSR_OW_BIT; /* overflow (not read before + another fault) */ + } else { + env->immu.sfsr = 0; + } + if (env->pstate & PS_PRIV) { + env->immu.sfsr |= SFSR_PR_BIT; + } + if (env->tl > 0) { + env->immu.sfsr |= SFSR_CT_NUCLEUS; + } + + /* FIXME: ASI field in SFSR must be set */ + env->immu.sfsr |= SFSR_FT_PRIV_BIT | SFSR_VALID_BIT; env->exception_index = TT_TFAULT; -#ifdef DEBUG_MMU - printf("TFAULT at 0x%" PRIx64 "\n", address); -#endif + + env->immu.tag_access = (address & ~0x1fffULL) | context; + + DPRINTF_MMU("TFAULT at %" PRIx64 " context %" PRIx64 "\n", + address, context); + return 1; } *prot = PAGE_EXEC; @@ -518,9 +593,10 @@ static int get_physical_address_code(CPUState *env, return 0; } } -#ifdef DEBUG_MMU - printf("TMISS at 0x%" PRIx64 "\n", address); -#endif + + DPRINTF_MMU("TMISS at %" PRIx64 " context %" PRIx64 "\n", + address, context); + /* Context is stored in DMMU (dmmuregs[1]) also for IMMU */ env->immu.tag_access = (address & ~0x1fffULL) | context; env->exception_index = TT_TMISS; @@ -529,16 +605,35 @@ static int get_physical_address_code(CPUState *env, static int get_physical_address(CPUState *env, target_phys_addr_t *physical, int *prot, int *access_index, - target_ulong address, int rw, int mmu_idx) + target_ulong address, int rw, int mmu_idx, + target_ulong *page_size) { - int is_user = mmu_idx == MMU_USER_IDX; + /* ??? We treat everything as a small page, then explicitly flush + everything when an entry is evicted. */ + *page_size = TARGET_PAGE_SIZE; + +#if defined (DEBUG_MMU) + /* safety net to catch wrong softmmu index use from dynamic code */ + if (env->tl > 0 && mmu_idx != MMU_NUCLEUS_IDX) { + DPRINTF_MMU("get_physical_address %s tl=%d mmu_idx=%d" + " primary context=%" PRIx64 + " secondary context=%" PRIx64 + " address=%" PRIx64 + "\n", + (rw == 2 ? "CODE" : "DATA"), + env->tl, mmu_idx, + env->dmmu.mmu_primary_context, + env->dmmu.mmu_secondary_context, + address); + } +#endif if (rw == 2) return get_physical_address_code(env, physical, prot, address, - is_user); + mmu_idx); else return get_physical_address_data(env, physical, prot, address, rw, - is_user); + mmu_idx); } /* Perform address translation */ @@ -547,39 +642,49 @@ int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw, { target_ulong virt_addr, vaddr; target_phys_addr_t paddr; - int error_code = 0, prot, ret = 0, access_index; + target_ulong page_size; + int error_code = 0, prot, access_index; error_code = get_physical_address(env, &paddr, &prot, &access_index, - address, rw, mmu_idx); + address, rw, mmu_idx, &page_size); if (error_code == 0) { virt_addr = address & TARGET_PAGE_MASK; vaddr = virt_addr + ((address & TARGET_PAGE_MASK) & (TARGET_PAGE_SIZE - 1)); -#ifdef DEBUG_MMU - printf("Translate at 0x%" PRIx64 " -> 0x%" PRIx64 ", vaddr 0x%" PRIx64 - "\n", address, paddr, vaddr); -#endif - ret = tlb_set_page_exec(env, vaddr, paddr, prot, mmu_idx, is_softmmu); - return ret; + + DPRINTF_MMU("Translate at %" PRIx64 " -> %" PRIx64 "," + " vaddr %" PRIx64 + " mmu_idx=%d" + " tl=%d" + " primary context=%" PRIx64 + " secondary context=%" PRIx64 + "\n", + address, paddr, vaddr, mmu_idx, env->tl, + env->dmmu.mmu_primary_context, + env->dmmu.mmu_secondary_context); + + tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size); + return 0; } // XXX return 1; } -#ifdef DEBUG_MMU -void dump_mmu(CPUState *env) +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUState *env) { unsigned int i; const char *mask; - printf("MMU contexts: Primary: %" PRId64 ", Secondary: %" PRId64 "\n", - env->dmmu.mmu_primary_context, env->dmmu.mmu_secondary_context); + (*cpu_fprintf)(f, "MMU contexts: Primary: %" PRId64 ", Secondary: %" + PRId64 "\n", + env->dmmu.mmu_primary_context, + env->dmmu.mmu_secondary_context); if ((env->lsu & DMMU_E) == 0) { - printf("DMMU disabled\n"); + (*cpu_fprintf)(f, "DMMU disabled\n"); } else { - printf("DMMU dump:\n"); + (*cpu_fprintf)(f, "DMMU dump\n"); for (i = 0; i < 64; i++) { - switch ((env->dtlb_tte[i] >> 61) & 3) { + switch (TTE_PGSIZE(env->dtlb[i].tte)) { default: case 0x0: mask = " 8k"; @@ -594,26 +699,29 @@ void dump_mmu(CPUState *env) mask = " 4M"; break; } - if ((env->dtlb_tte[i] & 0x8000000000000000ULL) != 0) { - printf("[%02u] VA: " PRIx64 ", PA: " PRIx64 - ", %s, %s, %s, %s, ctx %" PRId64 "\n", - i, - env->dtlb_tag[i] & (uint64_t)~0x1fffULL, - env->dtlb_tte[i] & (uint64_t)0x1ffffffe000ULL, - mask, - env->dtlb_tte[i] & 0x4? "priv": "user", - env->dtlb_tte[i] & 0x2? "RW": "RO", - env->dtlb_tte[i] & 0x40? "locked": "unlocked", - env->dtlb_tag[i] & (uint64_t)0x1fffULL); + if (TTE_IS_VALID(env->dtlb[i].tte)) { + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" + ", %s, %s, %s, %s, ctx %" PRId64 " %s\n", + i, + env->dtlb[i].tag & (uint64_t)~0x1fffULL, + TTE_PA(env->dtlb[i].tte), + mask, + TTE_IS_PRIV(env->dtlb[i].tte) ? "priv" : "user", + TTE_IS_W_OK(env->dtlb[i].tte) ? "RW" : "RO", + TTE_IS_LOCKED(env->dtlb[i].tte) ? + "locked" : "unlocked", + env->dtlb[i].tag & (uint64_t)0x1fffULL, + TTE_IS_GLOBAL(env->dtlb[i].tte)? + "global" : "local"); } } } if ((env->lsu & IMMU_E) == 0) { - printf("IMMU disabled\n"); + (*cpu_fprintf)(f, "IMMU disabled\n"); } else { - printf("IMMU dump:\n"); + (*cpu_fprintf)(f, "IMMU dump\n"); for (i = 0; i < 64; i++) { - switch ((env->itlb_tte[i] >> 61) & 3) { + switch (TTE_PGSIZE(env->itlb[i].tte)) { default: case 0x0: mask = " 8k"; @@ -628,49 +736,311 @@ void dump_mmu(CPUState *env) mask = " 4M"; break; } - if ((env->itlb_tte[i] & 0x8000000000000000ULL) != 0) { - printf("[%02u] VA: " PRIx64 ", PA: " PRIx64 - ", %s, %s, %s, ctx %" PRId64 "\n", - i, - env->itlb[i].tag & (uint64_t)~0x1fffULL, - env->itlb_tte[i] & (uint64_t)0x1ffffffe000ULL, - mask, - env->itlb_tte[i] & 0x4? "priv": "user", - env->itlb_tte[i] & 0x40? "locked": "unlocked", - env->itlb[i].tag & (uint64_t)0x1fffULL); + if (TTE_IS_VALID(env->itlb[i].tte)) { + (*cpu_fprintf)(f, "[%02u] VA: %" PRIx64 ", PA: %llx" + ", %s, %s, %s, ctx %" PRId64 " %s\n", + i, + env->itlb[i].tag & (uint64_t)~0x1fffULL, + TTE_PA(env->itlb[i].tte), + mask, + TTE_IS_PRIV(env->itlb[i].tte) ? "priv" : "user", + TTE_IS_LOCKED(env->itlb[i].tte) ? + "locked" : "unlocked", + env->itlb[i].tag & (uint64_t)0x1fffULL, + TTE_IS_GLOBAL(env->itlb[i].tte)? + "global" : "local"); } } } } -#endif /* DEBUG_MMU */ #endif /* TARGET_SPARC64 */ #endif /* !CONFIG_USER_ONLY */ -#if defined(CONFIG_USER_ONLY) -target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) +#if !defined(CONFIG_USER_ONLY) +static int cpu_sparc_get_phys_page(CPUState *env, target_phys_addr_t *phys, + target_ulong addr, int rw, int mmu_idx) { - return addr; + target_ulong page_size; + int prot, access_index; + + return get_physical_address(env, phys, &prot, &access_index, addr, rw, + mmu_idx, &page_size); } -#else +#if defined(TARGET_SPARC64) +target_phys_addr_t cpu_get_phys_page_nofault(CPUState *env, target_ulong addr, + int mmu_idx) +{ + target_phys_addr_t phys_addr; + + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 4, mmu_idx) != 0) { + return -1; + } + return phys_addr; +} +#endif + target_phys_addr_t cpu_get_phys_page_debug(CPUState *env, target_ulong addr) { target_phys_addr_t phys_addr; - int prot, access_index; + int mmu_idx = cpu_mmu_index(env); - if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2, - MMU_KERNEL_IDX) != 0) - if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, - 0, MMU_KERNEL_IDX) != 0) + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 2, mmu_idx) != 0) { + if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 0, mmu_idx) != 0) { return -1; - if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) + } + } + if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED) { return -1; + } return phys_addr; } #endif +#ifdef TARGET_SPARC64 +#ifdef DEBUG_PCALL +static const char * const excp_names[0x80] = { + [TT_TFAULT] = "Instruction Access Fault", + [TT_TMISS] = "Instruction Access MMU Miss", + [TT_CODE_ACCESS] = "Instruction Access Error", + [TT_ILL_INSN] = "Illegal Instruction", + [TT_PRIV_INSN] = "Privileged Instruction", + [TT_NFPU_INSN] = "FPU Disabled", + [TT_FP_EXCP] = "FPU Exception", + [TT_TOVF] = "Tag Overflow", + [TT_CLRWIN] = "Clean Windows", + [TT_DIV_ZERO] = "Division By Zero", + [TT_DFAULT] = "Data Access Fault", + [TT_DMISS] = "Data Access MMU Miss", + [TT_DATA_ACCESS] = "Data Access Error", + [TT_DPROT] = "Data Protection Error", + [TT_UNALIGNED] = "Unaligned Memory Access", + [TT_PRIV_ACT] = "Privileged Action", + [TT_EXTINT | 0x1] = "External Interrupt 1", + [TT_EXTINT | 0x2] = "External Interrupt 2", + [TT_EXTINT | 0x3] = "External Interrupt 3", + [TT_EXTINT | 0x4] = "External Interrupt 4", + [TT_EXTINT | 0x5] = "External Interrupt 5", + [TT_EXTINT | 0x6] = "External Interrupt 6", + [TT_EXTINT | 0x7] = "External Interrupt 7", + [TT_EXTINT | 0x8] = "External Interrupt 8", + [TT_EXTINT | 0x9] = "External Interrupt 9", + [TT_EXTINT | 0xa] = "External Interrupt 10", + [TT_EXTINT | 0xb] = "External Interrupt 11", + [TT_EXTINT | 0xc] = "External Interrupt 12", + [TT_EXTINT | 0xd] = "External Interrupt 13", + [TT_EXTINT | 0xe] = "External Interrupt 14", + [TT_EXTINT | 0xf] = "External Interrupt 15", +}; +#endif + +void do_interrupt(CPUState *env) +{ + int intno = env->exception_index; + trap_state *tsptr; + +#ifdef DEBUG_PCALL + if (qemu_loglevel_mask(CPU_LOG_INT)) { + static int count; + const char *name; + + if (intno < 0 || intno >= 0x180) { + name = "Unknown"; + } else if (intno >= 0x100) { + name = "Trap Instruction"; + } else if (intno >= 0xc0) { + name = "Window Fill"; + } else if (intno >= 0x80) { + name = "Window Spill"; + } else { + name = excp_names[intno]; + if (!name) { + name = "Unknown"; + } + } + + qemu_log("%6d: %s (v=%04x) pc=%016" PRIx64 " npc=%016" PRIx64 + " SP=%016" PRIx64 "\n", + count, name, intno, + env->pc, + env->npc, env->regwptr[6]); + log_cpu_state(env, 0); +#if 0 + { + int i; + uint8_t *ptr; + + qemu_log(" code="); + ptr = (uint8_t *)env->pc; + for (i = 0; i < 16; i++) { + qemu_log(" %02x", ldub(ptr + i)); + } + qemu_log("\n"); + } +#endif + count++; + } +#endif +#if !defined(CONFIG_USER_ONLY) + if (env->tl >= env->maxtl) { + cpu_abort(env, "Trap 0x%04x while trap level (%d) >= MAXTL (%d)," + " Error state", env->exception_index, env->tl, env->maxtl); + return; + } +#endif + if (env->tl < env->maxtl - 1) { + env->tl++; + } else { + env->pstate |= PS_RED; + if (env->tl < env->maxtl) { + env->tl++; + } + } + tsptr = cpu_tsptr(env); + + tsptr->tstate = (cpu_get_ccr(env) << 32) | + ((env->asi & 0xff) << 24) | ((env->pstate & 0xf3f) << 8) | + cpu_get_cwp64(env); + tsptr->tpc = env->pc; + tsptr->tnpc = env->npc; + tsptr->tt = intno; + + switch (intno) { + case TT_IVEC: + cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_IG); + break; + case TT_TFAULT: + case TT_DFAULT: + case TT_TMISS ... TT_TMISS + 3: + case TT_DMISS ... TT_DMISS + 3: + case TT_DPROT ... TT_DPROT + 3: + cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_MG); + break; + default: + cpu_change_pstate(env, PS_PEF | PS_PRIV | PS_AG); + break; + } + + if (intno == TT_CLRWIN) { + cpu_set_cwp(env, cpu_cwp_dec(env, env->cwp - 1)); + } else if ((intno & 0x1c0) == TT_SPILL) { + cpu_set_cwp(env, cpu_cwp_dec(env, env->cwp - env->cansave - 2)); + } else if ((intno & 0x1c0) == TT_FILL) { + cpu_set_cwp(env, cpu_cwp_inc(env, env->cwp + 1)); + } + env->tbr &= ~0x7fffULL; + env->tbr |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5); + env->pc = env->tbr; + env->npc = env->pc + 4; + env->exception_index = -1; +} +#else +#ifdef DEBUG_PCALL +static const char * const excp_names[0x80] = { + [TT_TFAULT] = "Instruction Access Fault", + [TT_ILL_INSN] = "Illegal Instruction", + [TT_PRIV_INSN] = "Privileged Instruction", + [TT_NFPU_INSN] = "FPU Disabled", + [TT_WIN_OVF] = "Window Overflow", + [TT_WIN_UNF] = "Window Underflow", + [TT_UNALIGNED] = "Unaligned Memory Access", + [TT_FP_EXCP] = "FPU Exception", + [TT_DFAULT] = "Data Access Fault", + [TT_TOVF] = "Tag Overflow", + [TT_EXTINT | 0x1] = "External Interrupt 1", + [TT_EXTINT | 0x2] = "External Interrupt 2", + [TT_EXTINT | 0x3] = "External Interrupt 3", + [TT_EXTINT | 0x4] = "External Interrupt 4", + [TT_EXTINT | 0x5] = "External Interrupt 5", + [TT_EXTINT | 0x6] = "External Interrupt 6", + [TT_EXTINT | 0x7] = "External Interrupt 7", + [TT_EXTINT | 0x8] = "External Interrupt 8", + [TT_EXTINT | 0x9] = "External Interrupt 9", + [TT_EXTINT | 0xa] = "External Interrupt 10", + [TT_EXTINT | 0xb] = "External Interrupt 11", + [TT_EXTINT | 0xc] = "External Interrupt 12", + [TT_EXTINT | 0xd] = "External Interrupt 13", + [TT_EXTINT | 0xe] = "External Interrupt 14", + [TT_EXTINT | 0xf] = "External Interrupt 15", + [TT_TOVF] = "Tag Overflow", + [TT_CODE_ACCESS] = "Instruction Access Error", + [TT_DATA_ACCESS] = "Data Access Error", + [TT_DIV_ZERO] = "Division By Zero", + [TT_NCP_INSN] = "Coprocessor Disabled", +}; +#endif + +void do_interrupt(CPUState *env) +{ + int cwp, intno = env->exception_index; + +#ifdef DEBUG_PCALL + if (qemu_loglevel_mask(CPU_LOG_INT)) { + static int count; + const char *name; + + if (intno < 0 || intno >= 0x100) { + name = "Unknown"; + } else if (intno >= 0x80) { + name = "Trap Instruction"; + } else { + name = excp_names[intno]; + if (!name) { + name = "Unknown"; + } + } + + qemu_log("%6d: %s (v=%02x) pc=%08x npc=%08x SP=%08x\n", + count, name, intno, + env->pc, + env->npc, env->regwptr[6]); + log_cpu_state(env, 0); +#if 0 + { + int i; + uint8_t *ptr; + + qemu_log(" code="); + ptr = (uint8_t *)env->pc; + for (i = 0; i < 16; i++) { + qemu_log(" %02x", ldub(ptr + i)); + } + qemu_log("\n"); + } +#endif + count++; + } +#endif +#if !defined(CONFIG_USER_ONLY) + if (env->psret == 0) { + cpu_abort(env, "Trap 0x%02x while interrupts disabled, Error state", + env->exception_index); + return; + } +#endif + env->psret = 0; + cwp = cpu_cwp_dec(env, env->cwp - 1); + cpu_set_cwp(env, cwp); + env->regwptr[9] = env->pc; + env->regwptr[10] = env->npc; + env->psrps = env->psrs; + env->psrs = 1; + env->tbr = (env->tbr & TBR_BASE_MASK) | (intno << 4); + env->pc = env->tbr; + env->npc = env->pc + 4; + env->exception_index = -1; + +#if !defined(CONFIG_USER_ONLY) + /* IRQ acknowledgment */ + if ((intno & ~15) == TT_EXTINT && env->qemu_irq_ack != NULL) { + env->qemu_irq_ack(env->irq_manager, intno); + } +#endif +} +#endif + void cpu_reset(CPUSPARCState *env) { if (qemu_loglevel_mask(CPU_LOG_RESET)) { @@ -695,12 +1065,12 @@ void cpu_reset(CPUSPARCState *env) #else #if !defined(TARGET_SPARC64) env->psret = 0; -#endif env->psrs = 1; env->psrps = 1; +#endif #ifdef TARGET_SPARC64 env->pstate = PS_PRIV|PS_RED|PS_PEF|PS_AG; - env->hpstate = HS_PRIV; + env->hpstate = cpu_has_hypervisor(env) ? HS_PRIV : 0; env->tl = env->maxtl; cpu_tsptr(env)->tt = TT_POWER_ON_RESET; env->lsu = 0; @@ -711,6 +1081,7 @@ void cpu_reset(CPUSPARCState *env) env->pc = 0; env->npc = env->pc + 4; #endif + env->cache_control = 0; } static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model) @@ -762,7 +1133,6 @@ CPUSPARCState *cpu_sparc_init(const char *cpu_model) cpu_sparc_close(env); return NULL; } - cpu_reset(env); qemu_init_vcpu(env); return env; @@ -1216,20 +1586,21 @@ static const sparc_def_t sparc_defs[] = { .mmu_sfsr_mask = 0xffffffff, .mmu_trcr_mask = 0xffffffff, .nwindows = 8, - .features = CPU_DEFAULT_FEATURES, + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN, }, { .name = "LEON3", .iu_version = 0xf3000000, .fpu_version = 4 << 17, /* FPU version 4 (Meiko) */ .mmu_version = 0xf3000000, - .mmu_bm = 0x00004000, + .mmu_bm = 0x00000000, .mmu_ctpr_mask = 0x007ffff0, .mmu_cxr_mask = 0x0000003f, .mmu_sfsr_mask = 0xffffffff, .mmu_trcr_mask = 0xffffffff, .nwindows = 8, - .features = CPU_DEFAULT_FEATURES, + .features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN | + CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL, }, #endif }; @@ -1251,8 +1622,7 @@ static const char * const feature_name[] = { "gl", }; -static void print_features(FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +static void print_features(FILE *f, fprintf_function cpu_fprintf, uint32_t features, const char *prefix) { unsigned int i; @@ -1285,7 +1655,7 @@ static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model) char *featurestr, *name = strtok(s, ","); uint32_t plus_features = 0; uint32_t minus_features = 0; - long long iu_version; + uint64_t iu_version; uint32_t fpu_version, mmu_version, nwindows; for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) { @@ -1317,7 +1687,7 @@ static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model) } cpu_def->iu_version = iu_version; #ifdef DEBUG_FEATURES - fprintf(stderr, "iu_version %llx\n", iu_version); + fprintf(stderr, "iu_version %" PRIx64 "\n", iu_version); #endif } else if (!strcmp(featurestr, "fpu_version")) { char *err; @@ -1380,7 +1750,7 @@ static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model) return -1; } -void sparc_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) +void sparc_cpu_list(FILE *f, fprintf_function cpu_fprintf) { unsigned int i; @@ -1407,8 +1777,21 @@ void sparc_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...)) "fpu_version mmu_version nwindows\n"); } -void cpu_dump_state(CPUState *env, FILE *f, - int (*cpu_fprintf)(FILE *f, const char *fmt, ...), +static void cpu_print_cc(FILE *f, fprintf_function cpu_fprintf, + uint32_t cc) +{ + cpu_fprintf(f, "%c%c%c%c", cc & PSR_NEG? 'N' : '-', + cc & PSR_ZERO? 'Z' : '-', cc & PSR_OVF? 'V' : '-', + cc & PSR_CARRY? 'C' : '-'); +} + +#ifdef TARGET_SPARC64 +#define REGS_PER_LINE 4 +#else +#define REGS_PER_LINE 8 +#endif + +void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, int flags) { int i, x; @@ -1416,26 +1799,32 @@ void cpu_dump_state(CPUState *env, FILE *f, cpu_fprintf(f, "pc: " TARGET_FMT_lx " npc: " TARGET_FMT_lx "\n", env->pc, env->npc); cpu_fprintf(f, "General Registers:\n"); - for (i = 0; i < 4; i++) - cpu_fprintf(f, "%%g%c: " TARGET_FMT_lx "\t", i + '0', env->gregs[i]); - cpu_fprintf(f, "\n"); - for (; i < 8; i++) - cpu_fprintf(f, "%%g%c: " TARGET_FMT_lx "\t", i + '0', env->gregs[i]); + + for (i = 0; i < 8; i++) { + if (i % REGS_PER_LINE == 0) { + cpu_fprintf(f, "%%g%d-%d:", i, i + REGS_PER_LINE - 1); + } + cpu_fprintf(f, " " TARGET_FMT_lx, env->gregs[i]); + if (i % REGS_PER_LINE == REGS_PER_LINE - 1) { + cpu_fprintf(f, "\n"); + } + } cpu_fprintf(f, "\nCurrent Register Window:\n"); for (x = 0; x < 3; x++) { - for (i = 0; i < 4; i++) - cpu_fprintf(f, "%%%c%d: " TARGET_FMT_lx "\t", - (x == 0 ? 'o' : (x == 1 ? 'l' : 'i')), i, - env->regwptr[i + x * 8]); - cpu_fprintf(f, "\n"); - for (; i < 8; i++) - cpu_fprintf(f, "%%%c%d: " TARGET_FMT_lx "\t", - (x == 0 ? 'o' : x == 1 ? 'l' : 'i'), i, - env->regwptr[i + x * 8]); - cpu_fprintf(f, "\n"); + for (i = 0; i < 8; i++) { + if (i % REGS_PER_LINE == 0) { + cpu_fprintf(f, "%%%c%d-%d: ", + x == 0 ? 'o' : (x == 1 ? 'l' : 'i'), + i, i + REGS_PER_LINE - 1); + } + cpu_fprintf(f, TARGET_FMT_lx " ", env->regwptr[i + x * 8]); + if (i % REGS_PER_LINE == REGS_PER_LINE - 1) { + cpu_fprintf(f, "\n"); + } + } } cpu_fprintf(f, "\nFloating Point Registers:\n"); - for (i = 0; i < 32; i++) { + for (i = 0; i < TARGET_FPREGS; i++) { if ((i & 3) == 0) cpu_fprintf(f, "%%f%02d:", i); cpu_fprintf(f, " %016f", *(float *)&env->fpr[i]); @@ -1443,21 +1832,26 @@ void cpu_dump_state(CPUState *env, FILE *f, cpu_fprintf(f, "\n"); } #ifdef TARGET_SPARC64 - cpu_fprintf(f, "pstate: 0x%08x ccr: 0x%02x asi: 0x%02x tl: %d fprs: %d\n", - env->pstate, GET_CCR(env), env->asi, env->tl, env->fprs); - cpu_fprintf(f, "cansave: %d canrestore: %d otherwin: %d wstate %d " - "cleanwin %d cwp %d\n", + cpu_fprintf(f, "pstate: %08x ccr: %02x (icc: ", env->pstate, + (unsigned)cpu_get_ccr(env)); + cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << PSR_CARRY_SHIFT); + cpu_fprintf(f, " xcc: "); + cpu_print_cc(f, cpu_fprintf, cpu_get_ccr(env) << (PSR_CARRY_SHIFT - 4)); + cpu_fprintf(f, ") asi: %02x tl: %d pil: %x\n", env->asi, env->tl, + env->psrpil); + cpu_fprintf(f, "cansave: %d canrestore: %d otherwin: %d wstate: %d " + "cleanwin: %d cwp: %d\n", env->cansave, env->canrestore, env->otherwin, env->wstate, env->cleanwin, env->nwindows - 1 - env->cwp); + cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx " fprs: " + TARGET_FMT_lx "\n", env->fsr, env->y, env->fprs); #else - -#define GET_FLAG(a,b) ((env->psr & a)?b:'-') - - cpu_fprintf(f, "psr: 0x%08x -> %c%c%c%c %c%c%c wim: 0x%08x\n", - GET_PSR(env), GET_FLAG(PSR_ZERO, 'Z'), GET_FLAG(PSR_OVF, 'V'), - GET_FLAG(PSR_NEG, 'N'), GET_FLAG(PSR_CARRY, 'C'), - env->psrs?'S':'-', env->psrps?'P':'-', - env->psret?'E':'-', env->wim); + cpu_fprintf(f, "psr: %08x (icc: ", cpu_get_psr(env)); + cpu_print_cc(f, cpu_fprintf, cpu_get_psr(env)); + cpu_fprintf(f, " SPE: %c%c%c) wim: %08x\n", env->psrs? 'S' : '-', + env->psrps? 'P' : '-', env->psret? 'E' : '-', + env->wim); + cpu_fprintf(f, "fsr: " TARGET_FMT_lx " y: " TARGET_FMT_lx "\n", + env->fsr, env->y); #endif - cpu_fprintf(f, "fsr: 0x%08x\n", env->fsr); }