qemu/target-sparc/helper.c
Paul Brook d4c430a80f Large page TLB flush
QEMU uses a fixed page size for the CPU TLB.  If the guest uses large
pages then we effectively split these into multiple smaller pages, and
populate the corresponding TLB entries on demand.

When the guest invalidates the TLB by virtual address we must invalidate
all entries covered by the large page.  However the address used to
invalidate the entry may not be present in the QEMU TLB, so we do not
know which regions to clear.

Implementing a full vaiable size TLB is hard and slow, so just keep a
simple address/mask pair to record which addresses may have been mapped by
large pages.  If the guest invalidates this region then flush the
whole TLB.

Signed-off-by: Paul Brook <paul@codesourcery.com>
2010-03-17 02:44:41 +00:00

1499 lines
47 KiB
C

/*
* sparc helpers
*
* Copyright (c) 2003-2005 Fabrice Bellard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <signal.h>
#include "cpu.h"
#include "exec-all.h"
#include "qemu-common.h"
//#define DEBUG_MMU
//#define DEBUG_FEATURES
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,
int mmu_idx, int is_softmmu)
{
if (rw & 2)
env1->exception_index = TT_TFAULT;
else
env1->exception_index = TT_DFAULT;
return 1;
}
#else
#ifndef TARGET_SPARC64
/*
* Sparc V8 Reference MMU (SRMMU)
*/
static const int access_table[8][8] = {
{ 0, 0, 0, 0, 8, 0, 12, 12 },
{ 0, 0, 0, 0, 8, 0, 0, 0 },
{ 8, 8, 0, 0, 0, 8, 12, 12 },
{ 8, 8, 0, 0, 0, 8, 0, 0 },
{ 8, 0, 8, 0, 8, 8, 12, 12 },
{ 8, 0, 8, 0, 8, 0, 8, 0 },
{ 8, 8, 8, 0, 8, 8, 12, 12 },
{ 8, 8, 8, 0, 8, 8, 8, 0 }
};
static const int perm_table[2][8] = {
{
PAGE_READ,
PAGE_READ | PAGE_WRITE,
PAGE_READ | PAGE_EXEC,
PAGE_READ | PAGE_WRITE | PAGE_EXEC,
PAGE_EXEC,
PAGE_READ | PAGE_WRITE,
PAGE_READ | PAGE_EXEC,
PAGE_READ | PAGE_WRITE | PAGE_EXEC
},
{
PAGE_READ,
PAGE_READ | PAGE_WRITE,
PAGE_READ | PAGE_EXEC,
PAGE_READ | PAGE_WRITE | PAGE_EXEC,
PAGE_EXEC,
PAGE_READ,
0,
0,
}
};
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 *page_size)
{
int access_perms = 0;
target_phys_addr_t pde_ptr;
uint32_t pde;
int error_code = 0, is_dirty, is_user;
unsigned long page_offset;
is_user = mmu_idx == MMU_USER_IDX;
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);
*prot = PAGE_READ | PAGE_EXEC;
return 0;
}
*physical = address;
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
return 0;
}
*access_index = ((rw & 1) << 2) | (rw & 2) | (is_user? 0 : 1);
*physical = 0xffffffffffff0000ULL;
/* SPARC reference MMU table walk: Context table->L1->L2->PTE */
/* Context base + context number */
pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
pde = ldl_phys(pde_ptr);
/* Ctx pde */
switch (pde & PTE_ENTRYTYPE_MASK) {
default:
case 0: /* Invalid */
return 1 << 2;
case 2: /* L0 PTE, maybe should not happen? */
case 3: /* Reserved */
return 4 << 2;
case 1: /* L0 PDE */
pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
pde = ldl_phys(pde_ptr);
switch (pde & PTE_ENTRYTYPE_MASK) {
default:
case 0: /* Invalid */
return (1 << 8) | (1 << 2);
case 3: /* Reserved */
return (1 << 8) | (4 << 2);
case 1: /* L1 PDE */
pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
pde = ldl_phys(pde_ptr);
switch (pde & PTE_ENTRYTYPE_MASK) {
default:
case 0: /* Invalid */
return (2 << 8) | (1 << 2);
case 3: /* Reserved */
return (2 << 8) | (4 << 2);
case 1: /* L2 PDE */
pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
pde = ldl_phys(pde_ptr);
switch (pde & PTE_ENTRYTYPE_MASK) {
default:
case 0: /* Invalid */
return (3 << 8) | (1 << 2);
case 1: /* PDE, should not happen */
case 3: /* Reserved */
return (3 << 8) | (4 << 2);
case 2: /* L3 PTE */
page_offset = (address & TARGET_PAGE_MASK) &
(TARGET_PAGE_SIZE - 1);
}
*page_size = TARGET_PAGE_SIZE;
break;
case 2: /* L2 PTE */
page_offset = address & 0x3ffff;
*page_size = 0x40000;
}
break;
case 2: /* L1 PTE */
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) {
pde |= PG_ACCESSED_MASK;
if (is_dirty)
pde |= PG_MODIFIED_MASK;
stl_phys_notdirty(pde_ptr, pde);
}
/* the page can be put in the TLB */
*prot = perm_table[is_user][access_perms];
if (!(pde & PG_MODIFIED_MASK)) {
/* only set write access if already dirty... otherwise wait
for dirty access */
*prot &= ~PAGE_WRITE;
}
/* Even if large ptes, we map only one 4KB page in the cache to
avoid filling it too fast */
*physical = ((target_phys_addr_t)(pde & PTE_ADDR_MASK) << 4) + page_offset;
return error_code;
}
/* Perform address translation */
int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
int mmu_idx, int is_softmmu)
{
target_phys_addr_t paddr;
target_ulong vaddr;
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, &page_size);
if (error_code == 0) {
vaddr = address & TARGET_PAGE_MASK;
paddr &= TARGET_PAGE_MASK;
#ifdef DEBUG_MMU
printf("Translate at " TARGET_FMT_lx " -> " TARGET_FMT_plx ", vaddr "
TARGET_FMT_lx "\n", address, paddr, vaddr);
#endif
tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size);
return 0;
}
if (env->mmuregs[3]) /* Fault status register */
env->mmuregs[3] = 1; /* overflow (not read before another fault) */
env->mmuregs[3] |= (access_index << 5) | error_code | 2;
env->mmuregs[4] = address; /* Fault address register */
if ((env->mmuregs[0] & MMU_NF) || env->psret == 0) {
// No fault mode: if a mapping is available, just override
// permissions. If no mapping is available, redirect accesses to
// neverland. Fake/overridden mappings will be flushed when
// switching to normal mode.
vaddr = address & TARGET_PAGE_MASK;
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
tlb_set_page(env, vaddr, paddr, prot, mmu_idx, TARGET_PAGE_SIZE);
return 0;
} else {
if (rw & 2)
env->exception_index = TT_TFAULT;
else
env->exception_index = TT_DFAULT;
return 1;
}
}
target_ulong mmu_probe(CPUState *env, target_ulong address, int mmulev)
{
target_phys_addr_t pde_ptr;
uint32_t pde;
/* Context base + context number */
pde_ptr = (target_phys_addr_t)(env->mmuregs[1] << 4) +
(env->mmuregs[2] << 2);
pde = ldl_phys(pde_ptr);
switch (pde & PTE_ENTRYTYPE_MASK) {
default:
case 0: /* Invalid */
case 2: /* PTE, maybe should not happen? */
case 3: /* Reserved */
return 0;
case 1: /* L1 PDE */
if (mmulev == 3)
return pde;
pde_ptr = ((address >> 22) & ~3) + ((pde & ~3) << 4);
pde = ldl_phys(pde_ptr);
switch (pde & PTE_ENTRYTYPE_MASK) {
default:
case 0: /* Invalid */
case 3: /* Reserved */
return 0;
case 2: /* L1 PTE */
return pde;
case 1: /* L2 PDE */
if (mmulev == 2)
return pde;
pde_ptr = ((address & 0xfc0000) >> 16) + ((pde & ~3) << 4);
pde = ldl_phys(pde_ptr);
switch (pde & PTE_ENTRYTYPE_MASK) {
default:
case 0: /* Invalid */
case 3: /* Reserved */
return 0;
case 2: /* L2 PTE */
return pde;
case 1: /* L3 PDE */
if (mmulev == 1)
return pde;
pde_ptr = ((address & 0x3f000) >> 10) + ((pde & ~3) << 4);
pde = ldl_phys(pde_ptr);
switch (pde & PTE_ENTRYTYPE_MASK) {
default:
case 0: /* Invalid */
case 1: /* PDE, should not happen */
case 3: /* Reserved */
return 0;
case 2: /* L3 PTE */
return pde;
}
}
}
}
return 0;
}
#ifdef DEBUG_MMU
void dump_mmu(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]);
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);
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);
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);
}
}
}
}
}
}
printf("MMU dump ends\n");
}
#endif /* DEBUG_MMU */
#else /* !TARGET_SPARC64 */
// 41 bit physical address space
static inline target_phys_addr_t ultrasparc_truncate_physical(uint64_t x)
{
return x & 0x1ffffffffffULL;
}
/*
* 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,
uint64_t address, uint64_t context,
target_phys_addr_t *physical,
int is_nucleus)
{
uint64_t mask;
switch ((tlb->tte >> 61) & 3) {
default:
case 0x0: // 8k
mask = 0xffffffffffffe000ULL;
break;
case 0x1: // 64k
mask = 0xffffffffffff0000ULL;
break;
case 0x2: // 512k
mask = 0xfffffffffff80000ULL;
break;
case 0x3: // 4M
mask = 0xffffffffffc00000ULL;
break;
}
// valid, context match, virtual address match?
if (TTE_IS_VALID(tlb->tte) &&
((is_nucleus && compare_masked(0, tlb->tag, 0x1fff))
|| TTE_IS_GLOBAL(tlb->tte) || compare_masked(context, tlb->tag, 0x1fff))
&& compare_masked(address, tlb->tag, mask))
{
// decode physical address
*physical = ((tlb->tte & mask) | (address & ~mask)) & 0x1ffffffe000ULL;
return 1;
}
return 0;
}
static int get_physical_address_data(CPUState *env,
target_phys_addr_t *physical, int *prot,
target_ulong address, int rw, int is_user)
{
unsigned int i;
uint64_t context;
int is_nucleus;
if ((env->lsu & DMMU_E) == 0) { /* DMMU disabled */
*physical = ultrasparc_truncate_physical(address);
*prot = PAGE_READ | PAGE_WRITE;
return 0;
}
context = env->dmmu.mmu_primary_context & 0x1fff;
is_nucleus = env->tl > 0;
for (i = 0; i < 64; i++) {
// ctx match, vaddr match, valid?
if (ultrasparc_tag_match(&env->dtlb[i],
address, context, physical,
is_nucleus)) {
// access ok?
if (((env->dtlb[i].tte & 0x4) && is_user) ||
(!(env->dtlb[i].tte & 0x2) && (rw == 1))) {
uint8_t fault_type = 0;
if ((env->dtlb[i].tte & 0x4) && is_user) {
fault_type |= 1; /* privilege violation */
}
if (env->dmmu.sfsr & 1) /* Fault status register */
env->dmmu.sfsr = 2; /* overflow (not read before
another fault) */
env->dmmu.sfsr |= (is_user << 3) | ((rw == 1) << 2) | 1;
env->dmmu.sfsr |= (fault_type << 7);
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;
}
*prot = PAGE_READ;
if (env->dtlb[i].tte & 0x2)
*prot |= PAGE_WRITE;
TTE_SET_USED(env->dtlb[i].tte);
return 0;
}
}
#ifdef DEBUG_MMU
printf("DMISS at 0x%" PRIx64 "\n", address);
#endif
env->dmmu.tag_access = (address & ~0x1fffULL) | context;
env->exception_index = TT_DMISS;
return 1;
}
static int get_physical_address_code(CPUState *env,
target_phys_addr_t *physical, int *prot,
target_ulong address, int is_user)
{
unsigned int i;
uint64_t context;
int is_nucleus;
if ((env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0) {
/* IMMU disabled */
*physical = ultrasparc_truncate_physical(address);
*prot = PAGE_EXEC;
return 0;
}
context = env->dmmu.mmu_primary_context & 0x1fff;
is_nucleus = env->tl > 0;
for (i = 0; i < 64; i++) {
// ctx match, vaddr match, valid?
if (ultrasparc_tag_match(&env->itlb[i],
address, context, physical,
is_nucleus)) {
// 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;
env->exception_index = TT_TFAULT;
#ifdef DEBUG_MMU
printf("TFAULT at 0x%" PRIx64 "\n", address);
#endif
return 1;
}
*prot = PAGE_EXEC;
TTE_SET_USED(env->itlb[i].tte);
return 0;
}
}
#ifdef DEBUG_MMU
printf("TMISS at 0x%" PRIx64 "\n", address);
#endif
/* Context is stored in DMMU (dmmuregs[1]) also for IMMU */
env->immu.tag_access = (address & ~0x1fffULL) | context;
env->exception_index = TT_TMISS;
return 1;
}
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 *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 (rw == 2)
return get_physical_address_code(env, physical, prot, address,
is_user);
else
return get_physical_address_data(env, physical, prot, address, rw,
is_user);
}
/* Perform address translation */
int cpu_sparc_handle_mmu_fault (CPUState *env, target_ulong address, int rw,
int mmu_idx, int is_softmmu)
{
target_ulong virt_addr, vaddr;
target_phys_addr_t paddr;
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, &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
tlb_set_page(env, vaddr, paddr, prot, mmu_idx, page_size);
return 0;
}
// XXX
return 1;
}
#ifdef DEBUG_MMU
void dump_mmu(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);
if ((env->lsu & DMMU_E) == 0) {
printf("DMMU disabled\n");
} else {
printf("DMMU dump:\n");
for (i = 0; i < 64; i++) {
switch ((env->dtlb[i].tte >> 61) & 3) {
default:
case 0x0:
mask = " 8k";
break;
case 0x1:
mask = " 64k";
break;
case 0x2:
mask = "512k";
break;
case 0x3:
mask = " 4M";
break;
}
if ((env->dtlb[i].tte & 0x8000000000000000ULL) != 0) {
printf("[%02u] VA: %" PRIx64 ", PA: %" PRIx64
", %s, %s, %s, %s, ctx %" PRId64 " %s\n",
i,
env->dtlb[i].tag & (uint64_t)~0x1fffULL,
env->dtlb[i].tte & (uint64_t)0x1ffffffe000ULL,
mask,
env->dtlb[i].tte & 0x4? "priv": "user",
env->dtlb[i].tte & 0x2? "RW": "RO",
env->dtlb[i].tte & 0x40? "locked": "unlocked",
env->dtlb[i].tag & (uint64_t)0x1fffULL,
TTE_IS_GLOBAL(env->dtlb[i].tag)? "global" : "local");
}
}
}
if ((env->lsu & IMMU_E) == 0) {
printf("IMMU disabled\n");
} else {
printf("IMMU dump:\n");
for (i = 0; i < 64; i++) {
switch ((env->itlb[i].tte >> 61) & 3) {
default:
case 0x0:
mask = " 8k";
break;
case 0x1:
mask = " 64k";
break;
case 0x2:
mask = "512k";
break;
case 0x3:
mask = " 4M";
break;
}
if ((env->itlb[i].tte & 0x8000000000000000ULL) != 0) {
printf("[%02u] VA: %" PRIx64 ", PA: %" PRIx64
", %s, %s, %s, ctx %" PRId64 " %s\n",
i,
env->itlb[i].tag & (uint64_t)~0x1fffULL,
env->itlb[i].tte & (uint64_t)0x1ffffffe000ULL,
mask,
env->itlb[i].tte & 0x4? "priv": "user",
env->itlb[i].tte & 0x40? "locked": "unlocked",
env->itlb[i].tag & (uint64_t)0x1fffULL,
TTE_IS_GLOBAL(env->itlb[i].tag)? "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)
{
target_phys_addr_t phys_addr;
target_ulong page_size;
int prot, access_index;
if (get_physical_address(env, &phys_addr, &prot, &access_index, addr, 2,
MMU_KERNEL_IDX, &page_size) != 0)
if (get_physical_address(env, &phys_addr, &prot, &access_index, addr,
0, MMU_KERNEL_IDX, &page_size) != 0)
return -1;
if (cpu_get_physical_page_desc(phys_addr) == IO_MEM_UNASSIGNED)
return -1;
return phys_addr;
}
#endif
void cpu_reset(CPUSPARCState *env)
{
if (qemu_loglevel_mask(CPU_LOG_RESET)) {
qemu_log("CPU Reset (CPU %d)\n", env->cpu_index);
log_cpu_state(env, 0);
}
tlb_flush(env, 1);
env->cwp = 0;
#ifndef TARGET_SPARC64
env->wim = 1;
#endif
env->regwptr = env->regbase + (env->cwp * 16);
CC_OP = CC_OP_FLAGS;
#if defined(CONFIG_USER_ONLY)
#ifdef TARGET_SPARC64
env->cleanwin = env->nwindows - 2;
env->cansave = env->nwindows - 2;
env->pstate = PS_RMO | PS_PEF | PS_IE;
env->asi = 0x82; // Primary no-fault
#endif
#else
#if !defined(TARGET_SPARC64)
env->psret = 0;
#endif
env->psrs = 1;
env->psrps = 1;
#ifdef TARGET_SPARC64
env->pstate = PS_PRIV|PS_RED|PS_PEF|PS_AG;
env->hpstate = HS_PRIV;
env->tl = env->maxtl;
cpu_tsptr(env)->tt = TT_POWER_ON_RESET;
env->lsu = 0;
#else
env->mmuregs[0] &= ~(MMU_E | MMU_NF);
env->mmuregs[0] |= env->def->mmu_bm;
#endif
env->pc = 0;
env->npc = env->pc + 4;
#endif
}
static int cpu_sparc_register(CPUSPARCState *env, const char *cpu_model)
{
sparc_def_t def1, *def = &def1;
if (cpu_sparc_find_by_name(def, cpu_model) < 0)
return -1;
env->def = qemu_mallocz(sizeof(*def));
memcpy(env->def, def, sizeof(*def));
#if defined(CONFIG_USER_ONLY)
if ((env->def->features & CPU_FEATURE_FLOAT))
env->def->features |= CPU_FEATURE_FLOAT128;
#endif
env->cpu_model_str = cpu_model;
env->version = def->iu_version;
env->fsr = def->fpu_version;
env->nwindows = def->nwindows;
#if !defined(TARGET_SPARC64)
env->mmuregs[0] |= def->mmu_version;
cpu_sparc_set_id(env, 0);
env->mxccregs[7] |= def->mxcc_version;
#else
env->mmu_version = def->mmu_version;
env->maxtl = def->maxtl;
env->version |= def->maxtl << 8;
env->version |= def->nwindows - 1;
#endif
return 0;
}
static void cpu_sparc_close(CPUSPARCState *env)
{
free(env->def);
free(env);
}
CPUSPARCState *cpu_sparc_init(const char *cpu_model)
{
CPUSPARCState *env;
env = qemu_mallocz(sizeof(CPUSPARCState));
cpu_exec_init(env);
gen_intermediate_code_init(env);
if (cpu_sparc_register(env, cpu_model) < 0) {
cpu_sparc_close(env);
return NULL;
}
qemu_init_vcpu(env);
return env;
}
void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu)
{
#if !defined(TARGET_SPARC64)
env->mxccregs[7] = ((cpu + 8) & 0xf) << 24;
#endif
}
static const sparc_def_t sparc_defs[] = {
#ifdef TARGET_SPARC64
{
.name = "Fujitsu Sparc64",
.iu_version = ((0x04ULL << 48) | (0x02ULL << 32) | (0ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 4,
.maxtl = 4,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Fujitsu Sparc64 III",
.iu_version = ((0x04ULL << 48) | (0x03ULL << 32) | (0ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 5,
.maxtl = 4,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Fujitsu Sparc64 IV",
.iu_version = ((0x04ULL << 48) | (0x04ULL << 32) | (0ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Fujitsu Sparc64 V",
.iu_version = ((0x04ULL << 48) | (0x05ULL << 32) | (0x51ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI UltraSparc I",
.iu_version = ((0x17ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI UltraSparc II",
.iu_version = ((0x17ULL << 48) | (0x11ULL << 32) | (0x20ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI UltraSparc IIi",
.iu_version = ((0x17ULL << 48) | (0x12ULL << 32) | (0x91ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI UltraSparc IIe",
.iu_version = ((0x17ULL << 48) | (0x13ULL << 32) | (0x14ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Sun UltraSparc III",
.iu_version = ((0x3eULL << 48) | (0x14ULL << 32) | (0x34ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Sun UltraSparc III Cu",
.iu_version = ((0x3eULL << 48) | (0x15ULL << 32) | (0x41ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_3,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Sun UltraSparc IIIi",
.iu_version = ((0x3eULL << 48) | (0x16ULL << 32) | (0x34ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Sun UltraSparc IV",
.iu_version = ((0x3eULL << 48) | (0x18ULL << 32) | (0x31ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_4,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Sun UltraSparc IV+",
.iu_version = ((0x3eULL << 48) | (0x19ULL << 32) | (0x22ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES | CPU_FEATURE_CMT,
},
{
.name = "Sun UltraSparc IIIi+",
.iu_version = ((0x3eULL << 48) | (0x22ULL << 32) | (0ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_3,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Sun UltraSparc T1",
// defined in sparc_ifu_fdp.v and ctu.h
.iu_version = ((0x3eULL << 48) | (0x23ULL << 32) | (0x02ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_sun4v,
.nwindows = 8,
.maxtl = 6,
.features = CPU_DEFAULT_FEATURES | CPU_FEATURE_HYPV | CPU_FEATURE_CMT
| CPU_FEATURE_GL,
},
{
.name = "Sun UltraSparc T2",
// defined in tlu_asi_ctl.v and n2_revid_cust.v
.iu_version = ((0x3eULL << 48) | (0x24ULL << 32) | (0x02ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_sun4v,
.nwindows = 8,
.maxtl = 6,
.features = CPU_DEFAULT_FEATURES | CPU_FEATURE_HYPV | CPU_FEATURE_CMT
| CPU_FEATURE_GL,
},
{
.name = "NEC UltraSparc I",
.iu_version = ((0x22ULL << 48) | (0x10ULL << 32) | (0x40ULL << 24)),
.fpu_version = 0x00000000,
.mmu_version = mmu_us_12,
.nwindows = 8,
.maxtl = 5,
.features = CPU_DEFAULT_FEATURES,
},
#else
{
.name = "Fujitsu MB86900",
.iu_version = 0x00 << 24, /* Impl 0, ver 0 */
.fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
.mmu_version = 0x00 << 24, /* Impl 0, ver 0 */
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 7,
.features = CPU_FEATURE_FLOAT | CPU_FEATURE_FSMULD,
},
{
.name = "Fujitsu MB86904",
.iu_version = 0x04 << 24, /* Impl 0, ver 4 */
.fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
.mmu_version = 0x04 << 24, /* Impl 0, ver 4 */
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x00ffffc0,
.mmu_cxr_mask = 0x000000ff,
.mmu_sfsr_mask = 0x00016fff,
.mmu_trcr_mask = 0x00ffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Fujitsu MB86907",
.iu_version = 0x05 << 24, /* Impl 0, ver 5 */
.fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
.mmu_version = 0x05 << 24, /* Impl 0, ver 5 */
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0xffffffc0,
.mmu_cxr_mask = 0x000000ff,
.mmu_sfsr_mask = 0x00016fff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "LSI L64811",
.iu_version = 0x10 << 24, /* Impl 1, ver 0 */
.fpu_version = 1 << 17, /* FPU version 1 (LSI L64814) */
.mmu_version = 0x10 << 24,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_FSQRT |
CPU_FEATURE_FSMULD,
},
{
.name = "Cypress CY7C601",
.iu_version = 0x11 << 24, /* Impl 1, ver 1 */
.fpu_version = 3 << 17, /* FPU version 3 (Cypress CY7C602) */
.mmu_version = 0x10 << 24,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_FSQRT |
CPU_FEATURE_FSMULD,
},
{
.name = "Cypress CY7C611",
.iu_version = 0x13 << 24, /* Impl 1, ver 3 */
.fpu_version = 3 << 17, /* FPU version 3 (Cypress CY7C602) */
.mmu_version = 0x10 << 24,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_FSQRT |
CPU_FEATURE_FSMULD,
},
{
.name = "TI MicroSparc I",
.iu_version = 0x41000000,
.fpu_version = 4 << 17,
.mmu_version = 0x41000000,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0x00016fff,
.mmu_trcr_mask = 0x0000003f,
.nwindows = 7,
.features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_MUL |
CPU_FEATURE_DIV | CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT |
CPU_FEATURE_FMUL,
},
{
.name = "TI MicroSparc II",
.iu_version = 0x42000000,
.fpu_version = 4 << 17,
.mmu_version = 0x02000000,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x00ffffc0,
.mmu_cxr_mask = 0x000000ff,
.mmu_sfsr_mask = 0x00016fff,
.mmu_trcr_mask = 0x00ffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI MicroSparc IIep",
.iu_version = 0x42000000,
.fpu_version = 4 << 17,
.mmu_version = 0x04000000,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x00ffffc0,
.mmu_cxr_mask = 0x000000ff,
.mmu_sfsr_mask = 0x00016bff,
.mmu_trcr_mask = 0x00ffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI SuperSparc 40", // STP1020NPGA
.iu_version = 0x41000000, // SuperSPARC 2.x
.fpu_version = 0 << 17,
.mmu_version = 0x00000800, // SuperSPARC 2.x, no MXCC
.mmu_bm = 0x00002000,
.mmu_ctpr_mask = 0xffffffc0,
.mmu_cxr_mask = 0x0000ffff,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI SuperSparc 50", // STP1020PGA
.iu_version = 0x40000000, // SuperSPARC 3.x
.fpu_version = 0 << 17,
.mmu_version = 0x01000800, // SuperSPARC 3.x, no MXCC
.mmu_bm = 0x00002000,
.mmu_ctpr_mask = 0xffffffc0,
.mmu_cxr_mask = 0x0000ffff,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI SuperSparc 51",
.iu_version = 0x40000000, // SuperSPARC 3.x
.fpu_version = 0 << 17,
.mmu_version = 0x01000000, // SuperSPARC 3.x, MXCC
.mmu_bm = 0x00002000,
.mmu_ctpr_mask = 0xffffffc0,
.mmu_cxr_mask = 0x0000ffff,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.mxcc_version = 0x00000104,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI SuperSparc 60", // STP1020APGA
.iu_version = 0x40000000, // SuperSPARC 3.x
.fpu_version = 0 << 17,
.mmu_version = 0x01000800, // SuperSPARC 3.x, no MXCC
.mmu_bm = 0x00002000,
.mmu_ctpr_mask = 0xffffffc0,
.mmu_cxr_mask = 0x0000ffff,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI SuperSparc 61",
.iu_version = 0x44000000, // SuperSPARC 3.x
.fpu_version = 0 << 17,
.mmu_version = 0x01000000, // SuperSPARC 3.x, MXCC
.mmu_bm = 0x00002000,
.mmu_ctpr_mask = 0xffffffc0,
.mmu_cxr_mask = 0x0000ffff,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.mxcc_version = 0x00000104,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "TI SuperSparc II",
.iu_version = 0x40000000, // SuperSPARC II 1.x
.fpu_version = 0 << 17,
.mmu_version = 0x08000000, // SuperSPARC II 1.x, MXCC
.mmu_bm = 0x00002000,
.mmu_ctpr_mask = 0xffffffc0,
.mmu_cxr_mask = 0x0000ffff,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.mxcc_version = 0x00000104,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Ross RT625",
.iu_version = 0x1e000000,
.fpu_version = 1 << 17,
.mmu_version = 0x1e000000,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "Ross RT620",
.iu_version = 0x1f000000,
.fpu_version = 1 << 17,
.mmu_version = 0x1f000000,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "BIT B5010",
.iu_version = 0x20000000,
.fpu_version = 0 << 17, /* B5010/B5110/B5120/B5210 */
.mmu_version = 0x20000000,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | CPU_FEATURE_FSQRT |
CPU_FEATURE_FSMULD,
},
{
.name = "Matsushita MN10501",
.iu_version = 0x50000000,
.fpu_version = 0 << 17,
.mmu_version = 0x50000000,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_FEATURE_FLOAT | CPU_FEATURE_MUL | CPU_FEATURE_FSQRT |
CPU_FEATURE_FSMULD,
},
{
.name = "Weitek W8601",
.iu_version = 0x90 << 24, /* Impl 9, ver 0 */
.fpu_version = 3 << 17, /* FPU version 3 (Weitek WTL3170/2) */
.mmu_version = 0x10 << 24,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "LEON2",
.iu_version = 0xf2000000,
.fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
.mmu_version = 0xf2000000,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
{
.name = "LEON3",
.iu_version = 0xf3000000,
.fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
.mmu_version = 0xf3000000,
.mmu_bm = 0x00004000,
.mmu_ctpr_mask = 0x007ffff0,
.mmu_cxr_mask = 0x0000003f,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES,
},
#endif
};
static const char * const feature_name[] = {
"float",
"float128",
"swap",
"mul",
"div",
"flush",
"fsqrt",
"fmul",
"vis1",
"vis2",
"fsmuld",
"hypv",
"cmt",
"gl",
};
static void print_features(FILE *f,
int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
uint32_t features, const char *prefix)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(feature_name); i++)
if (feature_name[i] && (features & (1 << i))) {
if (prefix)
(*cpu_fprintf)(f, "%s", prefix);
(*cpu_fprintf)(f, "%s ", feature_name[i]);
}
}
static void add_flagname_to_bitmaps(const char *flagname, uint32_t *features)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(feature_name); i++)
if (feature_name[i] && !strcmp(flagname, feature_name[i])) {
*features |= 1 << i;
return;
}
fprintf(stderr, "CPU feature %s not found\n", flagname);
}
static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model)
{
unsigned int i;
const sparc_def_t *def = NULL;
char *s = strdup(cpu_model);
char *featurestr, *name = strtok(s, ",");
uint32_t plus_features = 0;
uint32_t minus_features = 0;
long long iu_version;
uint32_t fpu_version, mmu_version, nwindows;
for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) {
if (strcasecmp(name, sparc_defs[i].name) == 0) {
def = &sparc_defs[i];
}
}
if (!def)
goto error;
memcpy(cpu_def, def, sizeof(*def));
featurestr = strtok(NULL, ",");
while (featurestr) {
char *val;
if (featurestr[0] == '+') {
add_flagname_to_bitmaps(featurestr + 1, &plus_features);
} else if (featurestr[0] == '-') {
add_flagname_to_bitmaps(featurestr + 1, &minus_features);
} else if ((val = strchr(featurestr, '='))) {
*val = 0; val++;
if (!strcmp(featurestr, "iu_version")) {
char *err;
iu_version = strtoll(val, &err, 0);
if (!*val || *err) {
fprintf(stderr, "bad numerical value %s\n", val);
goto error;
}
cpu_def->iu_version = iu_version;
#ifdef DEBUG_FEATURES
fprintf(stderr, "iu_version %llx\n", iu_version);
#endif
} else if (!strcmp(featurestr, "fpu_version")) {
char *err;
fpu_version = strtol(val, &err, 0);
if (!*val || *err) {
fprintf(stderr, "bad numerical value %s\n", val);
goto error;
}
cpu_def->fpu_version = fpu_version;
#ifdef DEBUG_FEATURES
fprintf(stderr, "fpu_version %x\n", fpu_version);
#endif
} else if (!strcmp(featurestr, "mmu_version")) {
char *err;
mmu_version = strtol(val, &err, 0);
if (!*val || *err) {
fprintf(stderr, "bad numerical value %s\n", val);
goto error;
}
cpu_def->mmu_version = mmu_version;
#ifdef DEBUG_FEATURES
fprintf(stderr, "mmu_version %x\n", mmu_version);
#endif
} else if (!strcmp(featurestr, "nwindows")) {
char *err;
nwindows = strtol(val, &err, 0);
if (!*val || *err || nwindows > MAX_NWINDOWS ||
nwindows < MIN_NWINDOWS) {
fprintf(stderr, "bad numerical value %s\n", val);
goto error;
}
cpu_def->nwindows = nwindows;
#ifdef DEBUG_FEATURES
fprintf(stderr, "nwindows %d\n", nwindows);
#endif
} else {
fprintf(stderr, "unrecognized feature %s\n", featurestr);
goto error;
}
} else {
fprintf(stderr, "feature string `%s' not in format "
"(+feature|-feature|feature=xyz)\n", featurestr);
goto error;
}
featurestr = strtok(NULL, ",");
}
cpu_def->features |= plus_features;
cpu_def->features &= ~minus_features;
#ifdef DEBUG_FEATURES
print_features(stderr, fprintf, cpu_def->features, NULL);
#endif
free(s);
return 0;
error:
free(s);
return -1;
}
void sparc_cpu_list(FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) {
(*cpu_fprintf)(f, "Sparc %16s IU " TARGET_FMT_lx " FPU %08x MMU %08x NWINS %d ",
sparc_defs[i].name,
sparc_defs[i].iu_version,
sparc_defs[i].fpu_version,
sparc_defs[i].mmu_version,
sparc_defs[i].nwindows);
print_features(f, cpu_fprintf, CPU_DEFAULT_FEATURES &
~sparc_defs[i].features, "-");
print_features(f, cpu_fprintf, ~CPU_DEFAULT_FEATURES &
sparc_defs[i].features, "+");
(*cpu_fprintf)(f, "\n");
}
(*cpu_fprintf)(f, "Default CPU feature flags (use '-' to remove): ");
print_features(f, cpu_fprintf, CPU_DEFAULT_FEATURES, NULL);
(*cpu_fprintf)(f, "\n");
(*cpu_fprintf)(f, "Available CPU feature flags (use '+' to add): ");
print_features(f, cpu_fprintf, ~CPU_DEFAULT_FEATURES, NULL);
(*cpu_fprintf)(f, "\n");
(*cpu_fprintf)(f, "Numerical features (use '=' to set): iu_version "
"fpu_version mmu_version nwindows\n");
}
static void cpu_print_cc(FILE *f,
int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
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,
int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
int flags)
{
int i, x;
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 < 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 < 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 < TARGET_FPREGS; i++) {
if ((i & 3) == 0)
cpu_fprintf(f, "%%f%02d:", i);
cpu_fprintf(f, " %016f", *(float *)&env->fpr[i]);
if ((i & 3) == 3)
cpu_fprintf(f, "\n");
}
#ifdef TARGET_SPARC64
cpu_fprintf(f, "pstate: %08x ccr: %02x (icc: ", env->pstate,
GET_CCR(env));
cpu_print_cc(f, cpu_fprintf, GET_CCR(env) << PSR_CARRY_SHIFT);
cpu_fprintf(f, " xcc: ");
cpu_print_cc(f, cpu_fprintf, 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
cpu_fprintf(f, "psr: %08x (icc: ", GET_PSR(env));
cpu_print_cc(f, cpu_fprintf, 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
}