Move target-* CPU file into a target/ folder

We've currently got 18 architectures in QEMU, and thus 18 target-xxx
folders in the root folder of the QEMU source tree. More architectures
(e.g. RISC-V, AVR) are likely to be included soon, too, so the main
folder of the QEMU sources slowly gets quite overcrowded with the
target-xxx folders.
To disburden the main folder a little bit, let's move the target-xxx
folders into a dedicated target/ folder, so that target-xxx/ simply
becomes target/xxx/ instead.

Acked-by: Laurent Vivier <laurent@vivier.eu> [m68k part]
Acked-by: Bastian Koppelmann <kbastian@mail.uni-paderborn.de> [tricore part]
Acked-by: Michael Walle <michael@walle.cc> [lm32 part]
Acked-by: Cornelia Huck <cornelia.huck@de.ibm.com> [s390x part]
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com> [s390x part]
Acked-by: Eduardo Habkost <ehabkost@redhat.com> [i386 part]
Acked-by: Artyom Tarasenko <atar4qemu@gmail.com> [sparc part]
Acked-by: Richard Henderson <rth@twiddle.net> [alpha part]
Acked-by: Max Filippov <jcmvbkbc@gmail.com> [xtensa part]
Reviewed-by: David Gibson <david@gibson.dropbear.id.au> [ppc part]
Acked-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> [cris&microblaze part]
Acked-by: Guan Xuetao <gxt@mprc.pku.edu.cn> [unicore32 part]
Signed-off-by: Thomas Huth <thuth@redhat.com>
This commit is contained in:
Thomas Huth 2016-10-11 08:56:52 +02:00
parent 82ecffa8c0
commit fcf5ef2ab5
369 changed files with 78 additions and 80 deletions

View file

@ -0,0 +1,7 @@
obj-$(CONFIG_SOFTMMU) += machine.o monitor.o
obj-y += translate.o helper.o cpu.o
obj-y += fop_helper.o cc_helper.o win_helper.o mmu_helper.o ldst_helper.o
obj-$(TARGET_SPARC) += int32_helper.o
obj-$(TARGET_SPARC64) += int64_helper.o
obj-$(TARGET_SPARC64) += vis_helper.o
obj-y += gdbstub.o

88
target/sparc/TODO Normal file
View file

@ -0,0 +1,88 @@
TODO-list:
CPU common:
- Unimplemented features/bugs:
- Delay slot handling may fail sometimes (branch end of page, delay
slot next page)
- Atomical instructions
- CPU features should match real CPUs (also ASI selection)
- Optimizations/improvements:
- Condition code/branch handling like x86, also for FPU?
- Remove remaining explicit alignment checks
- Global register for regwptr, so that windowed registers can be
accessed directly
- Improve Sparc32plus addressing
- NPC/PC static optimisations (use JUMP_TB when possible)? (Is this
obsolete?)
- Synthetic instructions
- MMU model dependent on CPU model
- Select ASI helper at translation time (on V9 only if known)
- KQemu/KVM support for VM only
- Hardware breakpoint/watchpoint support
- Cache emulation mode
- Reverse-endian pages
- Faster FPU emulation
- Busy loop detection
Sparc32 CPUs:
- Unimplemented features/bugs:
- Sun4/Sun4c MMUs
- Some V8 ASIs
Sparc64 CPUs:
- Unimplemented features/bugs:
- Interrupt handling
- Secondary address space, other MMU functions
- Many V9/UA2005/UA2007 ASIs
- Rest of V9 instructions, missing VIS instructions
- IG/MG/AG vs. UA2007 globals
- Full hypervisor support
- SMP/CMT
- Sun4v CPUs
Sun4:
- To be added
Sun4c:
- A lot of unimplemented features
- Maybe split from Sun4m
Sun4m:
- Unimplemented features/bugs:
- Hardware devices do not match real boards
- Floppy does not work
- CS4231: merge with cs4231a, add DMA
- Add cg6, bwtwo
- Arbitrary resolution support
- PCI for MicroSparc-IIe
- JavaStation machines
- SBus slot probing, FCode ROM support
- SMP probing support
- Interrupt routing does not match real HW
- SuSE 7.3 keyboard sometimes unresponsive
- Gentoo 2004.1 SMP does not work
- SS600MP ledma -> lebuffer
- Type 5 keyboard
- Less fixed hardware choices
- DBRI audio (Am7930)
- BPP parallel
- Diagnostic switch
- ESP PIO mode
Sun4d:
- A lot of unimplemented features:
- SBI
- IO-unit
- Maybe split from Sun4m
Sun4u:
- Unimplemented features/bugs:
- Interrupt controller
- PCI/IOMMU support (Simba, JIO, Tomatillo, Psycho, Schizo, Safari...)
- SMP
- Happy Meal Ethernet, flash, I2C, GPIO
- A lot of real machine types
Sun4v:
- A lot of unimplemented features
- A lot of real machine types

311
target/sparc/asi.h Normal file
View file

@ -0,0 +1,311 @@
#ifndef _SPARC_ASI_H
#define _SPARC_ASI_H
/* asi.h: Address Space Identifier values for the sparc.
*
* Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu)
*
* Pioneer work for sun4m: Paul Hatchman (paul@sfe.com.au)
* Joint edition for sun4c+sun4m: Pete A. Zaitcev <zaitcev@ipmce.su>
*/
/* The first batch are for the sun4c. */
#define ASI_NULL1 0x00
#define ASI_NULL2 0x01
/* sun4c and sun4 control registers and mmu/vac ops */
#define ASI_CONTROL 0x02
#define ASI_SEGMAP 0x03
#define ASI_PTE 0x04
#define ASI_HWFLUSHSEG 0x05
#define ASI_HWFLUSHPAGE 0x06
#define ASI_REGMAP 0x06
#define ASI_HWFLUSHCONTEXT 0x07
#define ASI_USERTXT 0x08
#define ASI_KERNELTXT 0x09
#define ASI_USERDATA 0x0a
#define ASI_KERNELDATA 0x0b
/* VAC Cache flushing on sun4c and sun4 */
#define ASI_FLUSHSEG 0x0c
#define ASI_FLUSHPG 0x0d
#define ASI_FLUSHCTX 0x0e
/* SPARCstation-5: only 6 bits are decoded. */
/* wo = Write Only, rw = Read Write; */
/* ss = Single Size, as = All Sizes; */
#define ASI_M_RES00 0x00 /* Don't touch... */
#define ASI_M_UNA01 0x01 /* Same here... */
#define ASI_M_MXCC 0x02 /* Access to TI VIKING MXCC registers */
#define ASI_M_FLUSH_PROBE 0x03 /* Reference MMU Flush/Probe; rw, ss */
#define ASI_M_MMUREGS 0x04 /* MMU Registers; rw, ss */
#define ASI_M_TLBDIAG 0x05 /* MMU TLB only Diagnostics */
#define ASI_M_DIAGS 0x06 /* Reference MMU Diagnostics */
#define ASI_M_IODIAG 0x07 /* MMU I/O TLB only Diagnostics */
#define ASI_M_USERTXT 0x08 /* Same as ASI_USERTXT; rw, as */
#define ASI_M_KERNELTXT 0x09 /* Same as ASI_KERNELTXT; rw, as */
#define ASI_M_USERDATA 0x0A /* Same as ASI_USERDATA; rw, as */
#define ASI_M_KERNELDATA 0x0B /* Same as ASI_KERNELDATA; rw, as */
#define ASI_M_TXTC_TAG 0x0C /* Instruction Cache Tag; rw, ss */
#define ASI_M_TXTC_DATA 0x0D /* Instruction Cache Data; rw, ss */
#define ASI_M_DATAC_TAG 0x0E /* Data Cache Tag; rw, ss */
#define ASI_M_DATAC_DATA 0x0F /* Data Cache Data; rw, ss */
/* The following cache flushing ASIs work only with the 'sta'
* instruction. Results are unpredictable for 'swap' and 'ldstuba',
* so don't do it.
*/
/* These ASI flushes affect external caches too. */
#define ASI_M_FLUSH_PAGE 0x10 /* Flush I&D Cache Line (page); wo, ss */
#define ASI_M_FLUSH_SEG 0x11 /* Flush I&D Cache Line (seg); wo, ss */
#define ASI_M_FLUSH_REGION 0x12 /* Flush I&D Cache Line (region); wo, ss */
#define ASI_M_FLUSH_CTX 0x13 /* Flush I&D Cache Line (context); wo, ss */
#define ASI_M_FLUSH_USER 0x14 /* Flush I&D Cache Line (user); wo, ss */
/* Block-copy operations are available only on certain V8 cpus. */
#define ASI_M_BCOPY 0x17 /* Block copy */
/* These affect only the ICACHE and are Ross HyperSparc and TurboSparc specific. */
#define ASI_M_IFLUSH_PAGE 0x18 /* Flush I Cache Line (page); wo, ss */
#define ASI_M_IFLUSH_SEG 0x19 /* Flush I Cache Line (seg); wo, ss */
#define ASI_M_IFLUSH_REGION 0x1A /* Flush I Cache Line (region); wo, ss */
#define ASI_M_IFLUSH_CTX 0x1B /* Flush I Cache Line (context); wo, ss */
#define ASI_M_IFLUSH_USER 0x1C /* Flush I Cache Line (user); wo, ss */
/* Block-fill operations are available on certain V8 cpus */
#define ASI_M_BFILL 0x1F
/* This allows direct access to main memory, actually 0x20 to 0x2f are
* the available ASI's for physical ram pass-through, but I don't have
* any idea what the other ones do....
*/
#define ASI_M_BYPASS 0x20 /* Reference MMU bypass; rw, as */
#define ASI_M_FBMEM 0x29 /* Graphics card frame buffer access */
#define ASI_M_VMEUS 0x2A /* VME user 16-bit access */
#define ASI_M_VMEPS 0x2B /* VME priv 16-bit access */
#define ASI_M_VMEUT 0x2C /* VME user 32-bit access */
#define ASI_M_VMEPT 0x2D /* VME priv 32-bit access */
#define ASI_M_SBUS 0x2E /* Direct SBus access */
#define ASI_M_CTL 0x2F /* Control Space (ECC and MXCC are here) */
/* This is ROSS HyperSparc only. */
#define ASI_M_FLUSH_IWHOLE 0x31 /* Flush entire ICACHE; wo, ss */
/* Tsunami/Viking/TurboSparc i/d cache flash clear. */
#define ASI_M_IC_FLCLEAR 0x36
#define ASI_M_DC_FLCLEAR 0x37
#define ASI_M_DCDR 0x39 /* Data Cache Diagnostics Register rw, ss */
#define ASI_M_VIKING_TMP1 0x40 /* Emulation temporary 1 on Viking */
/* only available on SuperSparc I */
/* #define ASI_M_VIKING_TMP2 0x41 */ /* Emulation temporary 2 on Viking */
#define ASI_M_ACTION 0x4c /* Breakpoint Action Register (GNU/Viking) */
/* LEON ASI */
#define ASI_LEON_NOCACHE 0x01
#define ASI_LEON_DCACHE_MISS 0x01
#define ASI_LEON_CACHEREGS 0x02
#define ASI_LEON_IFLUSH 0x10
#define ASI_LEON_DFLUSH 0x11
#define ASI_LEON_MMUFLUSH 0x18
#define ASI_LEON_MMUREGS 0x19
#define ASI_LEON_BYPASS 0x1c
#define ASI_LEON_FLUSH_PAGE 0x10
/* V9 Architecture mandary ASIs. */
#define ASI_N 0x04 /* Nucleus */
#define ASI_NL 0x0c /* Nucleus, little endian */
#define ASI_AIUP 0x10 /* Primary, user */
#define ASI_AIUS 0x11 /* Secondary, user */
#define ASI_AIUPL 0x18 /* Primary, user, little endian */
#define ASI_AIUSL 0x19 /* Secondary, user, little endian */
#define ASI_P 0x80 /* Primary, implicit */
#define ASI_S 0x81 /* Secondary, implicit */
#define ASI_PNF 0x82 /* Primary, no fault */
#define ASI_SNF 0x83 /* Secondary, no fault */
#define ASI_PL 0x88 /* Primary, implicit, l-endian */
#define ASI_SL 0x89 /* Secondary, implicit, l-endian */
#define ASI_PNFL 0x8a /* Primary, no fault, l-endian */
#define ASI_SNFL 0x8b /* Secondary, no fault, l-endian */
/* SpitFire and later extended ASIs. The "(III)" marker designates
* UltraSparc-III and later specific ASIs. The "(CMT)" marker designates
* Chip Multi Threading specific ASIs. "(NG)" designates Niagara specific
* ASIs, "(4V)" designates SUN4V specific ASIs. "(NG4)" designates SPARC-T4
* and later ASIs.
*/
#define ASI_REAL 0x14 /* Real address, cachable */
#define ASI_PHYS_USE_EC 0x14 /* PADDR, E-cachable */
#define ASI_REAL_IO 0x15 /* Real address, non-cachable */
#define ASI_PHYS_BYPASS_EC_E 0x15 /* PADDR, E-bit */
#define ASI_BLK_AIUP_4V 0x16 /* (4V) Prim, user, block ld/st */
#define ASI_BLK_AIUS_4V 0x17 /* (4V) Sec, user, block ld/st */
#define ASI_REAL_L 0x1c /* Real address, cachable, LE */
#define ASI_PHYS_USE_EC_L 0x1c /* PADDR, E-cachable, little endian*/
#define ASI_REAL_IO_L 0x1d /* Real address, non-cachable, LE */
#define ASI_PHYS_BYPASS_EC_E_L 0x1d /* PADDR, E-bit, little endian */
#define ASI_BLK_AIUP_L_4V 0x1e /* (4V) Prim, user, block, l-endian*/
#define ASI_BLK_AIUS_L_4V 0x1f /* (4V) Sec, user, block, l-endian */
#define ASI_SCRATCHPAD 0x20 /* (4V) Scratch Pad Registers */
#define ASI_MMU 0x21 /* (4V) MMU Context Registers */
#define ASI_TWINX_AIUP 0x22 /* twin load, primary user */
#define ASI_TWINX_AIUS 0x23 /* twin load, secondary user */
#define ASI_BLK_INIT_QUAD_LDD_AIUS 0x23 /* (NG) init-store, twin load,
* secondary, user
*/
#define ASI_NUCLEUS_QUAD_LDD 0x24 /* Cachable, qword load */
#define ASI_QUEUE 0x25 /* (4V) Interrupt Queue Registers */
#define ASI_TWINX_REAL 0x26 /* twin load, real, cachable */
#define ASI_QUAD_LDD_PHYS_4V 0x26 /* (4V) Physical, qword load */
#define ASI_TWINX_N 0x27 /* twin load, nucleus */
#define ASI_TWINX_AIUP_L 0x2a /* twin load, primary user, LE */
#define ASI_TWINX_AIUS_L 0x2b /* twin load, secondary user, LE */
#define ASI_NUCLEUS_QUAD_LDD_L 0x2c /* Cachable, qword load, l-endian */
#define ASI_TWINX_REAL_L 0x2e /* twin load, real, cachable, LE */
#define ASI_QUAD_LDD_PHYS_L_4V 0x2e /* (4V) Phys, qword load, l-endian */
#define ASI_TWINX_NL 0x2f /* twin load, nucleus, LE */
#define ASI_PCACHE_DATA_STATUS 0x30 /* (III) PCache data stat RAM diag */
#define ASI_PCACHE_DATA 0x31 /* (III) PCache data RAM diag */
#define ASI_PCACHE_TAG 0x32 /* (III) PCache tag RAM diag */
#define ASI_PCACHE_SNOOP_TAG 0x33 /* (III) PCache snoop tag RAM diag */
#define ASI_QUAD_LDD_PHYS 0x34 /* (III+) PADDR, qword load */
#define ASI_WCACHE_VALID_BITS 0x38 /* (III) WCache Valid Bits diag */
#define ASI_WCACHE_DATA 0x39 /* (III) WCache data RAM diag */
#define ASI_WCACHE_TAG 0x3a /* (III) WCache tag RAM diag */
#define ASI_WCACHE_SNOOP_TAG 0x3b /* (III) WCache snoop tag RAM diag */
#define ASI_QUAD_LDD_PHYS_L 0x3c /* (III+) PADDR, qw-load, l-endian */
#define ASI_SRAM_FAST_INIT 0x40 /* (III+) Fast SRAM init */
#define ASI_CORE_AVAILABLE 0x41 /* (CMT) LP Available */
#define ASI_CORE_ENABLE_STAT 0x41 /* (CMT) LP Enable Status */
#define ASI_CORE_ENABLE 0x41 /* (CMT) LP Enable RW */
#define ASI_XIR_STEERING 0x41 /* (CMT) XIR Steering RW */
#define ASI_CORE_RUNNING_RW 0x41 /* (CMT) LP Running RW */
#define ASI_CORE_RUNNING_W1S 0x41 /* (CMT) LP Running Write-One Set */
#define ASI_CORE_RUNNING_W1C 0x41 /* (CMT) LP Running Write-One Clr */
#define ASI_CORE_RUNNING_STAT 0x41 /* (CMT) LP Running Status */
#define ASI_CMT_ERROR_STEERING 0x41 /* (CMT) Error Steering RW */
#define ASI_DCACHE_INVALIDATE 0x42 /* (III) DCache Invalidate diag */
#define ASI_DCACHE_UTAG 0x43 /* (III) DCache uTag diag */
#define ASI_DCACHE_SNOOP_TAG 0x44 /* (III) DCache snoop tag RAM diag */
#define ASI_LSU_CONTROL 0x45 /* Load-store control unit */
#define ASI_DCU_CONTROL_REG 0x45 /* (III) DCache Unit Control reg */
#define ASI_DCACHE_DATA 0x46 /* DCache data-ram diag access */
#define ASI_DCACHE_TAG 0x47 /* Dcache tag/valid ram diag access*/
#define ASI_INTR_DISPATCH_STAT 0x48 /* IRQ vector dispatch status */
#define ASI_INTR_RECEIVE 0x49 /* IRQ vector receive status */
#define ASI_UPA_CONFIG 0x4a /* UPA config space */
#define ASI_JBUS_CONFIG 0x4a /* (IIIi) JBUS Config Register */
#define ASI_SAFARI_CONFIG 0x4a /* (III) Safari Config Register */
#define ASI_SAFARI_ADDRESS 0x4a /* (III) Safari Address Register */
#define ASI_ESTATE_ERROR_EN 0x4b /* E-cache error enable space */
#define ASI_AFSR 0x4c /* Async fault status register */
#define ASI_AFAR 0x4d /* Async fault address register */
#define ASI_EC_TAG_DATA 0x4e /* E-cache tag/valid ram diag acc */
#define ASI_IMMU 0x50 /* Insn-MMU main register space */
#define ASI_IMMU_TSB_8KB_PTR 0x51 /* Insn-MMU 8KB TSB pointer reg */
#define ASI_IMMU_TSB_64KB_PTR 0x52 /* Insn-MMU 64KB TSB pointer reg */
#define ASI_ITLB_DATA_IN 0x54 /* Insn-MMU TLB data in reg */
#define ASI_ITLB_DATA_ACCESS 0x55 /* Insn-MMU TLB data access reg */
#define ASI_ITLB_TAG_READ 0x56 /* Insn-MMU TLB tag read reg */
#define ASI_IMMU_DEMAP 0x57 /* Insn-MMU TLB demap */
#define ASI_DMMU 0x58 /* Data-MMU main register space */
#define ASI_DMMU_TSB_8KB_PTR 0x59 /* Data-MMU 8KB TSB pointer reg */
#define ASI_DMMU_TSB_64KB_PTR 0x5a /* Data-MMU 16KB TSB pointer reg */
#define ASI_DMMU_TSB_DIRECT_PTR 0x5b /* Data-MMU TSB direct pointer reg */
#define ASI_DTLB_DATA_IN 0x5c /* Data-MMU TLB data in reg */
#define ASI_DTLB_DATA_ACCESS 0x5d /* Data-MMU TLB data access reg */
#define ASI_DTLB_TAG_READ 0x5e /* Data-MMU TLB tag read reg */
#define ASI_DMMU_DEMAP 0x5f /* Data-MMU TLB demap */
#define ASI_IIU_INST_TRAP 0x60 /* (III) Instruction Breakpoint */
#define ASI_INTR_ID 0x63 /* (CMT) Interrupt ID register */
#define ASI_CORE_ID 0x63 /* (CMT) LP ID register */
#define ASI_CESR_ID 0x63 /* (CMT) CESR ID register */
#define ASI_IC_INSTR 0x66 /* Insn cache instrucion ram diag */
#define ASI_IC_TAG 0x67 /* Insn cache tag/valid ram diag */
#define ASI_IC_STAG 0x68 /* (III) Insn cache snoop tag ram */
#define ASI_IC_PRE_DECODE 0x6e /* Insn cache pre-decode ram diag */
#define ASI_IC_NEXT_FIELD 0x6f /* Insn cache next-field ram diag */
#define ASI_BRPRED_ARRAY 0x6f /* (III) Branch Prediction RAM diag*/
#define ASI_BLK_AIUP 0x70 /* Primary, user, block load/store */
#define ASI_BLK_AIUS 0x71 /* Secondary, user, block ld/st */
#define ASI_MCU_CTRL_REG 0x72 /* (III) Memory controller regs */
#define ASI_EC_DATA 0x74 /* (III) E-cache data staging reg */
#define ASI_EC_CTRL 0x75 /* (III) E-cache control reg */
#define ASI_EC_W 0x76 /* E-cache diag write access */
#define ASI_UDB_ERROR_W 0x77 /* External UDB error regs W */
#define ASI_UDB_CONTROL_W 0x77 /* External UDB control regs W */
#define ASI_INTR_W 0x77 /* IRQ vector dispatch write */
#define ASI_INTR_DATAN_W 0x77 /* (III) Out irq vector data reg N */
#define ASI_INTR_DISPATCH_W 0x77 /* (III) Interrupt vector dispatch */
#define ASI_BLK_AIUPL 0x78 /* Primary, user, little, blk ld/st*/
#define ASI_BLK_AIUSL 0x79 /* Secondary, user, little, blk ld/st*/
#define ASI_EC_R 0x7e /* E-cache diag read access */
#define ASI_UDBH_ERROR_R 0x7f /* External UDB error regs rd hi */
#define ASI_UDBL_ERROR_R 0x7f /* External UDB error regs rd low */
#define ASI_UDBH_CONTROL_R 0x7f /* External UDB control regs rd hi */
#define ASI_UDBL_CONTROL_R 0x7f /* External UDB control regs rd low*/
#define ASI_INTR_R 0x7f /* IRQ vector dispatch read */
#define ASI_INTR_DATAN_R 0x7f /* (III) In irq vector data reg N */
#define ASI_PIC 0xb0 /* (NG4) PIC registers */
#define ASI_PST8_P 0xc0 /* Primary, 8 8-bit, partial */
#define ASI_PST8_S 0xc1 /* Secondary, 8 8-bit, partial */
#define ASI_PST16_P 0xc2 /* Primary, 4 16-bit, partial */
#define ASI_PST16_S 0xc3 /* Secondary, 4 16-bit, partial */
#define ASI_PST32_P 0xc4 /* Primary, 2 32-bit, partial */
#define ASI_PST32_S 0xc5 /* Secondary, 2 32-bit, partial */
#define ASI_PST8_PL 0xc8 /* Primary, 8 8-bit, partial, L */
#define ASI_PST8_SL 0xc9 /* Secondary, 8 8-bit, partial, L */
#define ASI_PST16_PL 0xca /* Primary, 4 16-bit, partial, L */
#define ASI_PST16_SL 0xcb /* Secondary, 4 16-bit, partial, L */
#define ASI_PST32_PL 0xcc /* Primary, 2 32-bit, partial, L */
#define ASI_PST32_SL 0xcd /* Secondary, 2 32-bit, partial, L */
#define ASI_FL8_P 0xd0 /* Primary, 1 8-bit, fpu ld/st */
#define ASI_FL8_S 0xd1 /* Secondary, 1 8-bit, fpu ld/st */
#define ASI_FL16_P 0xd2 /* Primary, 1 16-bit, fpu ld/st */
#define ASI_FL16_S 0xd3 /* Secondary, 1 16-bit, fpu ld/st */
#define ASI_FL8_PL 0xd8 /* Primary, 1 8-bit, fpu ld/st, L */
#define ASI_FL8_SL 0xd9 /* Secondary, 1 8-bit, fpu ld/st, L*/
#define ASI_FL16_PL 0xda /* Primary, 1 16-bit, fpu ld/st, L */
#define ASI_FL16_SL 0xdb /* Secondary, 1 16-bit, fpu ld/st,L*/
#define ASI_BLK_COMMIT_P 0xe0 /* Primary, blk store commit */
#define ASI_BLK_COMMIT_S 0xe1 /* Secondary, blk store commit */
#define ASI_TWINX_P 0xe2 /* twin load, primary implicit */
#define ASI_BLK_INIT_QUAD_LDD_P 0xe2 /* (NG) init-store, twin load,
* primary, implicit */
#define ASI_TWINX_S 0xe3 /* twin load, secondary implicit */
#define ASI_BLK_INIT_QUAD_LDD_S 0xe3 /* (NG) init-store, twin load,
* secondary, implicit */
#define ASI_TWINX_PL 0xea /* twin load, primary implicit, LE */
#define ASI_TWINX_SL 0xeb /* twin load, secondary implicit, LE */
#define ASI_BLK_P 0xf0 /* Primary, blk ld/st */
#define ASI_BLK_S 0xf1 /* Secondary, blk ld/st */
#define ASI_ST_BLKINIT_MRU_P 0xf2 /* (NG4) init-store, twin load,
* Most-Recently-Used, primary,
* implicit
*/
#define ASI_ST_BLKINIT_MRU_S 0xf2 /* (NG4) init-store, twin load,
* Most-Recently-Used, secondary,
* implicit
*/
#define ASI_BLK_PL 0xf8 /* Primary, blk ld/st, little */
#define ASI_BLK_SL 0xf9 /* Secondary, blk ld/st, little */
#define ASI_ST_BLKINIT_MRU_PL 0xfa /* (NG4) init-store, twin load,
* Most-Recently-Used, primary,
* implicit, little-endian
*/
#define ASI_ST_BLKINIT_MRU_SL 0xfb /* (NG4) init-store, twin load,
* Most-Recently-Used, secondary,
* implicit, little-endian
*/
#endif /* _SPARC_ASI_H */

471
target/sparc/cc_helper.c Normal file
View file

@ -0,0 +1,471 @@
/*
* Helpers for lazy condition code handling
*
* 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 "qemu/osdep.h"
#include "cpu.h"
#include "exec/helper-proto.h"
static uint32_t compute_all_flags(CPUSPARCState *env)
{
return env->psr & PSR_ICC;
}
static uint32_t compute_C_flags(CPUSPARCState *env)
{
return env->psr & PSR_CARRY;
}
static inline uint32_t get_NZ_icc(int32_t dst)
{
uint32_t ret = 0;
if (dst == 0) {
ret = PSR_ZERO;
} else if (dst < 0) {
ret = PSR_NEG;
}
return ret;
}
#ifdef TARGET_SPARC64
static uint32_t compute_all_flags_xcc(CPUSPARCState *env)
{
return env->xcc & PSR_ICC;
}
static uint32_t compute_C_flags_xcc(CPUSPARCState *env)
{
return env->xcc & PSR_CARRY;
}
static inline uint32_t get_NZ_xcc(target_long dst)
{
uint32_t ret = 0;
if (!dst) {
ret = PSR_ZERO;
} else if (dst < 0) {
ret = PSR_NEG;
}
return ret;
}
#endif
static inline uint32_t get_V_div_icc(target_ulong src2)
{
uint32_t ret = 0;
if (src2 != 0) {
ret = PSR_OVF;
}
return ret;
}
static uint32_t compute_all_div(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_icc(CC_DST);
ret |= get_V_div_icc(CC_SRC2);
return ret;
}
static uint32_t compute_C_div(CPUSPARCState *env)
{
return 0;
}
static inline uint32_t get_C_add_icc(uint32_t dst, uint32_t src1)
{
uint32_t ret = 0;
if (dst < src1) {
ret = PSR_CARRY;
}
return ret;
}
static inline uint32_t get_C_addx_icc(uint32_t dst, uint32_t src1,
uint32_t src2)
{
uint32_t ret = 0;
if (((src1 & src2) | (~dst & (src1 | src2))) & (1U << 31)) {
ret = PSR_CARRY;
}
return ret;
}
static inline uint32_t get_V_add_icc(uint32_t dst, uint32_t src1,
uint32_t src2)
{
uint32_t ret = 0;
if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1U << 31)) {
ret = PSR_OVF;
}
return ret;
}
#ifdef TARGET_SPARC64
static inline uint32_t get_C_add_xcc(target_ulong dst, target_ulong src1)
{
uint32_t ret = 0;
if (dst < src1) {
ret = PSR_CARRY;
}
return ret;
}
static inline uint32_t get_C_addx_xcc(target_ulong dst, target_ulong src1,
target_ulong src2)
{
uint32_t ret = 0;
if (((src1 & src2) | (~dst & (src1 | src2))) & (1ULL << 63)) {
ret = PSR_CARRY;
}
return ret;
}
static inline uint32_t get_V_add_xcc(target_ulong dst, target_ulong src1,
target_ulong src2)
{
uint32_t ret = 0;
if (((src1 ^ src2 ^ -1) & (src1 ^ dst)) & (1ULL << 63)) {
ret = PSR_OVF;
}
return ret;
}
static uint32_t compute_all_add_xcc(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_xcc(CC_DST);
ret |= get_C_add_xcc(CC_DST, CC_SRC);
ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_C_add_xcc(CPUSPARCState *env)
{
return get_C_add_xcc(CC_DST, CC_SRC);
}
#endif
static uint32_t compute_all_add(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_icc(CC_DST);
ret |= get_C_add_icc(CC_DST, CC_SRC);
ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_C_add(CPUSPARCState *env)
{
return get_C_add_icc(CC_DST, CC_SRC);
}
#ifdef TARGET_SPARC64
static uint32_t compute_all_addx_xcc(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_xcc(CC_DST);
ret |= get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2);
ret |= get_V_add_xcc(CC_DST, CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_C_addx_xcc(CPUSPARCState *env)
{
return get_C_addx_xcc(CC_DST, CC_SRC, CC_SRC2);
}
#endif
static uint32_t compute_all_addx(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_icc(CC_DST);
ret |= get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2);
ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_C_addx(CPUSPARCState *env)
{
return get_C_addx_icc(CC_DST, CC_SRC, CC_SRC2);
}
static inline uint32_t get_V_tag_icc(target_ulong src1, target_ulong src2)
{
uint32_t ret = 0;
if ((src1 | src2) & 0x3) {
ret = PSR_OVF;
}
return ret;
}
static uint32_t compute_all_tadd(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_icc(CC_DST);
ret |= get_C_add_icc(CC_DST, CC_SRC);
ret |= get_V_add_icc(CC_DST, CC_SRC, CC_SRC2);
ret |= get_V_tag_icc(CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_all_taddtv(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_icc(CC_DST);
ret |= get_C_add_icc(CC_DST, CC_SRC);
return ret;
}
static inline uint32_t get_C_sub_icc(uint32_t src1, uint32_t src2)
{
uint32_t ret = 0;
if (src1 < src2) {
ret = PSR_CARRY;
}
return ret;
}
static inline uint32_t get_C_subx_icc(uint32_t dst, uint32_t src1,
uint32_t src2)
{
uint32_t ret = 0;
if (((~src1 & src2) | (dst & (~src1 | src2))) & (1U << 31)) {
ret = PSR_CARRY;
}
return ret;
}
static inline uint32_t get_V_sub_icc(uint32_t dst, uint32_t src1,
uint32_t src2)
{
uint32_t ret = 0;
if (((src1 ^ src2) & (src1 ^ dst)) & (1U << 31)) {
ret = PSR_OVF;
}
return ret;
}
#ifdef TARGET_SPARC64
static inline uint32_t get_C_sub_xcc(target_ulong src1, target_ulong src2)
{
uint32_t ret = 0;
if (src1 < src2) {
ret = PSR_CARRY;
}
return ret;
}
static inline uint32_t get_C_subx_xcc(target_ulong dst, target_ulong src1,
target_ulong src2)
{
uint32_t ret = 0;
if (((~src1 & src2) | (dst & (~src1 | src2))) & (1ULL << 63)) {
ret = PSR_CARRY;
}
return ret;
}
static inline uint32_t get_V_sub_xcc(target_ulong dst, target_ulong src1,
target_ulong src2)
{
uint32_t ret = 0;
if (((src1 ^ src2) & (src1 ^ dst)) & (1ULL << 63)) {
ret = PSR_OVF;
}
return ret;
}
static uint32_t compute_all_sub_xcc(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_xcc(CC_DST);
ret |= get_C_sub_xcc(CC_SRC, CC_SRC2);
ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_C_sub_xcc(CPUSPARCState *env)
{
return get_C_sub_xcc(CC_SRC, CC_SRC2);
}
#endif
static uint32_t compute_all_sub(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_icc(CC_DST);
ret |= get_C_sub_icc(CC_SRC, CC_SRC2);
ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_C_sub(CPUSPARCState *env)
{
return get_C_sub_icc(CC_SRC, CC_SRC2);
}
#ifdef TARGET_SPARC64
static uint32_t compute_all_subx_xcc(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_xcc(CC_DST);
ret |= get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2);
ret |= get_V_sub_xcc(CC_DST, CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_C_subx_xcc(CPUSPARCState *env)
{
return get_C_subx_xcc(CC_DST, CC_SRC, CC_SRC2);
}
#endif
static uint32_t compute_all_subx(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_icc(CC_DST);
ret |= get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2);
ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_C_subx(CPUSPARCState *env)
{
return get_C_subx_icc(CC_DST, CC_SRC, CC_SRC2);
}
static uint32_t compute_all_tsub(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_icc(CC_DST);
ret |= get_C_sub_icc(CC_SRC, CC_SRC2);
ret |= get_V_sub_icc(CC_DST, CC_SRC, CC_SRC2);
ret |= get_V_tag_icc(CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_all_tsubtv(CPUSPARCState *env)
{
uint32_t ret;
ret = get_NZ_icc(CC_DST);
ret |= get_C_sub_icc(CC_SRC, CC_SRC2);
return ret;
}
static uint32_t compute_all_logic(CPUSPARCState *env)
{
return get_NZ_icc(CC_DST);
}
static uint32_t compute_C_logic(CPUSPARCState *env)
{
return 0;
}
#ifdef TARGET_SPARC64
static uint32_t compute_all_logic_xcc(CPUSPARCState *env)
{
return get_NZ_xcc(CC_DST);
}
#endif
typedef struct CCTable {
uint32_t (*compute_all)(CPUSPARCState *env); /* return all the flags */
uint32_t (*compute_c)(CPUSPARCState *env); /* return the C flag */
} CCTable;
static const CCTable icc_table[CC_OP_NB] = {
/* CC_OP_DYNAMIC should never happen */
[CC_OP_FLAGS] = { compute_all_flags, compute_C_flags },
[CC_OP_DIV] = { compute_all_div, compute_C_div },
[CC_OP_ADD] = { compute_all_add, compute_C_add },
[CC_OP_ADDX] = { compute_all_addx, compute_C_addx },
[CC_OP_TADD] = { compute_all_tadd, compute_C_add },
[CC_OP_TADDTV] = { compute_all_taddtv, compute_C_add },
[CC_OP_SUB] = { compute_all_sub, compute_C_sub },
[CC_OP_SUBX] = { compute_all_subx, compute_C_subx },
[CC_OP_TSUB] = { compute_all_tsub, compute_C_sub },
[CC_OP_TSUBTV] = { compute_all_tsubtv, compute_C_sub },
[CC_OP_LOGIC] = { compute_all_logic, compute_C_logic },
};
#ifdef TARGET_SPARC64
static const CCTable xcc_table[CC_OP_NB] = {
/* CC_OP_DYNAMIC should never happen */
[CC_OP_FLAGS] = { compute_all_flags_xcc, compute_C_flags_xcc },
[CC_OP_DIV] = { compute_all_logic_xcc, compute_C_logic },
[CC_OP_ADD] = { compute_all_add_xcc, compute_C_add_xcc },
[CC_OP_ADDX] = { compute_all_addx_xcc, compute_C_addx_xcc },
[CC_OP_TADD] = { compute_all_add_xcc, compute_C_add_xcc },
[CC_OP_TADDTV] = { compute_all_add_xcc, compute_C_add_xcc },
[CC_OP_SUB] = { compute_all_sub_xcc, compute_C_sub_xcc },
[CC_OP_SUBX] = { compute_all_subx_xcc, compute_C_subx_xcc },
[CC_OP_TSUB] = { compute_all_sub_xcc, compute_C_sub_xcc },
[CC_OP_TSUBTV] = { compute_all_sub_xcc, compute_C_sub_xcc },
[CC_OP_LOGIC] = { compute_all_logic_xcc, compute_C_logic },
};
#endif
void helper_compute_psr(CPUSPARCState *env)
{
uint32_t new_psr;
new_psr = icc_table[CC_OP].compute_all(env);
env->psr = new_psr;
#ifdef TARGET_SPARC64
new_psr = xcc_table[CC_OP].compute_all(env);
env->xcc = new_psr;
#endif
CC_OP = CC_OP_FLAGS;
}
uint32_t helper_compute_C_icc(CPUSPARCState *env)
{
return icc_table[CC_OP].compute_c(env) >> PSR_CARRY_SHIFT;
}

56
target/sparc/cpu-qom.h Normal file
View file

@ -0,0 +1,56 @@
/*
* QEMU SPARC CPU
*
* Copyright (c) 2012 SUSE LINUX Products GmbH
*
* 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.1 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/lgpl-2.1.html>
*/
#ifndef QEMU_SPARC_CPU_QOM_H
#define QEMU_SPARC_CPU_QOM_H
#include "qom/cpu.h"
#ifdef TARGET_SPARC64
#define TYPE_SPARC_CPU "sparc64-cpu"
#else
#define TYPE_SPARC_CPU "sparc-cpu"
#endif
#define SPARC_CPU_CLASS(klass) \
OBJECT_CLASS_CHECK(SPARCCPUClass, (klass), TYPE_SPARC_CPU)
#define SPARC_CPU(obj) \
OBJECT_CHECK(SPARCCPU, (obj), TYPE_SPARC_CPU)
#define SPARC_CPU_GET_CLASS(obj) \
OBJECT_GET_CLASS(SPARCCPUClass, (obj), TYPE_SPARC_CPU)
/**
* SPARCCPUClass:
* @parent_realize: The parent class' realize handler.
* @parent_reset: The parent class' reset handler.
*
* A SPARC CPU model.
*/
typedef struct SPARCCPUClass {
/*< private >*/
CPUClass parent_class;
/*< public >*/
DeviceRealize parent_realize;
void (*parent_reset)(CPUState *cpu);
} SPARCCPUClass;
typedef struct SPARCCPU SPARCCPU;
#endif

895
target/sparc/cpu.c Normal file
View file

@ -0,0 +1,895 @@
/*
* Sparc CPU init 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 "qemu/osdep.h"
#include "qapi/error.h"
#include "cpu.h"
#include "qemu/error-report.h"
#include "exec/exec-all.h"
//#define DEBUG_FEATURES
static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *cpu_model);
/* CPUClass::reset() */
static void sparc_cpu_reset(CPUState *s)
{
SPARCCPU *cpu = SPARC_CPU(s);
SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(cpu);
CPUSPARCState *env = &cpu->env;
scc->parent_reset(s);
memset(env, 0, offsetof(CPUSPARCState, version));
tlb_flush(s, 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;
env->psrs = 1;
env->psrps = 1;
#endif
#ifdef TARGET_SPARC64
env->pstate = PS_PRIV|PS_RED|PS_PEF|PS_AG;
env->hpstate = cpu_has_hypervisor(env) ? HS_PRIV : 0;
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
env->cache_control = 0;
}
static bool sparc_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
{
if (interrupt_request & CPU_INTERRUPT_HARD) {
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
if (cpu_interrupts_enabled(env) && env->interrupt_index > 0) {
int pil = env->interrupt_index & 0xf;
int type = env->interrupt_index & 0xf0;
if (type != TT_EXTINT || cpu_pil_allowed(env, pil)) {
cs->exception_index = env->interrupt_index;
sparc_cpu_do_interrupt(cs);
return true;
}
}
}
return false;
}
static void cpu_sparc_disas_set_info(CPUState *cpu, disassemble_info *info)
{
info->print_insn = print_insn_sparc;
#ifdef TARGET_SPARC64
info->mach = bfd_mach_sparc_v9b;
#endif
}
static void sparc_cpu_parse_features(CPUState *cs, char *features,
Error **errp);
static int cpu_sparc_register(SPARCCPU *cpu, const char *cpu_model)
{
CPUSPARCState *env = &cpu->env;
char *s = g_strdup(cpu_model);
char *featurestr, *name = strtok(s, ",");
sparc_def_t def1, *def = &def1;
Error *err = NULL;
if (cpu_sparc_find_by_name(def, name) < 0) {
g_free(s);
return -1;
}
env->def = g_memdup(def, sizeof(*def));
featurestr = strtok(NULL, ",");
sparc_cpu_parse_features(CPU(cpu), featurestr, &err);
g_free(s);
if (err) {
error_report_err(err);
return -1;
}
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;
}
SPARCCPU *cpu_sparc_init(const char *cpu_model)
{
SPARCCPU *cpu;
cpu = SPARC_CPU(object_new(TYPE_SPARC_CPU));
if (cpu_sparc_register(cpu, cpu_model) < 0) {
object_unref(OBJECT(cpu));
return NULL;
}
object_property_set_bool(OBJECT(cpu), true, "realized", NULL);
return cpu;
}
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 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 = "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 = "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 | CPU_FEATURE_TA0_SHUTDOWN,
},
{
.name = "LEON3",
.iu_version = 0xf3000000,
.fpu_version = 4 << 17, /* FPU version 4 (Meiko) */
.mmu_version = 0xf3000000,
.mmu_bm = 0x00000000,
.mmu_ctpr_mask = 0xfffffffc,
.mmu_cxr_mask = 0x000000ff,
.mmu_sfsr_mask = 0xffffffff,
.mmu_trcr_mask = 0xffffffff,
.nwindows = 8,
.features = CPU_DEFAULT_FEATURES | CPU_FEATURE_TA0_SHUTDOWN |
CPU_FEATURE_ASR17 | CPU_FEATURE_CACHE_CTRL | CPU_FEATURE_POWERDOWN |
CPU_FEATURE_CASA,
},
#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, fprintf_function cpu_fprintf,
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;
}
}
error_report("CPU feature %s not found", flagname);
}
static int cpu_sparc_find_by_name(sparc_def_t *cpu_def, const char *name)
{
unsigned int i;
const sparc_def_t *def = NULL;
for (i = 0; i < ARRAY_SIZE(sparc_defs); i++) {
if (strcasecmp(name, sparc_defs[i].name) == 0) {
def = &sparc_defs[i];
}
}
if (!def) {
return -1;
}
memcpy(cpu_def, def, sizeof(*def));
return 0;
}
static void sparc_cpu_parse_features(CPUState *cs, char *features,
Error **errp)
{
SPARCCPU *cpu = SPARC_CPU(cs);
sparc_def_t *cpu_def = cpu->env.def;
char *featurestr;
uint32_t plus_features = 0;
uint32_t minus_features = 0;
uint64_t iu_version;
uint32_t fpu_version, mmu_version, nwindows;
featurestr = features ? strtok(features, ",") : 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) {
error_setg(errp, "bad numerical value %s", val);
return;
}
cpu_def->iu_version = iu_version;
#ifdef DEBUG_FEATURES
fprintf(stderr, "iu_version %" PRIx64 "\n", iu_version);
#endif
} else if (!strcmp(featurestr, "fpu_version")) {
char *err;
fpu_version = strtol(val, &err, 0);
if (!*val || *err) {
error_setg(errp, "bad numerical value %s", val);
return;
}
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) {
error_setg(errp, "bad numerical value %s", val);
return;
}
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) {
error_setg(errp, "bad numerical value %s", val);
return;
}
cpu_def->nwindows = nwindows;
#ifdef DEBUG_FEATURES
fprintf(stderr, "nwindows %d\n", nwindows);
#endif
} else {
error_setg(errp, "unrecognized feature %s", featurestr);
return;
}
} else {
error_setg(errp, "feature string `%s' not in format "
"(+feature|-feature|feature=xyz)", featurestr);
return;
}
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
}
void sparc_cpu_list(FILE *f, fprintf_function cpu_fprintf)
{
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, 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 sparc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf,
int flags)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
int i, x;
cpu_fprintf(f, "pc: " TARGET_FMT_lx " npc: " TARGET_FMT_lx "\n", env->pc,
env->npc);
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");
}
}
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");
}
}
}
for (i = 0; i < TARGET_DPREGS; i++) {
if ((i & 3) == 0) {
cpu_fprintf(f, "%%f%02d: ", i * 2);
}
cpu_fprintf(f, " %016" PRIx64, env->fpr[i].ll);
if ((i & 3) == 3) {
cpu_fprintf(f, "\n");
}
}
#ifdef TARGET_SPARC64
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
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, "\n");
}
static void sparc_cpu_set_pc(CPUState *cs, vaddr value)
{
SPARCCPU *cpu = SPARC_CPU(cs);
cpu->env.pc = value;
cpu->env.npc = value + 4;
}
static void sparc_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb)
{
SPARCCPU *cpu = SPARC_CPU(cs);
cpu->env.pc = tb->pc;
cpu->env.npc = tb->cs_base;
}
static bool sparc_cpu_has_work(CPUState *cs)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
return (cs->interrupt_request & CPU_INTERRUPT_HARD) &&
cpu_interrupts_enabled(env);
}
static void sparc_cpu_realizefn(DeviceState *dev, Error **errp)
{
CPUState *cs = CPU(dev);
SPARCCPUClass *scc = SPARC_CPU_GET_CLASS(dev);
Error *local_err = NULL;
#if defined(CONFIG_USER_ONLY)
SPARCCPU *cpu = SPARC_CPU(dev);
CPUSPARCState *env = &cpu->env;
if ((env->def->features & CPU_FEATURE_FLOAT)) {
env->def->features |= CPU_FEATURE_FLOAT128;
}
#endif
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
error_propagate(errp, local_err);
return;
}
qemu_init_vcpu(cs);
scc->parent_realize(dev, errp);
}
static void sparc_cpu_initfn(Object *obj)
{
CPUState *cs = CPU(obj);
SPARCCPU *cpu = SPARC_CPU(obj);
CPUSPARCState *env = &cpu->env;
cs->env_ptr = env;
if (tcg_enabled()) {
gen_intermediate_code_init(env);
}
}
static void sparc_cpu_uninitfn(Object *obj)
{
SPARCCPU *cpu = SPARC_CPU(obj);
CPUSPARCState *env = &cpu->env;
g_free(env->def);
}
static void sparc_cpu_class_init(ObjectClass *oc, void *data)
{
SPARCCPUClass *scc = SPARC_CPU_CLASS(oc);
CPUClass *cc = CPU_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
scc->parent_realize = dc->realize;
dc->realize = sparc_cpu_realizefn;
scc->parent_reset = cc->reset;
cc->reset = sparc_cpu_reset;
cc->has_work = sparc_cpu_has_work;
cc->do_interrupt = sparc_cpu_do_interrupt;
cc->cpu_exec_interrupt = sparc_cpu_exec_interrupt;
cc->dump_state = sparc_cpu_dump_state;
#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
cc->memory_rw_debug = sparc_cpu_memory_rw_debug;
#endif
cc->set_pc = sparc_cpu_set_pc;
cc->synchronize_from_tb = sparc_cpu_synchronize_from_tb;
cc->gdb_read_register = sparc_cpu_gdb_read_register;
cc->gdb_write_register = sparc_cpu_gdb_write_register;
#ifdef CONFIG_USER_ONLY
cc->handle_mmu_fault = sparc_cpu_handle_mmu_fault;
#else
cc->do_unassigned_access = sparc_cpu_unassigned_access;
cc->do_unaligned_access = sparc_cpu_do_unaligned_access;
cc->get_phys_page_debug = sparc_cpu_get_phys_page_debug;
cc->vmsd = &vmstate_sparc_cpu;
#endif
cc->disas_set_info = cpu_sparc_disas_set_info;
#if defined(TARGET_SPARC64) && !defined(TARGET_ABI32)
cc->gdb_num_core_regs = 86;
#else
cc->gdb_num_core_regs = 72;
#endif
}
static const TypeInfo sparc_cpu_type_info = {
.name = TYPE_SPARC_CPU,
.parent = TYPE_CPU,
.instance_size = sizeof(SPARCCPU),
.instance_init = sparc_cpu_initfn,
.instance_finalize = sparc_cpu_uninitfn,
.abstract = false,
.class_size = sizeof(SPARCCPUClass),
.class_init = sparc_cpu_class_init,
};
static void sparc_cpu_register_types(void)
{
type_register_static(&sparc_cpu_type_info);
}
type_init(sparc_cpu_register_types)

779
target/sparc/cpu.h Normal file
View file

@ -0,0 +1,779 @@
#ifndef SPARC_CPU_H
#define SPARC_CPU_H
#include "qemu-common.h"
#include "qemu/bswap.h"
#include "cpu-qom.h"
#define ALIGNED_ONLY
#if !defined(TARGET_SPARC64)
#define TARGET_LONG_BITS 32
#define TARGET_DPREGS 16
#define TARGET_PAGE_BITS 12 /* 4k */
#define TARGET_PHYS_ADDR_SPACE_BITS 36
#define TARGET_VIRT_ADDR_SPACE_BITS 32
#else
#define TARGET_LONG_BITS 64
#define TARGET_DPREGS 32
#define TARGET_PAGE_BITS 13 /* 8k */
#define TARGET_PHYS_ADDR_SPACE_BITS 41
# ifdef TARGET_ABI32
# define TARGET_VIRT_ADDR_SPACE_BITS 32
# else
# define TARGET_VIRT_ADDR_SPACE_BITS 44
# endif
#endif
#define CPUArchState struct CPUSPARCState
#include "exec/cpu-defs.h"
#include "fpu/softfloat.h"
/*#define EXCP_INTERRUPT 0x100*/
/* trap definitions */
#ifndef TARGET_SPARC64
#define TT_TFAULT 0x01
#define TT_ILL_INSN 0x02
#define TT_PRIV_INSN 0x03
#define TT_NFPU_INSN 0x04
#define TT_WIN_OVF 0x05
#define TT_WIN_UNF 0x06
#define TT_UNALIGNED 0x07
#define TT_FP_EXCP 0x08
#define TT_DFAULT 0x09
#define TT_TOVF 0x0a
#define TT_EXTINT 0x10
#define TT_CODE_ACCESS 0x21
#define TT_UNIMP_FLUSH 0x25
#define TT_DATA_ACCESS 0x29
#define TT_DIV_ZERO 0x2a
#define TT_NCP_INSN 0x24
#define TT_TRAP 0x80
#else
#define TT_POWER_ON_RESET 0x01
#define TT_TFAULT 0x08
#define TT_CODE_ACCESS 0x0a
#define TT_ILL_INSN 0x10
#define TT_UNIMP_FLUSH TT_ILL_INSN
#define TT_PRIV_INSN 0x11
#define TT_NFPU_INSN 0x20
#define TT_FP_EXCP 0x21
#define TT_TOVF 0x23
#define TT_CLRWIN 0x24
#define TT_DIV_ZERO 0x28
#define TT_DFAULT 0x30
#define TT_DATA_ACCESS 0x32
#define TT_UNALIGNED 0x34
#define TT_PRIV_ACT 0x37
#define TT_EXTINT 0x40
#define TT_IVEC 0x60
#define TT_TMISS 0x64
#define TT_DMISS 0x68
#define TT_DPROT 0x6c
#define TT_SPILL 0x80
#define TT_FILL 0xc0
#define TT_WOTHER (1 << 5)
#define TT_TRAP 0x100
#endif
#define PSR_NEG_SHIFT 23
#define PSR_NEG (1 << PSR_NEG_SHIFT)
#define PSR_ZERO_SHIFT 22
#define PSR_ZERO (1 << PSR_ZERO_SHIFT)
#define PSR_OVF_SHIFT 21
#define PSR_OVF (1 << PSR_OVF_SHIFT)
#define PSR_CARRY_SHIFT 20
#define PSR_CARRY (1 << PSR_CARRY_SHIFT)
#define PSR_ICC (PSR_NEG|PSR_ZERO|PSR_OVF|PSR_CARRY)
#if !defined(TARGET_SPARC64)
#define PSR_EF (1<<12)
#define PSR_PIL 0xf00
#define PSR_S (1<<7)
#define PSR_PS (1<<6)
#define PSR_ET (1<<5)
#define PSR_CWP 0x1f
#endif
#define CC_SRC (env->cc_src)
#define CC_SRC2 (env->cc_src2)
#define CC_DST (env->cc_dst)
#define CC_OP (env->cc_op)
/* Even though lazy evaluation of CPU condition codes tends to be less
* important on RISC systems where condition codes are only updated
* when explicitly requested, SPARC uses it to update 32-bit and 64-bit
* condition codes.
*/
enum {
CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
CC_OP_FLAGS, /* all cc are back in status register */
CC_OP_DIV, /* modify N, Z and V, C = 0*/
CC_OP_ADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */
CC_OP_ADDX, /* modify all flags, CC_DST = res, CC_SRC = src1 */
CC_OP_TADD, /* modify all flags, CC_DST = res, CC_SRC = src1 */
CC_OP_TADDTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */
CC_OP_SUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
CC_OP_SUBX, /* modify all flags, CC_DST = res, CC_SRC = src1 */
CC_OP_TSUB, /* modify all flags, CC_DST = res, CC_SRC = src1 */
CC_OP_TSUBTV, /* modify all flags except V, CC_DST = res, CC_SRC = src1 */
CC_OP_LOGIC, /* modify N and Z, C = V = 0, CC_DST = res */
CC_OP_NB,
};
/* Trap base register */
#define TBR_BASE_MASK 0xfffff000
#if defined(TARGET_SPARC64)
#define PS_TCT (1<<12) /* UA2007, impl.dep. trap on control transfer */
#define PS_IG (1<<11) /* v9, zero on UA2007 */
#define PS_MG (1<<10) /* v9, zero on UA2007 */
#define PS_CLE (1<<9) /* UA2007 */
#define PS_TLE (1<<8) /* UA2007 */
#define PS_RMO (1<<7)
#define PS_RED (1<<5) /* v9, zero on UA2007 */
#define PS_PEF (1<<4) /* enable fpu */
#define PS_AM (1<<3) /* address mask */
#define PS_PRIV (1<<2)
#define PS_IE (1<<1)
#define PS_AG (1<<0) /* v9, zero on UA2007 */
#define FPRS_FEF (1<<2)
#define HS_PRIV (1<<2)
#endif
/* Fcc */
#define FSR_RD1 (1ULL << 31)
#define FSR_RD0 (1ULL << 30)
#define FSR_RD_MASK (FSR_RD1 | FSR_RD0)
#define FSR_RD_NEAREST 0
#define FSR_RD_ZERO FSR_RD0
#define FSR_RD_POS FSR_RD1
#define FSR_RD_NEG (FSR_RD1 | FSR_RD0)
#define FSR_NVM (1ULL << 27)
#define FSR_OFM (1ULL << 26)
#define FSR_UFM (1ULL << 25)
#define FSR_DZM (1ULL << 24)
#define FSR_NXM (1ULL << 23)
#define FSR_TEM_MASK (FSR_NVM | FSR_OFM | FSR_UFM | FSR_DZM | FSR_NXM)
#define FSR_NVA (1ULL << 9)
#define FSR_OFA (1ULL << 8)
#define FSR_UFA (1ULL << 7)
#define FSR_DZA (1ULL << 6)
#define FSR_NXA (1ULL << 5)
#define FSR_AEXC_MASK (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA)
#define FSR_NVC (1ULL << 4)
#define FSR_OFC (1ULL << 3)
#define FSR_UFC (1ULL << 2)
#define FSR_DZC (1ULL << 1)
#define FSR_NXC (1ULL << 0)
#define FSR_CEXC_MASK (FSR_NVC | FSR_OFC | FSR_UFC | FSR_DZC | FSR_NXC)
#define FSR_FTT2 (1ULL << 16)
#define FSR_FTT1 (1ULL << 15)
#define FSR_FTT0 (1ULL << 14)
//gcc warns about constant overflow for ~FSR_FTT_MASK
//#define FSR_FTT_MASK (FSR_FTT2 | FSR_FTT1 | FSR_FTT0)
#ifdef TARGET_SPARC64
#define FSR_FTT_NMASK 0xfffffffffffe3fffULL
#define FSR_FTT_CEXC_NMASK 0xfffffffffffe3fe0ULL
#define FSR_LDFSR_OLDMASK 0x0000003f000fc000ULL
#define FSR_LDXFSR_MASK 0x0000003fcfc00fffULL
#define FSR_LDXFSR_OLDMASK 0x00000000000fc000ULL
#else
#define FSR_FTT_NMASK 0xfffe3fffULL
#define FSR_FTT_CEXC_NMASK 0xfffe3fe0ULL
#define FSR_LDFSR_OLDMASK 0x000fc000ULL
#endif
#define FSR_LDFSR_MASK 0xcfc00fffULL
#define FSR_FTT_IEEE_EXCP (1ULL << 14)
#define FSR_FTT_UNIMPFPOP (3ULL << 14)
#define FSR_FTT_SEQ_ERROR (4ULL << 14)
#define FSR_FTT_INVAL_FPR (6ULL << 14)
#define FSR_FCC1_SHIFT 11
#define FSR_FCC1 (1ULL << FSR_FCC1_SHIFT)
#define FSR_FCC0_SHIFT 10
#define FSR_FCC0 (1ULL << FSR_FCC0_SHIFT)
/* MMU */
#define MMU_E (1<<0)
#define MMU_NF (1<<1)
#define PTE_ENTRYTYPE_MASK 3
#define PTE_ACCESS_MASK 0x1c
#define PTE_ACCESS_SHIFT 2
#define PTE_PPN_SHIFT 7
#define PTE_ADDR_MASK 0xffffff00
#define PG_ACCESSED_BIT 5
#define PG_MODIFIED_BIT 6
#define PG_CACHE_BIT 7
#define PG_ACCESSED_MASK (1 << PG_ACCESSED_BIT)
#define PG_MODIFIED_MASK (1 << PG_MODIFIED_BIT)
#define PG_CACHE_MASK (1 << PG_CACHE_BIT)
/* 3 <= NWINDOWS <= 32. */
#define MIN_NWINDOWS 3
#define MAX_NWINDOWS 32
#if !defined(TARGET_SPARC64)
#define NB_MMU_MODES 3
#else
#define NB_MMU_MODES 7
typedef struct trap_state {
uint64_t tpc;
uint64_t tnpc;
uint64_t tstate;
uint32_t tt;
} trap_state;
#endif
#define TARGET_INSN_START_EXTRA_WORDS 1
typedef struct sparc_def_t {
const char *name;
target_ulong iu_version;
uint32_t fpu_version;
uint32_t mmu_version;
uint32_t mmu_bm;
uint32_t mmu_ctpr_mask;
uint32_t mmu_cxr_mask;
uint32_t mmu_sfsr_mask;
uint32_t mmu_trcr_mask;
uint32_t mxcc_version;
uint32_t features;
uint32_t nwindows;
uint32_t maxtl;
} sparc_def_t;
#define CPU_FEATURE_FLOAT (1 << 0)
#define CPU_FEATURE_FLOAT128 (1 << 1)
#define CPU_FEATURE_SWAP (1 << 2)
#define CPU_FEATURE_MUL (1 << 3)
#define CPU_FEATURE_DIV (1 << 4)
#define CPU_FEATURE_FLUSH (1 << 5)
#define CPU_FEATURE_FSQRT (1 << 6)
#define CPU_FEATURE_FMUL (1 << 7)
#define CPU_FEATURE_VIS1 (1 << 8)
#define CPU_FEATURE_VIS2 (1 << 9)
#define CPU_FEATURE_FSMULD (1 << 10)
#define CPU_FEATURE_HYPV (1 << 11)
#define CPU_FEATURE_CMT (1 << 12)
#define CPU_FEATURE_GL (1 << 13)
#define CPU_FEATURE_TA0_SHUTDOWN (1 << 14) /* Shutdown on "ta 0x0" */
#define CPU_FEATURE_ASR17 (1 << 15)
#define CPU_FEATURE_CACHE_CTRL (1 << 16)
#define CPU_FEATURE_POWERDOWN (1 << 17)
#define CPU_FEATURE_CASA (1 << 18)
#ifndef TARGET_SPARC64
#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \
CPU_FEATURE_MUL | CPU_FEATURE_DIV | \
CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \
CPU_FEATURE_FMUL | CPU_FEATURE_FSMULD)
#else
#define CPU_DEFAULT_FEATURES (CPU_FEATURE_FLOAT | CPU_FEATURE_SWAP | \
CPU_FEATURE_MUL | CPU_FEATURE_DIV | \
CPU_FEATURE_FLUSH | CPU_FEATURE_FSQRT | \
CPU_FEATURE_FMUL | CPU_FEATURE_VIS1 | \
CPU_FEATURE_VIS2 | CPU_FEATURE_FSMULD | \
CPU_FEATURE_CASA)
enum {
mmu_us_12, // Ultrasparc < III (64 entry TLB)
mmu_us_3, // Ultrasparc III (512 entry TLB)
mmu_us_4, // Ultrasparc IV (several TLBs, 32 and 256MB pages)
mmu_sun4v, // T1, T2
};
#endif
#define TTE_VALID_BIT (1ULL << 63)
#define TTE_NFO_BIT (1ULL << 60)
#define TTE_USED_BIT (1ULL << 41)
#define TTE_LOCKED_BIT (1ULL << 6)
#define TTE_SIDEEFFECT_BIT (1ULL << 3)
#define TTE_PRIV_BIT (1ULL << 2)
#define TTE_W_OK_BIT (1ULL << 1)
#define TTE_GLOBAL_BIT (1ULL << 0)
#define TTE_IS_VALID(tte) ((tte) & TTE_VALID_BIT)
#define TTE_IS_NFO(tte) ((tte) & TTE_NFO_BIT)
#define TTE_IS_USED(tte) ((tte) & TTE_USED_BIT)
#define TTE_IS_LOCKED(tte) ((tte) & TTE_LOCKED_BIT)
#define TTE_IS_SIDEEFFECT(tte) ((tte) & TTE_SIDEEFFECT_BIT)
#define TTE_IS_PRIV(tte) ((tte) & TTE_PRIV_BIT)
#define TTE_IS_W_OK(tte) ((tte) & TTE_W_OK_BIT)
#define TTE_IS_GLOBAL(tte) ((tte) & TTE_GLOBAL_BIT)
#define TTE_SET_USED(tte) ((tte) |= TTE_USED_BIT)
#define TTE_SET_UNUSED(tte) ((tte) &= ~TTE_USED_BIT)
#define TTE_PGSIZE(tte) (((tte) >> 61) & 3ULL)
#define TTE_PA(tte) ((tte) & 0x1ffffffe000ULL)
#define SFSR_NF_BIT (1ULL << 24) /* JPS1 NoFault */
#define SFSR_TM_BIT (1ULL << 15) /* JPS1 TLB Miss */
#define SFSR_FT_VA_IMMU_BIT (1ULL << 13) /* USIIi VA out of range (IMMU) */
#define SFSR_FT_VA_DMMU_BIT (1ULL << 12) /* USIIi VA out of range (DMMU) */
#define SFSR_FT_NFO_BIT (1ULL << 11) /* NFO page access */
#define SFSR_FT_ILL_BIT (1ULL << 10) /* illegal LDA/STA ASI */
#define SFSR_FT_ATOMIC_BIT (1ULL << 9) /* atomic op on noncacheable area */
#define SFSR_FT_NF_E_BIT (1ULL << 8) /* NF access on side effect area */
#define SFSR_FT_PRIV_BIT (1ULL << 7) /* privilege violation */
#define SFSR_PR_BIT (1ULL << 3) /* privilege mode */
#define SFSR_WRITE_BIT (1ULL << 2) /* write access mode */
#define SFSR_OW_BIT (1ULL << 1) /* status overwritten */
#define SFSR_VALID_BIT (1ULL << 0) /* status valid */
#define SFSR_ASI_SHIFT 16 /* 23:16 ASI value */
#define SFSR_ASI_MASK (0xffULL << SFSR_ASI_SHIFT)
#define SFSR_CT_PRIMARY (0ULL << 4) /* 5:4 context type */
#define SFSR_CT_SECONDARY (1ULL << 4)
#define SFSR_CT_NUCLEUS (2ULL << 4)
#define SFSR_CT_NOTRANS (3ULL << 4)
#define SFSR_CT_MASK (3ULL << 4)
/* Leon3 cache control */
/* Cache control: emulate the behavior of cache control registers but without
any effect on the emulated */
#define CACHE_STATE_MASK 0x3
#define CACHE_DISABLED 0x0
#define CACHE_FROZEN 0x1
#define CACHE_ENABLED 0x3
/* Cache Control register fields */
#define CACHE_CTRL_IF (1 << 4) /* Instruction Cache Freeze on Interrupt */
#define CACHE_CTRL_DF (1 << 5) /* Data Cache Freeze on Interrupt */
#define CACHE_CTRL_DP (1 << 14) /* Data cache flush pending */
#define CACHE_CTRL_IP (1 << 15) /* Instruction cache flush pending */
#define CACHE_CTRL_IB (1 << 16) /* Instruction burst fetch */
#define CACHE_CTRL_FI (1 << 21) /* Flush Instruction cache (Write only) */
#define CACHE_CTRL_FD (1 << 22) /* Flush Data cache (Write only) */
#define CACHE_CTRL_DS (1 << 23) /* Data cache snoop enable */
typedef struct SparcTLBEntry {
uint64_t tag;
uint64_t tte;
} SparcTLBEntry;
struct CPUTimer
{
const char *name;
uint32_t frequency;
uint32_t disabled;
uint64_t disabled_mask;
uint32_t npt;
uint64_t npt_mask;
int64_t clock_offset;
QEMUTimer *qtimer;
};
typedef struct CPUTimer CPUTimer;
typedef struct CPUSPARCState CPUSPARCState;
struct CPUSPARCState {
target_ulong gregs[8]; /* general registers */
target_ulong *regwptr; /* pointer to current register window */
target_ulong pc; /* program counter */
target_ulong npc; /* next program counter */
target_ulong y; /* multiply/divide register */
/* emulator internal flags handling */
target_ulong cc_src, cc_src2;
target_ulong cc_dst;
uint32_t cc_op;
target_ulong cond; /* conditional branch result (XXX: save it in a
temporary register when possible) */
uint32_t psr; /* processor state register */
target_ulong fsr; /* FPU state register */
CPU_DoubleU fpr[TARGET_DPREGS]; /* floating point registers */
uint32_t cwp; /* index of current register window (extracted
from PSR) */
#if !defined(TARGET_SPARC64) || defined(TARGET_ABI32)
uint32_t wim; /* window invalid mask */
#endif
target_ulong tbr; /* trap base register */
#if !defined(TARGET_SPARC64)
int psrs; /* supervisor mode (extracted from PSR) */
int psrps; /* previous supervisor mode */
int psret; /* enable traps */
#endif
uint32_t psrpil; /* interrupt blocking level */
uint32_t pil_in; /* incoming interrupt level bitmap */
#if !defined(TARGET_SPARC64)
int psref; /* enable fpu */
#endif
int interrupt_index;
/* NOTE: we allow 8 more registers to handle wrapping */
target_ulong regbase[MAX_NWINDOWS * 16 + 8];
CPU_COMMON
/* Fields from here on are preserved across CPU reset. */
target_ulong version;
uint32_t nwindows;
/* MMU regs */
#if defined(TARGET_SPARC64)
uint64_t lsu;
#define DMMU_E 0x8
#define IMMU_E 0x4
//typedef struct SparcMMU
union {
uint64_t immuregs[16];
struct {
uint64_t tsb_tag_target;
uint64_t unused_mmu_primary_context; // use DMMU
uint64_t unused_mmu_secondary_context; // use DMMU
uint64_t sfsr;
uint64_t sfar;
uint64_t tsb;
uint64_t tag_access;
} immu;
};
union {
uint64_t dmmuregs[16];
struct {
uint64_t tsb_tag_target;
uint64_t mmu_primary_context;
uint64_t mmu_secondary_context;
uint64_t sfsr;
uint64_t sfar;
uint64_t tsb;
uint64_t tag_access;
} dmmu;
};
SparcTLBEntry itlb[64];
SparcTLBEntry dtlb[64];
uint32_t mmu_version;
#else
uint32_t mmuregs[32];
uint64_t mxccdata[4];
uint64_t mxccregs[8];
uint32_t mmubpctrv, mmubpctrc, mmubpctrs;
uint64_t mmubpaction;
uint64_t mmubpregs[4];
uint64_t prom_addr;
#endif
/* temporary float registers */
float128 qt0, qt1;
float_status fp_status;
#if defined(TARGET_SPARC64)
#define MAXTL_MAX 8
#define MAXTL_MASK (MAXTL_MAX - 1)
trap_state ts[MAXTL_MAX];
uint32_t xcc; /* Extended integer condition codes */
uint32_t asi;
uint32_t pstate;
uint32_t tl;
uint32_t maxtl;
uint32_t cansave, canrestore, otherwin, wstate, cleanwin;
uint64_t agregs[8]; /* alternate general registers */
uint64_t bgregs[8]; /* backup for normal global registers */
uint64_t igregs[8]; /* interrupt general registers */
uint64_t mgregs[8]; /* mmu general registers */
uint64_t fprs;
uint64_t tick_cmpr, stick_cmpr;
CPUTimer *tick, *stick;
#define TICK_NPT_MASK 0x8000000000000000ULL
#define TICK_INT_DIS 0x8000000000000000ULL
uint64_t gsr;
uint32_t gl; // UA2005
/* UA 2005 hyperprivileged registers */
uint64_t hpstate, htstate[MAXTL_MAX], hintp, htba, hver, hstick_cmpr, ssr;
CPUTimer *hstick; // UA 2005
/* Interrupt vector registers */
uint64_t ivec_status;
uint64_t ivec_data[3];
uint32_t softint;
#define SOFTINT_TIMER 1
#define SOFTINT_STIMER (1 << 16)
#define SOFTINT_INTRMASK (0xFFFE)
#define SOFTINT_REG_MASK (SOFTINT_STIMER|SOFTINT_INTRMASK|SOFTINT_TIMER)
#endif
sparc_def_t *def;
void *irq_manager;
void (*qemu_irq_ack)(CPUSPARCState *env, void *irq_manager, int intno);
/* Leon3 cache control */
uint32_t cache_control;
};
/**
* SPARCCPU:
* @env: #CPUSPARCState
*
* A SPARC CPU.
*/
struct SPARCCPU {
/*< private >*/
CPUState parent_obj;
/*< public >*/
CPUSPARCState env;
};
static inline SPARCCPU *sparc_env_get_cpu(CPUSPARCState *env)
{
return container_of(env, SPARCCPU, env);
}
#define ENV_GET_CPU(e) CPU(sparc_env_get_cpu(e))
#define ENV_OFFSET offsetof(SPARCCPU, env)
#ifndef CONFIG_USER_ONLY
extern const struct VMStateDescription vmstate_sparc_cpu;
#endif
void sparc_cpu_do_interrupt(CPUState *cpu);
void sparc_cpu_dump_state(CPUState *cpu, FILE *f,
fprintf_function cpu_fprintf, int flags);
hwaddr sparc_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr);
int sparc_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg);
int sparc_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg);
void QEMU_NORETURN sparc_cpu_do_unaligned_access(CPUState *cpu, vaddr addr,
MMUAccessType access_type,
int mmu_idx,
uintptr_t retaddr);
void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t) QEMU_NORETURN;
#ifndef NO_CPU_IO_DEFS
/* cpu_init.c */
SPARCCPU *cpu_sparc_init(const char *cpu_model);
void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu);
void sparc_cpu_list(FILE *f, fprintf_function cpu_fprintf);
/* mmu_helper.c */
int sparc_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw,
int mmu_idx);
target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev);
void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUSPARCState *env);
#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
int sparc_cpu_memory_rw_debug(CPUState *cpu, vaddr addr,
uint8_t *buf, int len, bool is_write);
#endif
/* translate.c */
void gen_intermediate_code_init(CPUSPARCState *env);
/* cpu-exec.c */
/* win_helper.c */
target_ulong cpu_get_psr(CPUSPARCState *env1);
void cpu_put_psr(CPUSPARCState *env1, target_ulong val);
void cpu_put_psr_raw(CPUSPARCState *env1, target_ulong val);
#ifdef TARGET_SPARC64
target_ulong cpu_get_ccr(CPUSPARCState *env1);
void cpu_put_ccr(CPUSPARCState *env1, target_ulong val);
target_ulong cpu_get_cwp64(CPUSPARCState *env1);
void cpu_put_cwp64(CPUSPARCState *env1, int cwp);
void cpu_change_pstate(CPUSPARCState *env1, uint32_t new_pstate);
#endif
int cpu_cwp_inc(CPUSPARCState *env1, int cwp);
int cpu_cwp_dec(CPUSPARCState *env1, int cwp);
void cpu_set_cwp(CPUSPARCState *env1, int new_cwp);
/* int_helper.c */
void leon3_irq_manager(CPUSPARCState *env, void *irq_manager, int intno);
/* sun4m.c, sun4u.c */
void cpu_check_irqs(CPUSPARCState *env);
/* leon3.c */
void leon3_irq_ack(void *irq_manager, int intno);
#if defined (TARGET_SPARC64)
static inline int compare_masked(uint64_t x, uint64_t y, uint64_t mask)
{
return (x & mask) == (y & mask);
}
#define MMU_CONTEXT_BITS 13
#define MMU_CONTEXT_MASK ((1 << MMU_CONTEXT_BITS) - 1)
static inline int tlb_compare_context(const SparcTLBEntry *tlb,
uint64_t context)
{
return compare_masked(context, tlb->tag, MMU_CONTEXT_MASK);
}
#endif
#endif
/* cpu-exec.c */
#if !defined(CONFIG_USER_ONLY)
void sparc_cpu_unassigned_access(CPUState *cpu, hwaddr addr,
bool is_write, bool is_exec, int is_asi,
unsigned size);
#if defined(TARGET_SPARC64)
hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr,
int mmu_idx);
#endif
#endif
int cpu_sparc_signal_handler(int host_signum, void *pinfo, void *puc);
#ifndef NO_CPU_IO_DEFS
#define cpu_init(cpu_model) CPU(cpu_sparc_init(cpu_model))
#endif
#define cpu_signal_handler cpu_sparc_signal_handler
#define cpu_list sparc_cpu_list
/* MMU modes definitions */
#if defined (TARGET_SPARC64)
#define MMU_USER_IDX 0
#define MMU_USER_SECONDARY_IDX 1
#define MMU_KERNEL_IDX 2
#define MMU_KERNEL_SECONDARY_IDX 3
#define MMU_NUCLEUS_IDX 4
#define MMU_HYPV_IDX 5
#define MMU_PHYS_IDX 6
#else
#define MMU_USER_IDX 0
#define MMU_KERNEL_IDX 1
#define MMU_PHYS_IDX 2
#endif
#if defined (TARGET_SPARC64)
static inline int cpu_has_hypervisor(CPUSPARCState *env1)
{
return env1->def->features & CPU_FEATURE_HYPV;
}
static inline int cpu_hypervisor_mode(CPUSPARCState *env1)
{
return cpu_has_hypervisor(env1) && (env1->hpstate & HS_PRIV);
}
static inline int cpu_supervisor_mode(CPUSPARCState *env1)
{
return env1->pstate & PS_PRIV;
}
#endif
static inline int cpu_mmu_index(CPUSPARCState *env, bool ifetch)
{
#if defined(CONFIG_USER_ONLY)
return MMU_USER_IDX;
#elif !defined(TARGET_SPARC64)
if ((env->mmuregs[0] & MMU_E) == 0) { /* MMU disabled */
return MMU_PHYS_IDX;
} else {
return env->psrs;
}
#else
/* IMMU or DMMU disabled. */
if (ifetch
? (env->lsu & IMMU_E) == 0 || (env->pstate & PS_RED) != 0
: (env->lsu & DMMU_E) == 0) {
return MMU_PHYS_IDX;
} else if (env->tl > 0) {
return MMU_NUCLEUS_IDX;
} else if (cpu_hypervisor_mode(env)) {
return MMU_HYPV_IDX;
} else if (cpu_supervisor_mode(env)) {
return MMU_KERNEL_IDX;
} else {
return MMU_USER_IDX;
}
#endif
}
static inline int cpu_interrupts_enabled(CPUSPARCState *env1)
{
#if !defined (TARGET_SPARC64)
if (env1->psret != 0)
return 1;
#else
if (env1->pstate & PS_IE)
return 1;
#endif
return 0;
}
static inline int cpu_pil_allowed(CPUSPARCState *env1, int pil)
{
#if !defined(TARGET_SPARC64)
/* level 15 is non-maskable on sparc v8 */
return pil == 15 || pil > env1->psrpil;
#else
return pil > env1->psrpil;
#endif
}
#include "exec/cpu-all.h"
#ifdef TARGET_SPARC64
/* sun4u.c */
void cpu_tick_set_count(CPUTimer *timer, uint64_t count);
uint64_t cpu_tick_get_count(CPUTimer *timer);
void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit);
trap_state* cpu_tsptr(CPUSPARCState* env);
#endif
#define TB_FLAG_MMU_MASK 7
#define TB_FLAG_FPU_ENABLED (1 << 4)
#define TB_FLAG_AM_ENABLED (1 << 5)
#define TB_FLAG_ASI_SHIFT 24
static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, target_ulong *pc,
target_ulong *cs_base, uint32_t *pflags)
{
uint32_t flags;
*pc = env->pc;
*cs_base = env->npc;
flags = cpu_mmu_index(env, false);
#ifdef TARGET_SPARC64
if (env->pstate & PS_AM) {
flags |= TB_FLAG_AM_ENABLED;
}
if ((env->def->features & CPU_FEATURE_FLOAT)
&& (env->pstate & PS_PEF)
&& (env->fprs & FPRS_FEF)) {
flags |= TB_FLAG_FPU_ENABLED;
}
flags |= env->asi << TB_FLAG_ASI_SHIFT;
#else
if ((env->def->features & CPU_FEATURE_FLOAT) && env->psref) {
flags |= TB_FLAG_FPU_ENABLED;
}
#endif
*pflags = flags;
}
static inline bool tb_fpu_enabled(int tb_flags)
{
#if defined(CONFIG_USER_ONLY)
return true;
#else
return tb_flags & TB_FLAG_FPU_ENABLED;
#endif
}
static inline bool tb_am_enabled(int tb_flags)
{
#ifndef TARGET_SPARC64
return false;
#else
return tb_flags & TB_FLAG_AM_ENABLED;
#endif
}
#endif

400
target/sparc/fop_helper.c Normal file
View file

@ -0,0 +1,400 @@
/*
* FPU op 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 "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#define QT0 (env->qt0)
#define QT1 (env->qt1)
static target_ulong do_check_ieee_exceptions(CPUSPARCState *env, uintptr_t ra)
{
target_ulong status = get_float_exception_flags(&env->fp_status);
target_ulong fsr = env->fsr;
if (unlikely(status)) {
/* Keep exception flags clear for next time. */
set_float_exception_flags(0, &env->fp_status);
/* Copy IEEE 754 flags into FSR */
if (status & float_flag_invalid) {
fsr |= FSR_NVC;
}
if (status & float_flag_overflow) {
fsr |= FSR_OFC;
}
if (status & float_flag_underflow) {
fsr |= FSR_UFC;
}
if (status & float_flag_divbyzero) {
fsr |= FSR_DZC;
}
if (status & float_flag_inexact) {
fsr |= FSR_NXC;
}
if ((fsr & FSR_CEXC_MASK) & ((fsr & FSR_TEM_MASK) >> 23)) {
CPUState *cs = CPU(sparc_env_get_cpu(env));
/* Unmasked exception, generate a trap. Note that while
the helper is marked as NO_WG, we can get away with
writing to cpu state along the exception path, since
TCG generated code will never see the write. */
env->fsr = fsr | FSR_FTT_IEEE_EXCP;
cs->exception_index = TT_FP_EXCP;
cpu_loop_exit_restore(cs, ra);
} else {
/* Accumulate exceptions */
fsr |= (fsr & FSR_CEXC_MASK) << 5;
}
}
return fsr;
}
target_ulong helper_check_ieee_exceptions(CPUSPARCState *env)
{
return do_check_ieee_exceptions(env, GETPC());
}
#define F_HELPER(name, p) void helper_f##name##p(CPUSPARCState *env)
#define F_BINOP(name) \
float32 helper_f ## name ## s (CPUSPARCState *env, float32 src1, \
float32 src2) \
{ \
return float32_ ## name (src1, src2, &env->fp_status); \
} \
float64 helper_f ## name ## d (CPUSPARCState * env, float64 src1,\
float64 src2) \
{ \
return float64_ ## name (src1, src2, &env->fp_status); \
} \
F_HELPER(name, q) \
{ \
QT0 = float128_ ## name (QT0, QT1, &env->fp_status); \
}
F_BINOP(add);
F_BINOP(sub);
F_BINOP(mul);
F_BINOP(div);
#undef F_BINOP
float64 helper_fsmuld(CPUSPARCState *env, float32 src1, float32 src2)
{
return float64_mul(float32_to_float64(src1, &env->fp_status),
float32_to_float64(src2, &env->fp_status),
&env->fp_status);
}
void helper_fdmulq(CPUSPARCState *env, float64 src1, float64 src2)
{
QT0 = float128_mul(float64_to_float128(src1, &env->fp_status),
float64_to_float128(src2, &env->fp_status),
&env->fp_status);
}
float32 helper_fnegs(float32 src)
{
return float32_chs(src);
}
#ifdef TARGET_SPARC64
float64 helper_fnegd(float64 src)
{
return float64_chs(src);
}
F_HELPER(neg, q)
{
QT0 = float128_chs(QT1);
}
#endif
/* Integer to float conversion. */
float32 helper_fitos(CPUSPARCState *env, int32_t src)
{
return int32_to_float32(src, &env->fp_status);
}
float64 helper_fitod(CPUSPARCState *env, int32_t src)
{
return int32_to_float64(src, &env->fp_status);
}
void helper_fitoq(CPUSPARCState *env, int32_t src)
{
QT0 = int32_to_float128(src, &env->fp_status);
}
#ifdef TARGET_SPARC64
float32 helper_fxtos(CPUSPARCState *env, int64_t src)
{
return int64_to_float32(src, &env->fp_status);
}
float64 helper_fxtod(CPUSPARCState *env, int64_t src)
{
return int64_to_float64(src, &env->fp_status);
}
void helper_fxtoq(CPUSPARCState *env, int64_t src)
{
QT0 = int64_to_float128(src, &env->fp_status);
}
#endif
#undef F_HELPER
/* floating point conversion */
float32 helper_fdtos(CPUSPARCState *env, float64 src)
{
return float64_to_float32(src, &env->fp_status);
}
float64 helper_fstod(CPUSPARCState *env, float32 src)
{
return float32_to_float64(src, &env->fp_status);
}
float32 helper_fqtos(CPUSPARCState *env)
{
return float128_to_float32(QT1, &env->fp_status);
}
void helper_fstoq(CPUSPARCState *env, float32 src)
{
QT0 = float32_to_float128(src, &env->fp_status);
}
float64 helper_fqtod(CPUSPARCState *env)
{
return float128_to_float64(QT1, &env->fp_status);
}
void helper_fdtoq(CPUSPARCState *env, float64 src)
{
QT0 = float64_to_float128(src, &env->fp_status);
}
/* Float to integer conversion. */
int32_t helper_fstoi(CPUSPARCState *env, float32 src)
{
return float32_to_int32_round_to_zero(src, &env->fp_status);
}
int32_t helper_fdtoi(CPUSPARCState *env, float64 src)
{
return float64_to_int32_round_to_zero(src, &env->fp_status);
}
int32_t helper_fqtoi(CPUSPARCState *env)
{
return float128_to_int32_round_to_zero(QT1, &env->fp_status);
}
#ifdef TARGET_SPARC64
int64_t helper_fstox(CPUSPARCState *env, float32 src)
{
return float32_to_int64_round_to_zero(src, &env->fp_status);
}
int64_t helper_fdtox(CPUSPARCState *env, float64 src)
{
return float64_to_int64_round_to_zero(src, &env->fp_status);
}
int64_t helper_fqtox(CPUSPARCState *env)
{
return float128_to_int64_round_to_zero(QT1, &env->fp_status);
}
#endif
float32 helper_fabss(float32 src)
{
return float32_abs(src);
}
#ifdef TARGET_SPARC64
float64 helper_fabsd(float64 src)
{
return float64_abs(src);
}
void helper_fabsq(CPUSPARCState *env)
{
QT0 = float128_abs(QT1);
}
#endif
float32 helper_fsqrts(CPUSPARCState *env, float32 src)
{
return float32_sqrt(src, &env->fp_status);
}
float64 helper_fsqrtd(CPUSPARCState *env, float64 src)
{
return float64_sqrt(src, &env->fp_status);
}
void helper_fsqrtq(CPUSPARCState *env)
{
QT0 = float128_sqrt(QT1, &env->fp_status);
}
#define GEN_FCMP(name, size, reg1, reg2, FS, E) \
target_ulong glue(helper_, name) (CPUSPARCState *env) \
{ \
int ret; \
target_ulong fsr; \
if (E) { \
ret = glue(size, _compare)(reg1, reg2, &env->fp_status); \
} else { \
ret = glue(size, _compare_quiet)(reg1, reg2, \
&env->fp_status); \
} \
fsr = do_check_ieee_exceptions(env, GETPC()); \
switch (ret) { \
case float_relation_unordered: \
fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \
fsr |= FSR_NVA; \
break; \
case float_relation_less: \
fsr &= ~(FSR_FCC1) << FS; \
fsr |= FSR_FCC0 << FS; \
break; \
case float_relation_greater: \
fsr &= ~(FSR_FCC0) << FS; \
fsr |= FSR_FCC1 << FS; \
break; \
default: \
fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
break; \
} \
return fsr; \
}
#define GEN_FCMP_T(name, size, FS, E) \
target_ulong glue(helper_, name)(CPUSPARCState *env, size src1, size src2)\
{ \
int ret; \
target_ulong fsr; \
if (E) { \
ret = glue(size, _compare)(src1, src2, &env->fp_status); \
} else { \
ret = glue(size, _compare_quiet)(src1, src2, \
&env->fp_status); \
} \
fsr = do_check_ieee_exceptions(env, GETPC()); \
switch (ret) { \
case float_relation_unordered: \
fsr |= (FSR_FCC1 | FSR_FCC0) << FS; \
break; \
case float_relation_less: \
fsr &= ~(FSR_FCC1 << FS); \
fsr |= FSR_FCC0 << FS; \
break; \
case float_relation_greater: \
fsr &= ~(FSR_FCC0 << FS); \
fsr |= FSR_FCC1 << FS; \
break; \
default: \
fsr &= ~((FSR_FCC1 | FSR_FCC0) << FS); \
break; \
} \
return fsr; \
}
GEN_FCMP_T(fcmps, float32, 0, 0);
GEN_FCMP_T(fcmpd, float64, 0, 0);
GEN_FCMP_T(fcmpes, float32, 0, 1);
GEN_FCMP_T(fcmped, float64, 0, 1);
GEN_FCMP(fcmpq, float128, QT0, QT1, 0, 0);
GEN_FCMP(fcmpeq, float128, QT0, QT1, 0, 1);
#ifdef TARGET_SPARC64
GEN_FCMP_T(fcmps_fcc1, float32, 22, 0);
GEN_FCMP_T(fcmpd_fcc1, float64, 22, 0);
GEN_FCMP(fcmpq_fcc1, float128, QT0, QT1, 22, 0);
GEN_FCMP_T(fcmps_fcc2, float32, 24, 0);
GEN_FCMP_T(fcmpd_fcc2, float64, 24, 0);
GEN_FCMP(fcmpq_fcc2, float128, QT0, QT1, 24, 0);
GEN_FCMP_T(fcmps_fcc3, float32, 26, 0);
GEN_FCMP_T(fcmpd_fcc3, float64, 26, 0);
GEN_FCMP(fcmpq_fcc3, float128, QT0, QT1, 26, 0);
GEN_FCMP_T(fcmpes_fcc1, float32, 22, 1);
GEN_FCMP_T(fcmped_fcc1, float64, 22, 1);
GEN_FCMP(fcmpeq_fcc1, float128, QT0, QT1, 22, 1);
GEN_FCMP_T(fcmpes_fcc2, float32, 24, 1);
GEN_FCMP_T(fcmped_fcc2, float64, 24, 1);
GEN_FCMP(fcmpeq_fcc2, float128, QT0, QT1, 24, 1);
GEN_FCMP_T(fcmpes_fcc3, float32, 26, 1);
GEN_FCMP_T(fcmped_fcc3, float64, 26, 1);
GEN_FCMP(fcmpeq_fcc3, float128, QT0, QT1, 26, 1);
#endif
#undef GEN_FCMP_T
#undef GEN_FCMP
static void set_fsr(CPUSPARCState *env, target_ulong fsr)
{
int rnd_mode;
switch (fsr & FSR_RD_MASK) {
case FSR_RD_NEAREST:
rnd_mode = float_round_nearest_even;
break;
default:
case FSR_RD_ZERO:
rnd_mode = float_round_to_zero;
break;
case FSR_RD_POS:
rnd_mode = float_round_up;
break;
case FSR_RD_NEG:
rnd_mode = float_round_down;
break;
}
set_float_rounding_mode(rnd_mode, &env->fp_status);
}
target_ulong helper_ldfsr(CPUSPARCState *env, target_ulong old_fsr,
uint32_t new_fsr)
{
old_fsr = (new_fsr & FSR_LDFSR_MASK) | (old_fsr & FSR_LDFSR_OLDMASK);
set_fsr(env, old_fsr);
return old_fsr;
}
#ifdef TARGET_SPARC64
target_ulong helper_ldxfsr(CPUSPARCState *env, target_ulong old_fsr,
uint64_t new_fsr)
{
old_fsr = (new_fsr & FSR_LDXFSR_MASK) | (old_fsr & FSR_LDXFSR_OLDMASK);
set_fsr(env, old_fsr);
return old_fsr;
}
#endif

209
target/sparc/gdbstub.c Normal file
View file

@ -0,0 +1,209 @@
/*
* SPARC gdb server stub
*
* Copyright (c) 2003-2005 Fabrice Bellard
* Copyright (c) 2013 SUSE LINUX Products GmbH
*
* 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 "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "exec/gdbstub.h"
#ifdef TARGET_ABI32
#define gdb_get_rega(buf, val) gdb_get_reg32(buf, val)
#else
#define gdb_get_rega(buf, val) gdb_get_regl(buf, val)
#endif
int sparc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
if (n < 8) {
/* g0..g7 */
return gdb_get_rega(mem_buf, env->gregs[n]);
}
if (n < 32) {
/* register window */
return gdb_get_rega(mem_buf, env->regwptr[n - 8]);
}
#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64)
if (n < 64) {
/* fprs */
if (n & 1) {
return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.lower);
} else {
return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.upper);
}
}
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
switch (n) {
case 64:
return gdb_get_rega(mem_buf, env->y);
case 65:
return gdb_get_rega(mem_buf, cpu_get_psr(env));
case 66:
return gdb_get_rega(mem_buf, env->wim);
case 67:
return gdb_get_rega(mem_buf, env->tbr);
case 68:
return gdb_get_rega(mem_buf, env->pc);
case 69:
return gdb_get_rega(mem_buf, env->npc);
case 70:
return gdb_get_rega(mem_buf, env->fsr);
case 71:
return gdb_get_rega(mem_buf, 0); /* csr */
default:
return gdb_get_rega(mem_buf, 0);
}
#else
if (n < 64) {
/* f0-f31 */
if (n & 1) {
return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.lower);
} else {
return gdb_get_reg32(mem_buf, env->fpr[(n - 32) / 2].l.upper);
}
}
if (n < 80) {
/* f32-f62 (double width, even numbers only) */
return gdb_get_reg64(mem_buf, env->fpr[(n - 32) / 2].ll);
}
switch (n) {
case 80:
return gdb_get_regl(mem_buf, env->pc);
case 81:
return gdb_get_regl(mem_buf, env->npc);
case 82:
return gdb_get_regl(mem_buf, (cpu_get_ccr(env) << 32) |
((env->asi & 0xff) << 24) |
((env->pstate & 0xfff) << 8) |
cpu_get_cwp64(env));
case 83:
return gdb_get_regl(mem_buf, env->fsr);
case 84:
return gdb_get_regl(mem_buf, env->fprs);
case 85:
return gdb_get_regl(mem_buf, env->y);
}
#endif
return 0;
}
int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
#if defined(TARGET_ABI32)
abi_ulong tmp;
tmp = ldl_p(mem_buf);
#else
target_ulong tmp;
tmp = ldtul_p(mem_buf);
#endif
if (n < 8) {
/* g0..g7 */
env->gregs[n] = tmp;
} else if (n < 32) {
/* register window */
env->regwptr[n - 8] = tmp;
}
#if defined(TARGET_ABI32) || !defined(TARGET_SPARC64)
else if (n < 64) {
/* fprs */
/* f0-f31 */
if (n & 1) {
env->fpr[(n - 32) / 2].l.lower = tmp;
} else {
env->fpr[(n - 32) / 2].l.upper = tmp;
}
} else {
/* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */
switch (n) {
case 64:
env->y = tmp;
break;
case 65:
cpu_put_psr(env, tmp);
break;
case 66:
env->wim = tmp;
break;
case 67:
env->tbr = tmp;
break;
case 68:
env->pc = tmp;
break;
case 69:
env->npc = tmp;
break;
case 70:
env->fsr = tmp;
break;
default:
return 0;
}
}
return 4;
#else
else if (n < 64) {
/* f0-f31 */
tmp = ldl_p(mem_buf);
if (n & 1) {
env->fpr[(n - 32) / 2].l.lower = tmp;
} else {
env->fpr[(n - 32) / 2].l.upper = tmp;
}
return 4;
} else if (n < 80) {
/* f32-f62 (double width, even numbers only) */
env->fpr[(n - 32) / 2].ll = tmp;
} else {
switch (n) {
case 80:
env->pc = tmp;
break;
case 81:
env->npc = tmp;
break;
case 82:
cpu_put_ccr(env, tmp >> 32);
env->asi = (tmp >> 24) & 0xff;
env->pstate = (tmp >> 8) & 0xfff;
cpu_put_cwp64(env, tmp & 0xff);
break;
case 83:
env->fsr = tmp;
break;
case 84:
env->fprs = tmp;
break;
case 85:
env->y = tmp;
break;
default:
return 0;
}
}
return 8;
#endif
}

257
target/sparc/helper.c Normal file
View file

@ -0,0 +1,257 @@
/*
* Misc 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 "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
#include "sysemu/sysemu.h"
void cpu_raise_exception_ra(CPUSPARCState *env, int tt, uintptr_t ra)
{
CPUState *cs = CPU(sparc_env_get_cpu(env));
cs->exception_index = tt;
cpu_loop_exit_restore(cs, ra);
}
void helper_raise_exception(CPUSPARCState *env, int tt)
{
CPUState *cs = CPU(sparc_env_get_cpu(env));
cs->exception_index = tt;
cpu_loop_exit(cs);
}
void helper_debug(CPUSPARCState *env)
{
CPUState *cs = CPU(sparc_env_get_cpu(env));
cs->exception_index = EXCP_DEBUG;
cpu_loop_exit(cs);
}
#ifdef TARGET_SPARC64
target_ulong helper_popc(target_ulong val)
{
return ctpop64(val);
}
void helper_tick_set_count(void *opaque, uint64_t count)
{
#if !defined(CONFIG_USER_ONLY)
cpu_tick_set_count(opaque, count);
#endif
}
uint64_t helper_tick_get_count(CPUSPARCState *env, void *opaque, int mem_idx)
{
#if !defined(CONFIG_USER_ONLY)
CPUTimer *timer = opaque;
if (timer->npt && mem_idx < MMU_KERNEL_IDX) {
cpu_raise_exception_ra(env, TT_PRIV_INSN, GETPC());
}
return cpu_tick_get_count(timer);
#else
return 0;
#endif
}
void helper_tick_set_limit(void *opaque, uint64_t limit)
{
#if !defined(CONFIG_USER_ONLY)
cpu_tick_set_limit(opaque, limit);
#endif
}
#endif
static target_ulong do_udiv(CPUSPARCState *env, target_ulong a,
target_ulong b, int cc, uintptr_t ra)
{
int overflow = 0;
uint64_t x0;
uint32_t x1;
x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32);
x1 = (b & 0xffffffff);
if (x1 == 0) {
cpu_raise_exception_ra(env, TT_DIV_ZERO, ra);
}
x0 = x0 / x1;
if (x0 > UINT32_MAX) {
x0 = UINT32_MAX;
overflow = 1;
}
if (cc) {
env->cc_dst = x0;
env->cc_src2 = overflow;
env->cc_op = CC_OP_DIV;
}
return x0;
}
target_ulong helper_udiv(CPUSPARCState *env, target_ulong a, target_ulong b)
{
return do_udiv(env, a, b, 0, GETPC());
}
target_ulong helper_udiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b)
{
return do_udiv(env, a, b, 1, GETPC());
}
static target_ulong do_sdiv(CPUSPARCState *env, target_ulong a,
target_ulong b, int cc, uintptr_t ra)
{
int overflow = 0;
int64_t x0;
int32_t x1;
x0 = (a & 0xffffffff) | ((int64_t) (env->y) << 32);
x1 = (b & 0xffffffff);
if (x1 == 0) {
cpu_raise_exception_ra(env, TT_DIV_ZERO, ra);
} else if (x1 == -1 && x0 == INT64_MIN) {
x0 = INT32_MAX;
overflow = 1;
} else {
x0 = x0 / x1;
if ((int32_t) x0 != x0) {
x0 = x0 < 0 ? INT32_MIN : INT32_MAX;
overflow = 1;
}
}
if (cc) {
env->cc_dst = x0;
env->cc_src2 = overflow;
env->cc_op = CC_OP_DIV;
}
return x0;
}
target_ulong helper_sdiv(CPUSPARCState *env, target_ulong a, target_ulong b)
{
return do_sdiv(env, a, b, 0, GETPC());
}
target_ulong helper_sdiv_cc(CPUSPARCState *env, target_ulong a, target_ulong b)
{
return do_sdiv(env, a, b, 1, GETPC());
}
#ifdef TARGET_SPARC64
int64_t helper_sdivx(CPUSPARCState *env, int64_t a, int64_t b)
{
if (b == 0) {
/* Raise divide by zero trap. */
cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC());
} else if (b == -1) {
/* Avoid overflow trap with i386 divide insn. */
return -a;
} else {
return a / b;
}
}
uint64_t helper_udivx(CPUSPARCState *env, uint64_t a, uint64_t b)
{
if (b == 0) {
/* Raise divide by zero trap. */
cpu_raise_exception_ra(env, TT_DIV_ZERO, GETPC());
}
return a / b;
}
#endif
target_ulong helper_taddcctv(CPUSPARCState *env, target_ulong src1,
target_ulong src2)
{
target_ulong dst;
/* Tag overflow occurs if either input has bits 0 or 1 set. */
if ((src1 | src2) & 3) {
goto tag_overflow;
}
dst = src1 + src2;
/* Tag overflow occurs if the addition overflows. */
if (~(src1 ^ src2) & (src1 ^ dst) & (1u << 31)) {
goto tag_overflow;
}
/* Only modify the CC after any exceptions have been generated. */
env->cc_op = CC_OP_TADDTV;
env->cc_src = src1;
env->cc_src2 = src2;
env->cc_dst = dst;
return dst;
tag_overflow:
cpu_raise_exception_ra(env, TT_TOVF, GETPC());
}
target_ulong helper_tsubcctv(CPUSPARCState *env, target_ulong src1,
target_ulong src2)
{
target_ulong dst;
/* Tag overflow occurs if either input has bits 0 or 1 set. */
if ((src1 | src2) & 3) {
goto tag_overflow;
}
dst = src1 - src2;
/* Tag overflow occurs if the subtraction overflows. */
if ((src1 ^ src2) & (src1 ^ dst) & (1u << 31)) {
goto tag_overflow;
}
/* Only modify the CC after any exceptions have been generated. */
env->cc_op = CC_OP_TSUBTV;
env->cc_src = src1;
env->cc_src2 = src2;
env->cc_dst = dst;
return dst;
tag_overflow:
cpu_raise_exception_ra(env, TT_TOVF, GETPC());
}
#ifndef TARGET_SPARC64
void helper_power_down(CPUSPARCState *env)
{
CPUState *cs = CPU(sparc_env_get_cpu(env));
cs->halted = 1;
cs->exception_index = EXCP_HLT;
env->pc = env->npc;
env->npc = env->pc + 4;
cpu_loop_exit(cs);
}
#endif

168
target/sparc/helper.h Normal file
View file

@ -0,0 +1,168 @@
#ifndef TARGET_SPARC64
DEF_HELPER_1(rett, void, env)
DEF_HELPER_2(wrpsr, void, env, tl)
DEF_HELPER_1(rdpsr, tl, env)
DEF_HELPER_1(power_down, void, env)
#else
DEF_HELPER_FLAGS_2(wrpil, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_2(wrpstate, void, env, tl)
DEF_HELPER_1(done, void, env)
DEF_HELPER_1(retry, void, env)
DEF_HELPER_FLAGS_1(flushw, TCG_CALL_NO_WG, void, env)
DEF_HELPER_FLAGS_1(saved, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_1(restored, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_1(rdccr, tl, env)
DEF_HELPER_2(wrccr, void, env, tl)
DEF_HELPER_1(rdcwp, tl, env)
DEF_HELPER_2(wrcwp, void, env, tl)
DEF_HELPER_FLAGS_2(array8, TCG_CALL_NO_RWG_SE, tl, tl, tl)
DEF_HELPER_FLAGS_1(popc, TCG_CALL_NO_RWG_SE, tl, tl)
DEF_HELPER_FLAGS_2(set_softint, TCG_CALL_NO_RWG, void, env, i64)
DEF_HELPER_FLAGS_2(clear_softint, TCG_CALL_NO_RWG, void, env, i64)
DEF_HELPER_FLAGS_2(write_softint, TCG_CALL_NO_RWG, void, env, i64)
DEF_HELPER_FLAGS_2(tick_set_count, TCG_CALL_NO_RWG, void, ptr, i64)
DEF_HELPER_FLAGS_3(tick_get_count, TCG_CALL_NO_WG, i64, env, ptr, int)
DEF_HELPER_FLAGS_2(tick_set_limit, TCG_CALL_NO_RWG, void, ptr, i64)
#endif
DEF_HELPER_FLAGS_3(check_align, TCG_CALL_NO_WG, void, env, tl, i32)
DEF_HELPER_1(debug, void, env)
DEF_HELPER_1(save, void, env)
DEF_HELPER_1(restore, void, env)
DEF_HELPER_3(udiv, tl, env, tl, tl)
DEF_HELPER_3(udiv_cc, tl, env, tl, tl)
DEF_HELPER_3(sdiv, tl, env, tl, tl)
DEF_HELPER_3(sdiv_cc, tl, env, tl, tl)
DEF_HELPER_3(taddcctv, tl, env, tl, tl)
DEF_HELPER_3(tsubcctv, tl, env, tl, tl)
#ifdef TARGET_SPARC64
DEF_HELPER_FLAGS_3(sdivx, TCG_CALL_NO_WG, s64, env, s64, s64)
DEF_HELPER_FLAGS_3(udivx, TCG_CALL_NO_WG, i64, env, i64, i64)
#endif
#if !defined(CONFIG_USER_ONLY) || defined(TARGET_SPARC64)
DEF_HELPER_FLAGS_4(ld_asi, TCG_CALL_NO_WG, i64, env, tl, int, i32)
DEF_HELPER_FLAGS_5(st_asi, TCG_CALL_NO_WG, void, env, tl, i64, int, i32)
#endif
DEF_HELPER_FLAGS_1(check_ieee_exceptions, TCG_CALL_NO_WG, tl, env)
DEF_HELPER_FLAGS_3(ldfsr, TCG_CALL_NO_RWG, tl, env, tl, i32)
DEF_HELPER_FLAGS_1(fabss, TCG_CALL_NO_RWG_SE, f32, f32)
DEF_HELPER_FLAGS_2(fsqrts, TCG_CALL_NO_RWG, f32, env, f32)
DEF_HELPER_FLAGS_2(fsqrtd, TCG_CALL_NO_RWG, f64, env, f64)
DEF_HELPER_FLAGS_3(fcmps, TCG_CALL_NO_WG, tl, env, f32, f32)
DEF_HELPER_FLAGS_3(fcmpd, TCG_CALL_NO_WG, tl, env, f64, f64)
DEF_HELPER_FLAGS_3(fcmpes, TCG_CALL_NO_WG, tl, env, f32, f32)
DEF_HELPER_FLAGS_3(fcmped, TCG_CALL_NO_WG, tl, env, f64, f64)
DEF_HELPER_FLAGS_1(fsqrtq, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_1(fcmpq, TCG_CALL_NO_WG, tl, env)
DEF_HELPER_FLAGS_1(fcmpeq, TCG_CALL_NO_WG, tl, env)
#ifdef TARGET_SPARC64
DEF_HELPER_FLAGS_3(ldxfsr, TCG_CALL_NO_RWG, tl, env, tl, i64)
DEF_HELPER_FLAGS_1(fabsd, TCG_CALL_NO_RWG_SE, f64, f64)
DEF_HELPER_FLAGS_3(fcmps_fcc1, TCG_CALL_NO_WG, tl, env, f32, f32)
DEF_HELPER_FLAGS_3(fcmps_fcc2, TCG_CALL_NO_WG, tl, env, f32, f32)
DEF_HELPER_FLAGS_3(fcmps_fcc3, TCG_CALL_NO_WG, tl, env, f32, f32)
DEF_HELPER_FLAGS_3(fcmpd_fcc1, TCG_CALL_NO_WG, tl, env, f64, f64)
DEF_HELPER_FLAGS_3(fcmpd_fcc2, TCG_CALL_NO_WG, tl, env, f64, f64)
DEF_HELPER_FLAGS_3(fcmpd_fcc3, TCG_CALL_NO_WG, tl, env, f64, f64)
DEF_HELPER_FLAGS_3(fcmpes_fcc1, TCG_CALL_NO_WG, tl, env, f32, f32)
DEF_HELPER_FLAGS_3(fcmpes_fcc2, TCG_CALL_NO_WG, tl, env, f32, f32)
DEF_HELPER_FLAGS_3(fcmpes_fcc3, TCG_CALL_NO_WG, tl, env, f32, f32)
DEF_HELPER_FLAGS_3(fcmped_fcc1, TCG_CALL_NO_WG, tl, env, f64, f64)
DEF_HELPER_FLAGS_3(fcmped_fcc2, TCG_CALL_NO_WG, tl, env, f64, f64)
DEF_HELPER_FLAGS_3(fcmped_fcc3, TCG_CALL_NO_WG, tl, env, f64, f64)
DEF_HELPER_FLAGS_1(fabsq, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_1(fcmpq_fcc1, TCG_CALL_NO_WG, tl, env)
DEF_HELPER_FLAGS_1(fcmpq_fcc2, TCG_CALL_NO_WG, tl, env)
DEF_HELPER_FLAGS_1(fcmpq_fcc3, TCG_CALL_NO_WG, tl, env)
DEF_HELPER_FLAGS_1(fcmpeq_fcc1, TCG_CALL_NO_WG, tl, env)
DEF_HELPER_FLAGS_1(fcmpeq_fcc2, TCG_CALL_NO_WG, tl, env)
DEF_HELPER_FLAGS_1(fcmpeq_fcc3, TCG_CALL_NO_WG, tl, env)
#endif
DEF_HELPER_2(raise_exception, noreturn, env, int)
#define F_HELPER_0_1(name) \
DEF_HELPER_FLAGS_1(f ## name, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_3(faddd, TCG_CALL_NO_RWG, f64, env, f64, f64)
DEF_HELPER_FLAGS_3(fsubd, TCG_CALL_NO_RWG, f64, env, f64, f64)
DEF_HELPER_FLAGS_3(fmuld, TCG_CALL_NO_RWG, f64, env, f64, f64)
DEF_HELPER_FLAGS_3(fdivd, TCG_CALL_NO_RWG, f64, env, f64, f64)
F_HELPER_0_1(addq)
F_HELPER_0_1(subq)
F_HELPER_0_1(mulq)
F_HELPER_0_1(divq)
DEF_HELPER_FLAGS_3(fadds, TCG_CALL_NO_RWG, f32, env, f32, f32)
DEF_HELPER_FLAGS_3(fsubs, TCG_CALL_NO_RWG, f32, env, f32, f32)
DEF_HELPER_FLAGS_3(fmuls, TCG_CALL_NO_RWG, f32, env, f32, f32)
DEF_HELPER_FLAGS_3(fdivs, TCG_CALL_NO_RWG, f32, env, f32, f32)
DEF_HELPER_FLAGS_3(fsmuld, TCG_CALL_NO_RWG, f64, env, f32, f32)
DEF_HELPER_FLAGS_3(fdmulq, TCG_CALL_NO_RWG, void, env, f64, f64)
DEF_HELPER_FLAGS_1(fnegs, TCG_CALL_NO_RWG_SE, f32, f32)
DEF_HELPER_FLAGS_2(fitod, TCG_CALL_NO_RWG_SE, f64, env, s32)
DEF_HELPER_FLAGS_2(fitoq, TCG_CALL_NO_RWG, void, env, s32)
DEF_HELPER_FLAGS_2(fitos, TCG_CALL_NO_RWG, f32, env, s32)
#ifdef TARGET_SPARC64
DEF_HELPER_FLAGS_1(fnegd, TCG_CALL_NO_RWG_SE, f64, f64)
DEF_HELPER_FLAGS_1(fnegq, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_2(fxtos, TCG_CALL_NO_RWG, f32, env, s64)
DEF_HELPER_FLAGS_2(fxtod, TCG_CALL_NO_RWG, f64, env, s64)
DEF_HELPER_FLAGS_2(fxtoq, TCG_CALL_NO_RWG, void, env, s64)
#endif
DEF_HELPER_FLAGS_2(fdtos, TCG_CALL_NO_RWG, f32, env, f64)
DEF_HELPER_FLAGS_2(fstod, TCG_CALL_NO_RWG, f64, env, f32)
DEF_HELPER_FLAGS_1(fqtos, TCG_CALL_NO_RWG, f32, env)
DEF_HELPER_FLAGS_2(fstoq, TCG_CALL_NO_RWG, void, env, f32)
DEF_HELPER_FLAGS_1(fqtod, TCG_CALL_NO_RWG, f64, env)
DEF_HELPER_FLAGS_2(fdtoq, TCG_CALL_NO_RWG, void, env, f64)
DEF_HELPER_FLAGS_2(fstoi, TCG_CALL_NO_RWG, s32, env, f32)
DEF_HELPER_FLAGS_2(fdtoi, TCG_CALL_NO_RWG, s32, env, f64)
DEF_HELPER_FLAGS_1(fqtoi, TCG_CALL_NO_RWG, s32, env)
#ifdef TARGET_SPARC64
DEF_HELPER_FLAGS_2(fstox, TCG_CALL_NO_RWG, s64, env, f32)
DEF_HELPER_FLAGS_2(fdtox, TCG_CALL_NO_RWG, s64, env, f64)
DEF_HELPER_FLAGS_1(fqtox, TCG_CALL_NO_RWG, s64, env)
DEF_HELPER_FLAGS_2(fpmerge, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(fmul8x16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(fmul8x16al, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(fmul8x16au, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(fmul8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(fmul8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(fmuld8sux16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(fmuld8ulx16, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_2(fexpand, TCG_CALL_NO_RWG_SE, i64, i64, i64)
DEF_HELPER_FLAGS_3(pdist, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64)
DEF_HELPER_FLAGS_2(fpack16, TCG_CALL_NO_RWG_SE, i32, i64, i64)
DEF_HELPER_FLAGS_3(fpack32, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64)
DEF_HELPER_FLAGS_2(fpackfix, TCG_CALL_NO_RWG_SE, i32, i64, i64)
DEF_HELPER_FLAGS_3(bshuffle, TCG_CALL_NO_RWG_SE, i64, i64, i64, i64)
#define VIS_HELPER(name) \
DEF_HELPER_FLAGS_2(f ## name ## 16, TCG_CALL_NO_RWG_SE, \
i64, i64, i64) \
DEF_HELPER_FLAGS_2(f ## name ## 16s, TCG_CALL_NO_RWG_SE, \
i32, i32, i32) \
DEF_HELPER_FLAGS_2(f ## name ## 32, TCG_CALL_NO_RWG_SE, \
i64, i64, i64) \
DEF_HELPER_FLAGS_2(f ## name ## 32s, TCG_CALL_NO_RWG_SE, \
i32, i32, i32)
VIS_HELPER(padd)
VIS_HELPER(psub)
#define VIS_CMPHELPER(name) \
DEF_HELPER_FLAGS_2(f##name##16, TCG_CALL_NO_RWG_SE, \
i64, i64, i64) \
DEF_HELPER_FLAGS_2(f##name##32, TCG_CALL_NO_RWG_SE, \
i64, i64, i64)
VIS_CMPHELPER(cmpgt)
VIS_CMPHELPER(cmpeq)
VIS_CMPHELPER(cmple)
VIS_CMPHELPER(cmpne)
#endif
#undef F_HELPER_0_1
#undef VIS_HELPER
#undef VIS_CMPHELPER
DEF_HELPER_1(compute_psr, void, env)
DEF_HELPER_FLAGS_1(compute_C_icc, TCG_CALL_NO_WG_SE, i32, env)

175
target/sparc/int32_helper.c Normal file
View file

@ -0,0 +1,175 @@
/*
* Sparc32 interrupt 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 "qemu/osdep.h"
#include "cpu.h"
#include "trace.h"
#include "sysemu/sysemu.h"
#include "exec/log.h"
#define DEBUG_PCALL
#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 sparc_cpu_do_interrupt(CPUState *cs)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
int cwp, intno = cs->exception_index;
/* Compute PSR before exposing state. */
if (env->cc_op != CC_OP_FLAGS) {
cpu_get_psr(env);
}
#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)\n", count, name, intno);
log_cpu_state(cs, 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) {
if (cs->exception_index == 0x80 &&
env->def->features & CPU_FEATURE_TA0_SHUTDOWN) {
qemu_system_shutdown_request();
} else {
cpu_abort(cs, "Trap 0x%02x while interrupts disabled, Error state",
cs->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;
cs->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, env->irq_manager, intno);
}
#endif
}
#if !defined(CONFIG_USER_ONLY)
static void leon3_cache_control_int(CPUSPARCState *env)
{
uint32_t state = 0;
if (env->cache_control & CACHE_CTRL_IF) {
/* Instruction cache state */
state = env->cache_control & CACHE_STATE_MASK;
if (state == CACHE_ENABLED) {
state = CACHE_FROZEN;
trace_int_helper_icache_freeze();
}
env->cache_control &= ~CACHE_STATE_MASK;
env->cache_control |= state;
}
if (env->cache_control & CACHE_CTRL_DF) {
/* Data cache state */
state = (env->cache_control >> 2) & CACHE_STATE_MASK;
if (state == CACHE_ENABLED) {
state = CACHE_FROZEN;
trace_int_helper_dcache_freeze();
}
env->cache_control &= ~(CACHE_STATE_MASK << 2);
env->cache_control |= (state << 2);
}
}
void leon3_irq_manager(CPUSPARCState *env, void *irq_manager, int intno)
{
leon3_irq_ack(irq_manager, intno);
leon3_cache_control_int(env);
}
#endif

205
target/sparc/int64_helper.c Normal file
View file

@ -0,0 +1,205 @@
/*
* Sparc64 interrupt 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 "qemu/osdep.h"
#include "cpu.h"
#include "exec/helper-proto.h"
#include "exec/log.h"
#include "trace.h"
#define DEBUG_PCALL
#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 sparc_cpu_do_interrupt(CPUState *cs)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
int intno = cs->exception_index;
trap_state *tsptr;
/* Compute PSR before exposing state. */
if (env->cc_op != CC_OP_FLAGS) {
cpu_get_psr(env);
}
#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)\n", count, name, intno);
log_cpu_state(cs, 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(cs, "Trap 0x%04x while trap level (%d) >= MAXTL (%d),"
" Error state", cs->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->pc = env->tbr & ~0x7fffULL;
env->pc |= ((env->tl > 1) ? 1 << 14 : 0) | (intno << 5);
env->npc = env->pc + 4;
cs->exception_index = -1;
}
trap_state *cpu_tsptr(CPUSPARCState* env)
{
return &env->ts[env->tl & MAXTL_MASK];
}
static bool do_modify_softint(CPUSPARCState *env, uint32_t value)
{
if (env->softint != value) {
env->softint = value;
#if !defined(CONFIG_USER_ONLY)
if (cpu_interrupts_enabled(env)) {
cpu_check_irqs(env);
}
#endif
return true;
}
return false;
}
void helper_set_softint(CPUSPARCState *env, uint64_t value)
{
if (do_modify_softint(env, env->softint | (uint32_t)value)) {
trace_int_helper_set_softint(env->softint);
}
}
void helper_clear_softint(CPUSPARCState *env, uint64_t value)
{
if (do_modify_softint(env, env->softint & (uint32_t)~value)) {
trace_int_helper_clear_softint(env->softint);
}
}
void helper_write_softint(CPUSPARCState *env, uint64_t value)
{
if (do_modify_softint(env, (uint32_t)value)) {
trace_int_helper_write_softint(env->softint);
}
}

1709
target/sparc/ldst_helper.c Normal file

File diff suppressed because it is too large Load diff

194
target/sparc/machine.c Normal file
View file

@ -0,0 +1,194 @@
#include "qemu/osdep.h"
#include "qemu-common.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "hw/hw.h"
#include "hw/boards.h"
#include "qemu/timer.h"
#include "migration/cpu.h"
#ifdef TARGET_SPARC64
static const VMStateDescription vmstate_cpu_timer = {
.name = "cpu_timer",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT32(frequency, CPUTimer),
VMSTATE_UINT32(disabled, CPUTimer),
VMSTATE_UINT64(disabled_mask, CPUTimer),
VMSTATE_UINT32(npt, CPUTimer),
VMSTATE_UINT64(npt_mask, CPUTimer),
VMSTATE_INT64(clock_offset, CPUTimer),
VMSTATE_TIMER_PTR(qtimer, CPUTimer),
VMSTATE_END_OF_LIST()
}
};
#define VMSTATE_CPU_TIMER(_f, _s) \
VMSTATE_STRUCT_POINTER(_f, _s, vmstate_cpu_timer, CPUTimer)
static const VMStateDescription vmstate_trap_state = {
.name = "trap_state",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT64(tpc, trap_state),
VMSTATE_UINT64(tnpc, trap_state),
VMSTATE_UINT64(tstate, trap_state),
VMSTATE_UINT32(tt, trap_state),
VMSTATE_END_OF_LIST()
}
};
static const VMStateDescription vmstate_tlb_entry = {
.name = "tlb_entry",
.version_id = 1,
.minimum_version_id = 1,
.minimum_version_id_old = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT64(tag, SparcTLBEntry),
VMSTATE_UINT64(tte, SparcTLBEntry),
VMSTATE_END_OF_LIST()
}
};
#endif
static int get_psr(QEMUFile *f, void *opaque, size_t size)
{
SPARCCPU *cpu = opaque;
CPUSPARCState *env = &cpu->env;
uint32_t val = qemu_get_be32(f);
/* needed to ensure that the wrapping registers are correctly updated */
env->cwp = 0;
cpu_put_psr_raw(env, val);
return 0;
}
static void put_psr(QEMUFile *f, void *opaque, size_t size)
{
SPARCCPU *cpu = opaque;
CPUSPARCState *env = &cpu->env;
uint32_t val;
val = cpu_get_psr(env);
qemu_put_be32(f, val);
}
static const VMStateInfo vmstate_psr = {
.name = "psr",
.get = get_psr,
.put = put_psr,
};
static void cpu_pre_save(void *opaque)
{
SPARCCPU *cpu = opaque;
CPUSPARCState *env = &cpu->env;
/* if env->cwp == env->nwindows - 1, this will set the ins of the last
* window as the outs of the first window
*/
cpu_set_cwp(env, env->cwp);
}
/* 32-bit SPARC retains migration compatibility with older versions
* of QEMU; 64-bit SPARC has had a migration break since then, so the
* versions are different.
*/
#ifndef TARGET_SPARC64
#define SPARC_VMSTATE_VER 7
#else
#define SPARC_VMSTATE_VER 9
#endif
const VMStateDescription vmstate_sparc_cpu = {
.name = "cpu",
.version_id = SPARC_VMSTATE_VER,
.minimum_version_id = SPARC_VMSTATE_VER,
.minimum_version_id_old = SPARC_VMSTATE_VER,
.pre_save = cpu_pre_save,
.fields = (VMStateField[]) {
VMSTATE_UINTTL_ARRAY(env.gregs, SPARCCPU, 8),
VMSTATE_UINT32(env.nwindows, SPARCCPU),
VMSTATE_VARRAY_MULTIPLY(env.regbase, SPARCCPU, env.nwindows, 16,
vmstate_info_uinttl, target_ulong),
VMSTATE_CPUDOUBLE_ARRAY(env.fpr, SPARCCPU, TARGET_DPREGS),
VMSTATE_UINTTL(env.pc, SPARCCPU),
VMSTATE_UINTTL(env.npc, SPARCCPU),
VMSTATE_UINTTL(env.y, SPARCCPU),
{
.name = "psr",
.version_id = 0,
.size = sizeof(uint32_t),
.info = &vmstate_psr,
.flags = VMS_SINGLE,
.offset = 0,
},
VMSTATE_UINTTL(env.fsr, SPARCCPU),
VMSTATE_UINTTL(env.tbr, SPARCCPU),
VMSTATE_INT32(env.interrupt_index, SPARCCPU),
VMSTATE_UINT32(env.pil_in, SPARCCPU),
#ifndef TARGET_SPARC64
/* MMU */
VMSTATE_UINT32(env.wim, SPARCCPU),
VMSTATE_UINT32_ARRAY(env.mmuregs, SPARCCPU, 32),
VMSTATE_UINT64_ARRAY(env.mxccdata, SPARCCPU, 4),
VMSTATE_UINT64_ARRAY(env.mxccregs, SPARCCPU, 8),
VMSTATE_UINT32(env.mmubpctrv, SPARCCPU),
VMSTATE_UINT32(env.mmubpctrc, SPARCCPU),
VMSTATE_UINT32(env.mmubpctrs, SPARCCPU),
VMSTATE_UINT64(env.mmubpaction, SPARCCPU),
VMSTATE_UINT64_ARRAY(env.mmubpregs, SPARCCPU, 4),
#else
VMSTATE_UINT64(env.lsu, SPARCCPU),
VMSTATE_UINT64_ARRAY(env.immuregs, SPARCCPU, 16),
VMSTATE_UINT64_ARRAY(env.dmmuregs, SPARCCPU, 16),
VMSTATE_STRUCT_ARRAY(env.itlb, SPARCCPU, 64, 0,
vmstate_tlb_entry, SparcTLBEntry),
VMSTATE_STRUCT_ARRAY(env.dtlb, SPARCCPU, 64, 0,
vmstate_tlb_entry, SparcTLBEntry),
VMSTATE_UINT32(env.mmu_version, SPARCCPU),
VMSTATE_STRUCT_ARRAY(env.ts, SPARCCPU, MAXTL_MAX, 0,
vmstate_trap_state, trap_state),
VMSTATE_UINT32(env.xcc, SPARCCPU),
VMSTATE_UINT32(env.asi, SPARCCPU),
VMSTATE_UINT32(env.pstate, SPARCCPU),
VMSTATE_UINT32(env.tl, SPARCCPU),
VMSTATE_UINT32(env.cansave, SPARCCPU),
VMSTATE_UINT32(env.canrestore, SPARCCPU),
VMSTATE_UINT32(env.otherwin, SPARCCPU),
VMSTATE_UINT32(env.wstate, SPARCCPU),
VMSTATE_UINT32(env.cleanwin, SPARCCPU),
VMSTATE_UINT64_ARRAY(env.agregs, SPARCCPU, 8),
VMSTATE_UINT64_ARRAY(env.bgregs, SPARCCPU, 8),
VMSTATE_UINT64_ARRAY(env.igregs, SPARCCPU, 8),
VMSTATE_UINT64_ARRAY(env.mgregs, SPARCCPU, 8),
VMSTATE_UINT64(env.fprs, SPARCCPU),
VMSTATE_UINT64(env.tick_cmpr, SPARCCPU),
VMSTATE_UINT64(env.stick_cmpr, SPARCCPU),
VMSTATE_CPU_TIMER(env.tick, SPARCCPU),
VMSTATE_CPU_TIMER(env.stick, SPARCCPU),
VMSTATE_UINT64(env.gsr, SPARCCPU),
VMSTATE_UINT32(env.gl, SPARCCPU),
VMSTATE_UINT64(env.hpstate, SPARCCPU),
VMSTATE_UINT64_ARRAY(env.htstate, SPARCCPU, MAXTL_MAX),
VMSTATE_UINT64(env.hintp, SPARCCPU),
VMSTATE_UINT64(env.htba, SPARCCPU),
VMSTATE_UINT64(env.hver, SPARCCPU),
VMSTATE_UINT64(env.hstick_cmpr, SPARCCPU),
VMSTATE_UINT64(env.ssr, SPARCCPU),
VMSTATE_CPU_TIMER(env.hstick, SPARCCPU),
/* On SPARC32 env.psrpil and env.cwp are migrated as part of the PSR */
VMSTATE_UINT32(env.psrpil, SPARCCPU),
VMSTATE_UINT32(env.cwp, SPARCCPU),
#endif
VMSTATE_END_OF_LIST()
},
};

880
target/sparc/mmu_helper.c Normal file
View file

@ -0,0 +1,880 @@
/*
* Sparc MMU 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 "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "trace.h"
#include "exec/address-spaces.h"
/* Sparc MMU emulation */
#if defined(CONFIG_USER_ONLY)
int sparc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
int mmu_idx)
{
if (rw & 2) {
cs->exception_index = TT_TFAULT;
} else {
cs->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(CPUSPARCState *env, hwaddr *physical,
int *prot, int *access_index,
target_ulong address, int rw, int mmu_idx,
target_ulong *page_size)
{
int access_perms = 0;
hwaddr pde_ptr;
uint32_t pde;
int error_code = 0, is_dirty, is_user;
unsigned long page_offset;
CPUState *cs = CPU(sparc_env_get_cpu(env));
is_user = mmu_idx == MMU_USER_IDX;
if (mmu_idx == MMU_PHYS_IDX) {
*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(cs->as, 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(cs->as, 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(cs->as, 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(cs->as, 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 = 0;
}
*page_size = TARGET_PAGE_SIZE;
break;
case 2: /* L2 PTE */
page_offset = address & 0x3f000;
*page_size = 0x40000;
}
break;
case 2: /* L1 PTE */
page_offset = address & 0xfff000;
*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(cs->as, 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 = ((hwaddr)(pde & PTE_ADDR_MASK) << 4) + page_offset;
return error_code;
}
/* Perform address translation */
int sparc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
int mmu_idx)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
hwaddr paddr;
target_ulong vaddr;
target_ulong page_size;
int error_code = 0, prot, access_index;
address &= TARGET_PAGE_MASK;
error_code = get_physical_address(env, &paddr, &prot, &access_index,
address, rw, mmu_idx, &page_size);
vaddr = address;
if (error_code == 0) {
qemu_log_mask(CPU_LOG_MMU,
"Translate at %" VADDR_PRIx " -> " TARGET_FMT_plx ", vaddr "
TARGET_FMT_lx "\n", address, paddr, vaddr);
tlb_set_page(cs, 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. */
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, TARGET_PAGE_SIZE);
return 0;
} else {
if (rw & 2) {
cs->exception_index = TT_TFAULT;
} else {
cs->exception_index = TT_DFAULT;
}
return 1;
}
}
target_ulong mmu_probe(CPUSPARCState *env, target_ulong address, int mmulev)
{
CPUState *cs = CPU(sparc_env_get_cpu(env));
hwaddr pde_ptr;
uint32_t pde;
/* Context base + context number */
pde_ptr = (hwaddr)(env->mmuregs[1] << 4) +
(env->mmuregs[2] << 2);
pde = ldl_phys(cs->as, 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(cs->as, 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(cs->as, 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(cs->as, 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;
}
void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUSPARCState *env)
{
CPUState *cs = CPU(sparc_env_get_cpu(env));
target_ulong va, va1, va2;
unsigned int n, m, o;
hwaddr pde_ptr, pa;
uint32_t pde;
pde_ptr = (env->mmuregs[1] << 4) + (env->mmuregs[2] << 2);
pde = ldl_phys(cs->as, pde_ptr);
(*cpu_fprintf)(f, "Root ptr: " TARGET_FMT_plx ", ctx: %d\n",
(hwaddr)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(cs, va);
(*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(cs, va1);
(*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(cs, va2);
(*cpu_fprintf)(f, " VA: " TARGET_FMT_lx ", PA: "
TARGET_FMT_plx " PTE: "
TARGET_FMT_lx "\n",
va2, pa, pde);
}
}
}
}
}
}
}
/* Gdb expects all registers windows to be flushed in ram. This function handles
* reads (and only reads) in stack frames as if windows were flushed. We assume
* that the sparc ABI is followed.
*/
int sparc_cpu_memory_rw_debug(CPUState *cs, vaddr address,
uint8_t *buf, int len, bool is_write)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
target_ulong addr = address;
int i;
int len1;
int cwp = env->cwp;
if (!is_write) {
for (i = 0; i < env->nwindows; i++) {
int off;
target_ulong fp = env->regbase[cwp * 16 + 22];
/* Assume fp == 0 means end of frame. */
if (fp == 0) {
break;
}
cwp = cpu_cwp_inc(env, cwp + 1);
/* Invalid window ? */
if (env->wim & (1 << cwp)) {
break;
}
/* According to the ABI, the stack is growing downward. */
if (addr + len < fp) {
break;
}
/* Not in this frame. */
if (addr > fp + 64) {
continue;
}
/* Handle access before this window. */
if (addr < fp) {
len1 = fp - addr;
if (cpu_memory_rw_debug(cs, addr, buf, len1, is_write) != 0) {
return -1;
}
addr += len1;
len -= len1;
buf += len1;
}
/* Access byte per byte to registers. Not very efficient but speed
* is not critical.
*/
off = addr - fp;
len1 = 64 - off;
if (len1 > len) {
len1 = len;
}
for (; len1; len1--) {
int reg = cwp * 16 + 8 + (off >> 2);
union {
uint32_t v;
uint8_t c[4];
} u;
u.v = cpu_to_be32(env->regbase[reg]);
*buf++ = u.c[off & 3];
addr++;
len--;
off++;
}
if (len == 0) {
return 0;
}
}
}
return cpu_memory_rw_debug(cs, addr, buf, len, is_write);
}
#else /* !TARGET_SPARC64 */
/* 41 bit physical address space */
static inline hwaddr ultrasparc_truncate_physical(uint64_t x)
{
return x & 0x1ffffffffffULL;
}
/*
* UltraSparc IIi I/DMMUs
*/
/* 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,
hwaddr *physical)
{
uint64_t mask;
switch (TTE_PGSIZE(tlb->tte)) {
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) &&
(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;
return 1;
}
return 0;
}
static int get_physical_address_data(CPUSPARCState *env,
hwaddr *physical, int *prot,
target_ulong address, int rw, int mmu_idx)
{
CPUState *cs = CPU(sparc_env_get_cpu(env));
unsigned int i;
uint64_t context;
uint64_t sfsr = 0;
bool is_user = false;
switch (mmu_idx) {
case MMU_PHYS_IDX:
g_assert_not_reached();
case MMU_USER_IDX:
is_user = true;
/* fallthru */
case MMU_KERNEL_IDX:
context = env->dmmu.mmu_primary_context & 0x1fff;
sfsr |= SFSR_CT_PRIMARY;
break;
case MMU_USER_SECONDARY_IDX:
is_user = true;
/* fallthru */
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)) {
int do_fault = 0;
/* access ok? */
/* 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 */
trace_mmu_helper_dfault(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. */
cs->exception_index = TT_DFAULT;
} else if (!TTE_IS_W_OK(env->dtlb[i].tte) && (rw == 1)) {
do_fault = 1;
cs->exception_index = TT_DPROT;
trace_mmu_helper_dprot(address, context, mmu_idx, env->tl);
}
if (!do_fault) {
*prot = PAGE_READ;
if (TTE_IS_W_OK(env->dtlb[i].tte)) {
*prot |= PAGE_WRITE;
}
TTE_SET_USED(env->dtlb[i].tte);
return 0;
}
if (env->dmmu.sfsr & SFSR_VALID_BIT) { /* Fault status register */
sfsr |= SFSR_OW_BIT; /* overflow (not read before
another fault) */
}
if (env->pstate & PS_PRIV) {
sfsr |= SFSR_PR_BIT;
}
/* 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;
}
}
trace_mmu_helper_dmiss(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;
cs->exception_index = TT_DMISS;
return 1;
}
static int get_physical_address_code(CPUSPARCState *env,
hwaddr *physical, int *prot,
target_ulong address, int mmu_idx)
{
CPUState *cs = CPU(sparc_env_get_cpu(env));
unsigned int i;
uint64_t context;
bool is_user = false;
switch (mmu_idx) {
case MMU_PHYS_IDX:
case MMU_USER_SECONDARY_IDX:
case MMU_KERNEL_SECONDARY_IDX:
g_assert_not_reached();
case MMU_USER_IDX:
is_user = true;
/* fallthru */
case MMU_KERNEL_IDX:
context = env->dmmu.mmu_primary_context & 0x1fff;
break;
default:
context = 0;
break;
}
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)) {
/* access ok? */
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;
cs->exception_index = TT_TFAULT;
env->immu.tag_access = (address & ~0x1fffULL) | context;
trace_mmu_helper_tfault(address, context);
return 1;
}
*prot = PAGE_EXEC;
TTE_SET_USED(env->itlb[i].tte);
return 0;
}
}
trace_mmu_helper_tmiss(address, context);
/* Context is stored in DMMU (dmmuregs[1]) also for IMMU */
env->immu.tag_access = (address & ~0x1fffULL) | context;
cs->exception_index = TT_TMISS;
return 1;
}
static int get_physical_address(CPUSPARCState *env, hwaddr *physical,
int *prot, int *access_index,
target_ulong address, int rw, int mmu_idx,
target_ulong *page_size)
{
/* ??? We treat everything as a small page, then explicitly flush
everything when an entry is evicted. */
*page_size = TARGET_PAGE_SIZE;
/* safety net to catch wrong softmmu index use from dynamic code */
if (env->tl > 0 && mmu_idx != MMU_NUCLEUS_IDX) {
if (rw == 2) {
trace_mmu_helper_get_phys_addr_code(env->tl, mmu_idx,
env->dmmu.mmu_primary_context,
env->dmmu.mmu_secondary_context,
address);
} else {
trace_mmu_helper_get_phys_addr_data(env->tl, mmu_idx,
env->dmmu.mmu_primary_context,
env->dmmu.mmu_secondary_context,
address);
}
}
if (mmu_idx == MMU_PHYS_IDX) {
*physical = ultrasparc_truncate_physical(address);
*prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
return 0;
}
if (rw == 2) {
return get_physical_address_code(env, physical, prot, address,
mmu_idx);
} else {
return get_physical_address_data(env, physical, prot, address, rw,
mmu_idx);
}
}
/* Perform address translation */
int sparc_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw,
int mmu_idx)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
target_ulong vaddr;
hwaddr paddr;
target_ulong page_size;
int error_code = 0, prot, access_index;
address &= TARGET_PAGE_MASK;
error_code = get_physical_address(env, &paddr, &prot, &access_index,
address, rw, mmu_idx, &page_size);
if (error_code == 0) {
vaddr = address;
trace_mmu_helper_mmu_fault(address, paddr, mmu_idx, env->tl,
env->dmmu.mmu_primary_context,
env->dmmu.mmu_secondary_context);
tlb_set_page(cs, vaddr, paddr, prot, mmu_idx, page_size);
return 0;
}
/* XXX */
return 1;
}
void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUSPARCState *env)
{
unsigned int i;
const char *mask;
(*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) {
(*cpu_fprintf)(f, "DMMU disabled\n");
} else {
(*cpu_fprintf)(f, "DMMU dump\n");
for (i = 0; i < 64; i++) {
switch (TTE_PGSIZE(env->dtlb[i].tte)) {
default:
case 0x0:
mask = " 8k";
break;
case 0x1:
mask = " 64k";
break;
case 0x2:
mask = "512k";
break;
case 0x3:
mask = " 4M";
break;
}
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) {
(*cpu_fprintf)(f, "IMMU disabled\n");
} else {
(*cpu_fprintf)(f, "IMMU dump\n");
for (i = 0; i < 64; i++) {
switch (TTE_PGSIZE(env->itlb[i].tte)) {
default:
case 0x0:
mask = " 8k";
break;
case 0x1:
mask = " 64k";
break;
case 0x2:
mask = "512k";
break;
case 0x3:
mask = " 4M";
break;
}
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 /* TARGET_SPARC64 */
static int cpu_sparc_get_phys_page(CPUSPARCState *env, hwaddr *phys,
target_ulong addr, int rw, int mmu_idx)
{
target_ulong page_size;
int prot, access_index;
return get_physical_address(env, phys, &prot, &access_index, addr, rw,
mmu_idx, &page_size);
}
#if defined(TARGET_SPARC64)
hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr,
int mmu_idx)
{
hwaddr phys_addr;
if (cpu_sparc_get_phys_page(env, &phys_addr, addr, 4, mmu_idx) != 0) {
return -1;
}
return phys_addr;
}
#endif
hwaddr sparc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
{
SPARCCPU *cpu = SPARC_CPU(cs);
CPUSPARCState *env = &cpu->env;
hwaddr phys_addr;
int mmu_idx = cpu_mmu_index(env, false);
MemoryRegionSection section;
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;
}
}
section = memory_region_find(get_system_memory(), phys_addr, 1);
memory_region_unref(section.mr);
if (!int128_nz(section.size)) {
return -1;
}
return phys_addr;
}
#endif

159
target/sparc/monitor.c Normal file
View file

@ -0,0 +1,159 @@
/*
* QEMU monitor
*
* Copyright (c) 2003-2004 Fabrice Bellard
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "qemu/osdep.h"
#include "cpu.h"
#include "monitor/monitor.h"
#include "monitor/hmp-target.h"
#include "hmp.h"
void hmp_info_tlb(Monitor *mon, const QDict *qdict)
{
CPUArchState *env1 = mon_get_cpu_env();
dump_mmu((FILE*)mon, (fprintf_function)monitor_printf, env1);
}
#ifndef TARGET_SPARC64
static target_long monitor_get_psr (const struct MonitorDef *md, int val)
{
CPUArchState *env = mon_get_cpu_env();
return cpu_get_psr(env);
}
#endif
static target_long monitor_get_reg(const struct MonitorDef *md, int val)
{
CPUArchState *env = mon_get_cpu_env();
return env->regwptr[val];
}
const MonitorDef monitor_defs[] = {
{ "g0", offsetof(CPUSPARCState, gregs[0]) },
{ "g1", offsetof(CPUSPARCState, gregs[1]) },
{ "g2", offsetof(CPUSPARCState, gregs[2]) },
{ "g3", offsetof(CPUSPARCState, gregs[3]) },
{ "g4", offsetof(CPUSPARCState, gregs[4]) },
{ "g5", offsetof(CPUSPARCState, gregs[5]) },
{ "g6", offsetof(CPUSPARCState, gregs[6]) },
{ "g7", offsetof(CPUSPARCState, gregs[7]) },
{ "o0", 0, monitor_get_reg },
{ "o1", 1, monitor_get_reg },
{ "o2", 2, monitor_get_reg },
{ "o3", 3, monitor_get_reg },
{ "o4", 4, monitor_get_reg },
{ "o5", 5, monitor_get_reg },
{ "o6", 6, monitor_get_reg },
{ "o7", 7, monitor_get_reg },
{ "l0", 8, monitor_get_reg },
{ "l1", 9, monitor_get_reg },
{ "l2", 10, monitor_get_reg },
{ "l3", 11, monitor_get_reg },
{ "l4", 12, monitor_get_reg },
{ "l5", 13, monitor_get_reg },
{ "l6", 14, monitor_get_reg },
{ "l7", 15, monitor_get_reg },
{ "i0", 16, monitor_get_reg },
{ "i1", 17, monitor_get_reg },
{ "i2", 18, monitor_get_reg },
{ "i3", 19, monitor_get_reg },
{ "i4", 20, monitor_get_reg },
{ "i5", 21, monitor_get_reg },
{ "i6", 22, monitor_get_reg },
{ "i7", 23, monitor_get_reg },
{ "pc", offsetof(CPUSPARCState, pc) },
{ "npc", offsetof(CPUSPARCState, npc) },
{ "y", offsetof(CPUSPARCState, y) },
#ifndef TARGET_SPARC64
{ "psr", 0, &monitor_get_psr, },
{ "wim", offsetof(CPUSPARCState, wim) },
#endif
{ "tbr", offsetof(CPUSPARCState, tbr) },
{ "fsr", offsetof(CPUSPARCState, fsr) },
{ "f0", offsetof(CPUSPARCState, fpr[0].l.upper) },
{ "f1", offsetof(CPUSPARCState, fpr[0].l.lower) },
{ "f2", offsetof(CPUSPARCState, fpr[1].l.upper) },
{ "f3", offsetof(CPUSPARCState, fpr[1].l.lower) },
{ "f4", offsetof(CPUSPARCState, fpr[2].l.upper) },
{ "f5", offsetof(CPUSPARCState, fpr[2].l.lower) },
{ "f6", offsetof(CPUSPARCState, fpr[3].l.upper) },
{ "f7", offsetof(CPUSPARCState, fpr[3].l.lower) },
{ "f8", offsetof(CPUSPARCState, fpr[4].l.upper) },
{ "f9", offsetof(CPUSPARCState, fpr[4].l.lower) },
{ "f10", offsetof(CPUSPARCState, fpr[5].l.upper) },
{ "f11", offsetof(CPUSPARCState, fpr[5].l.lower) },
{ "f12", offsetof(CPUSPARCState, fpr[6].l.upper) },
{ "f13", offsetof(CPUSPARCState, fpr[6].l.lower) },
{ "f14", offsetof(CPUSPARCState, fpr[7].l.upper) },
{ "f15", offsetof(CPUSPARCState, fpr[7].l.lower) },
{ "f16", offsetof(CPUSPARCState, fpr[8].l.upper) },
{ "f17", offsetof(CPUSPARCState, fpr[8].l.lower) },
{ "f18", offsetof(CPUSPARCState, fpr[9].l.upper) },
{ "f19", offsetof(CPUSPARCState, fpr[9].l.lower) },
{ "f20", offsetof(CPUSPARCState, fpr[10].l.upper) },
{ "f21", offsetof(CPUSPARCState, fpr[10].l.lower) },
{ "f22", offsetof(CPUSPARCState, fpr[11].l.upper) },
{ "f23", offsetof(CPUSPARCState, fpr[11].l.lower) },
{ "f24", offsetof(CPUSPARCState, fpr[12].l.upper) },
{ "f25", offsetof(CPUSPARCState, fpr[12].l.lower) },
{ "f26", offsetof(CPUSPARCState, fpr[13].l.upper) },
{ "f27", offsetof(CPUSPARCState, fpr[13].l.lower) },
{ "f28", offsetof(CPUSPARCState, fpr[14].l.upper) },
{ "f29", offsetof(CPUSPARCState, fpr[14].l.lower) },
{ "f30", offsetof(CPUSPARCState, fpr[15].l.upper) },
{ "f31", offsetof(CPUSPARCState, fpr[15].l.lower) },
#ifdef TARGET_SPARC64
{ "f32", offsetof(CPUSPARCState, fpr[16]) },
{ "f34", offsetof(CPUSPARCState, fpr[17]) },
{ "f36", offsetof(CPUSPARCState, fpr[18]) },
{ "f38", offsetof(CPUSPARCState, fpr[19]) },
{ "f40", offsetof(CPUSPARCState, fpr[20]) },
{ "f42", offsetof(CPUSPARCState, fpr[21]) },
{ "f44", offsetof(CPUSPARCState, fpr[22]) },
{ "f46", offsetof(CPUSPARCState, fpr[23]) },
{ "f48", offsetof(CPUSPARCState, fpr[24]) },
{ "f50", offsetof(CPUSPARCState, fpr[25]) },
{ "f52", offsetof(CPUSPARCState, fpr[26]) },
{ "f54", offsetof(CPUSPARCState, fpr[27]) },
{ "f56", offsetof(CPUSPARCState, fpr[28]) },
{ "f58", offsetof(CPUSPARCState, fpr[29]) },
{ "f60", offsetof(CPUSPARCState, fpr[30]) },
{ "f62", offsetof(CPUSPARCState, fpr[31]) },
{ "asi", offsetof(CPUSPARCState, asi) },
{ "pstate", offsetof(CPUSPARCState, pstate) },
{ "cansave", offsetof(CPUSPARCState, cansave) },
{ "canrestore", offsetof(CPUSPARCState, canrestore) },
{ "otherwin", offsetof(CPUSPARCState, otherwin) },
{ "wstate", offsetof(CPUSPARCState, wstate) },
{ "cleanwin", offsetof(CPUSPARCState, cleanwin) },
{ "fprs", offsetof(CPUSPARCState, fprs) },
#endif
{ NULL },
};
const MonitorDef *target_monitor_defs(void)
{
return monitor_defs;
}

28
target/sparc/trace-events Normal file
View file

@ -0,0 +1,28 @@
# See docs/tracing.txt for syntax documentation.
# target/sparc/mmu_helper.c
mmu_helper_dfault(uint64_t address, uint64_t context, int mmu_idx, uint32_t tl) "DFAULT at %"PRIx64" context %"PRIx64" mmu_idx=%d tl=%d"
mmu_helper_dprot(uint64_t address, uint64_t context, int mmu_idx, uint32_t tl) "DPROT at %"PRIx64" context %"PRIx64" mmu_idx=%d tl=%d"
mmu_helper_dmiss(uint64_t address, uint64_t context) "DMISS at %"PRIx64" context %"PRIx64
mmu_helper_tfault(uint64_t address, uint64_t context) "TFAULT at %"PRIx64" context %"PRIx64
mmu_helper_tmiss(uint64_t address, uint64_t context) "TMISS at %"PRIx64" context %"PRIx64
mmu_helper_get_phys_addr_code(uint32_t tl, int mmu_idx, uint64_t prim_context, uint64_t sec_context, uint64_t address) "tl=%d mmu_idx=%d primary context=%"PRIx64" secondary context=%"PRIx64" address=%"PRIx64
mmu_helper_get_phys_addr_data(uint32_t tl, int mmu_idx, uint64_t prim_context, uint64_t sec_context, uint64_t address) "tl=%d mmu_idx=%d primary context=%"PRIx64" secondary context=%"PRIx64" address=%"PRIx64
mmu_helper_mmu_fault(uint64_t address, uint64_t paddr, int mmu_idx, uint32_t tl, uint64_t prim_context, uint64_t sec_context) "Translate at %"PRIx64" -> %"PRIx64", mmu_idx=%d tl=%d primary context=%"PRIx64" secondary context=%"PRIx64
# target/sparc/int64_helper.c
int_helper_set_softint(uint32_t softint) "new %08x"
int_helper_clear_softint(uint32_t softint) "new %08x"
int_helper_write_softint(uint32_t softint) "new %08x"
# target/sparc/int32_helper.c
int_helper_icache_freeze(void) "Instruction cache: freeze"
int_helper_dcache_freeze(void) "Data cache: freeze"
# target/sparc/win_helper.c
win_helper_gregset_error(uint32_t pstate) "ERROR in get_gregset: active pstate bits=%x"
win_helper_switch_pstate(uint32_t pstate_regs, uint32_t new_pstate_regs) "change_pstate: switching regs old=%x new=%x"
win_helper_no_switch_pstate(uint32_t new_pstate_regs) "change_pstate: regs new=%x (unchanged)"
win_helper_wrpil(uint32_t psrpil, uint32_t new_pil) "old=%x new=%x"
win_helper_done(uint32_t tl) "tl=%d"
win_helper_retry(uint32_t tl) "tl=%d"

5924
target/sparc/translate.c Normal file

File diff suppressed because it is too large Load diff

490
target/sparc/vis_helper.c Normal file
View file

@ -0,0 +1,490 @@
/*
* VIS op 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 "qemu/osdep.h"
#include "cpu.h"
#include "exec/helper-proto.h"
/* This function uses non-native bit order */
#define GET_FIELD(X, FROM, TO) \
((X) >> (63 - (TO)) & ((1ULL << ((TO) - (FROM) + 1)) - 1))
/* This function uses the order in the manuals, i.e. bit 0 is 2^0 */
#define GET_FIELD_SP(X, FROM, TO) \
GET_FIELD(X, 63 - (TO), 63 - (FROM))
target_ulong helper_array8(target_ulong pixel_addr, target_ulong cubesize)
{
return (GET_FIELD_SP(pixel_addr, 60, 63) << (17 + 2 * cubesize)) |
(GET_FIELD_SP(pixel_addr, 39, 39 + cubesize - 1) << (17 + cubesize)) |
(GET_FIELD_SP(pixel_addr, 17 + cubesize - 1, 17) << 17) |
(GET_FIELD_SP(pixel_addr, 56, 59) << 13) |
(GET_FIELD_SP(pixel_addr, 35, 38) << 9) |
(GET_FIELD_SP(pixel_addr, 13, 16) << 5) |
(((pixel_addr >> 55) & 1) << 4) |
(GET_FIELD_SP(pixel_addr, 33, 34) << 2) |
GET_FIELD_SP(pixel_addr, 11, 12);
}
#ifdef HOST_WORDS_BIGENDIAN
#define VIS_B64(n) b[7 - (n)]
#define VIS_W64(n) w[3 - (n)]
#define VIS_SW64(n) sw[3 - (n)]
#define VIS_L64(n) l[1 - (n)]
#define VIS_B32(n) b[3 - (n)]
#define VIS_W32(n) w[1 - (n)]
#else
#define VIS_B64(n) b[n]
#define VIS_W64(n) w[n]
#define VIS_SW64(n) sw[n]
#define VIS_L64(n) l[n]
#define VIS_B32(n) b[n]
#define VIS_W32(n) w[n]
#endif
typedef union {
uint8_t b[8];
uint16_t w[4];
int16_t sw[4];
uint32_t l[2];
uint64_t ll;
float64 d;
} VIS64;
typedef union {
uint8_t b[4];
uint16_t w[2];
uint32_t l;
float32 f;
} VIS32;
uint64_t helper_fpmerge(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
s.ll = src1;
d.ll = src2;
/* Reverse calculation order to handle overlap */
d.VIS_B64(7) = s.VIS_B64(3);
d.VIS_B64(6) = d.VIS_B64(3);
d.VIS_B64(5) = s.VIS_B64(2);
d.VIS_B64(4) = d.VIS_B64(2);
d.VIS_B64(3) = s.VIS_B64(1);
d.VIS_B64(2) = d.VIS_B64(1);
d.VIS_B64(1) = s.VIS_B64(0);
/* d.VIS_B64(0) = d.VIS_B64(0); */
return d.ll;
}
uint64_t helper_fmul8x16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
s.ll = src1;
d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * (int32_t)s.VIS_B64(r); \
if ((tmp & 0xff) > 0x7f) { \
tmp += 0x100; \
} \
d.VIS_W64(r) = tmp >> 8;
PMUL(0);
PMUL(1);
PMUL(2);
PMUL(3);
#undef PMUL
return d.ll;
}
uint64_t helper_fmul8x16al(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
s.ll = src1;
d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(1) * (int32_t)s.VIS_B64(r); \
if ((tmp & 0xff) > 0x7f) { \
tmp += 0x100; \
} \
d.VIS_W64(r) = tmp >> 8;
PMUL(0);
PMUL(1);
PMUL(2);
PMUL(3);
#undef PMUL
return d.ll;
}
uint64_t helper_fmul8x16au(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
s.ll = src1;
d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(0) * (int32_t)s.VIS_B64(r); \
if ((tmp & 0xff) > 0x7f) { \
tmp += 0x100; \
} \
d.VIS_W64(r) = tmp >> 8;
PMUL(0);
PMUL(1);
PMUL(2);
PMUL(3);
#undef PMUL
return d.ll;
}
uint64_t helper_fmul8sux16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
s.ll = src1;
d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
if ((tmp & 0xff) > 0x7f) { \
tmp += 0x100; \
} \
d.VIS_W64(r) = tmp >> 8;
PMUL(0);
PMUL(1);
PMUL(2);
PMUL(3);
#undef PMUL
return d.ll;
}
uint64_t helper_fmul8ulx16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
s.ll = src1;
d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
if ((tmp & 0xff) > 0x7f) { \
tmp += 0x100; \
} \
d.VIS_W64(r) = tmp >> 8;
PMUL(0);
PMUL(1);
PMUL(2);
PMUL(3);
#undef PMUL
return d.ll;
}
uint64_t helper_fmuld8sux16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
s.ll = src1;
d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * ((int32_t)s.VIS_SW64(r) >> 8); \
if ((tmp & 0xff) > 0x7f) { \
tmp += 0x100; \
} \
d.VIS_L64(r) = tmp;
/* Reverse calculation order to handle overlap */
PMUL(1);
PMUL(0);
#undef PMUL
return d.ll;
}
uint64_t helper_fmuld8ulx16(uint64_t src1, uint64_t src2)
{
VIS64 s, d;
uint32_t tmp;
s.ll = src1;
d.ll = src2;
#define PMUL(r) \
tmp = (int32_t)d.VIS_SW64(r) * ((uint32_t)s.VIS_B64(r * 2)); \
if ((tmp & 0xff) > 0x7f) { \
tmp += 0x100; \
} \
d.VIS_L64(r) = tmp;
/* Reverse calculation order to handle overlap */
PMUL(1);
PMUL(0);
#undef PMUL
return d.ll;
}
uint64_t helper_fexpand(uint64_t src1, uint64_t src2)
{
VIS32 s;
VIS64 d;
s.l = (uint32_t)src1;
d.ll = src2;
d.VIS_W64(0) = s.VIS_B32(0) << 4;
d.VIS_W64(1) = s.VIS_B32(1) << 4;
d.VIS_W64(2) = s.VIS_B32(2) << 4;
d.VIS_W64(3) = s.VIS_B32(3) << 4;
return d.ll;
}
#define VIS_HELPER(name, F) \
uint64_t name##16(uint64_t src1, uint64_t src2) \
{ \
VIS64 s, d; \
\
s.ll = src1; \
d.ll = src2; \
\
d.VIS_W64(0) = F(d.VIS_W64(0), s.VIS_W64(0)); \
d.VIS_W64(1) = F(d.VIS_W64(1), s.VIS_W64(1)); \
d.VIS_W64(2) = F(d.VIS_W64(2), s.VIS_W64(2)); \
d.VIS_W64(3) = F(d.VIS_W64(3), s.VIS_W64(3)); \
\
return d.ll; \
} \
\
uint32_t name##16s(uint32_t src1, uint32_t src2) \
{ \
VIS32 s, d; \
\
s.l = src1; \
d.l = src2; \
\
d.VIS_W32(0) = F(d.VIS_W32(0), s.VIS_W32(0)); \
d.VIS_W32(1) = F(d.VIS_W32(1), s.VIS_W32(1)); \
\
return d.l; \
} \
\
uint64_t name##32(uint64_t src1, uint64_t src2) \
{ \
VIS64 s, d; \
\
s.ll = src1; \
d.ll = src2; \
\
d.VIS_L64(0) = F(d.VIS_L64(0), s.VIS_L64(0)); \
d.VIS_L64(1) = F(d.VIS_L64(1), s.VIS_L64(1)); \
\
return d.ll; \
} \
\
uint32_t name##32s(uint32_t src1, uint32_t src2) \
{ \
VIS32 s, d; \
\
s.l = src1; \
d.l = src2; \
\
d.l = F(d.l, s.l); \
\
return d.l; \
}
#define FADD(a, b) ((a) + (b))
#define FSUB(a, b) ((a) - (b))
VIS_HELPER(helper_fpadd, FADD)
VIS_HELPER(helper_fpsub, FSUB)
#define VIS_CMPHELPER(name, F) \
uint64_t name##16(uint64_t src1, uint64_t src2) \
{ \
VIS64 s, d; \
\
s.ll = src1; \
d.ll = src2; \
\
d.VIS_W64(0) = F(s.VIS_W64(0), d.VIS_W64(0)) ? 1 : 0; \
d.VIS_W64(0) |= F(s.VIS_W64(1), d.VIS_W64(1)) ? 2 : 0; \
d.VIS_W64(0) |= F(s.VIS_W64(2), d.VIS_W64(2)) ? 4 : 0; \
d.VIS_W64(0) |= F(s.VIS_W64(3), d.VIS_W64(3)) ? 8 : 0; \
d.VIS_W64(1) = d.VIS_W64(2) = d.VIS_W64(3) = 0; \
\
return d.ll; \
} \
\
uint64_t name##32(uint64_t src1, uint64_t src2) \
{ \
VIS64 s, d; \
\
s.ll = src1; \
d.ll = src2; \
\
d.VIS_L64(0) = F(s.VIS_L64(0), d.VIS_L64(0)) ? 1 : 0; \
d.VIS_L64(0) |= F(s.VIS_L64(1), d.VIS_L64(1)) ? 2 : 0; \
d.VIS_L64(1) = 0; \
\
return d.ll; \
}
#define FCMPGT(a, b) ((a) > (b))
#define FCMPEQ(a, b) ((a) == (b))
#define FCMPLE(a, b) ((a) <= (b))
#define FCMPNE(a, b) ((a) != (b))
VIS_CMPHELPER(helper_fcmpgt, FCMPGT)
VIS_CMPHELPER(helper_fcmpeq, FCMPEQ)
VIS_CMPHELPER(helper_fcmple, FCMPLE)
VIS_CMPHELPER(helper_fcmpne, FCMPNE)
uint64_t helper_pdist(uint64_t sum, uint64_t src1, uint64_t src2)
{
int i;
for (i = 0; i < 8; i++) {
int s1, s2;
s1 = (src1 >> (56 - (i * 8))) & 0xff;
s2 = (src2 >> (56 - (i * 8))) & 0xff;
/* Absolute value of difference. */
s1 -= s2;
if (s1 < 0) {
s1 = -s1;
}
sum += s1;
}
return sum;
}
uint32_t helper_fpack16(uint64_t gsr, uint64_t rs2)
{
int scale = (gsr >> 3) & 0xf;
uint32_t ret = 0;
int byte;
for (byte = 0; byte < 4; byte++) {
uint32_t val;
int16_t src = rs2 >> (byte * 16);
int32_t scaled = src << scale;
int32_t from_fixed = scaled >> 7;
val = (from_fixed < 0 ? 0 :
from_fixed > 255 ? 255 : from_fixed);
ret |= val << (8 * byte);
}
return ret;
}
uint64_t helper_fpack32(uint64_t gsr, uint64_t rs1, uint64_t rs2)
{
int scale = (gsr >> 3) & 0x1f;
uint64_t ret = 0;
int word;
ret = (rs1 << 8) & ~(0x000000ff000000ffULL);
for (word = 0; word < 2; word++) {
uint64_t val;
int32_t src = rs2 >> (word * 32);
int64_t scaled = (int64_t)src << scale;
int64_t from_fixed = scaled >> 23;
val = (from_fixed < 0 ? 0 :
(from_fixed > 255) ? 255 : from_fixed);
ret |= val << (32 * word);
}
return ret;
}
uint32_t helper_fpackfix(uint64_t gsr, uint64_t rs2)
{
int scale = (gsr >> 3) & 0x1f;
uint32_t ret = 0;
int word;
for (word = 0; word < 2; word++) {
uint32_t val;
int32_t src = rs2 >> (word * 32);
int64_t scaled = (int64_t)src << scale;
int64_t from_fixed = scaled >> 16;
val = (from_fixed < -32768 ? -32768 :
from_fixed > 32767 ? 32767 : from_fixed);
ret |= (val & 0xffff) << (word * 16);
}
return ret;
}
uint64_t helper_bshuffle(uint64_t gsr, uint64_t src1, uint64_t src2)
{
union {
uint64_t ll[2];
uint8_t b[16];
} s;
VIS64 r;
uint32_t i, mask, host;
/* Set up S such that we can index across all of the bytes. */
#ifdef HOST_WORDS_BIGENDIAN
s.ll[0] = src1;
s.ll[1] = src2;
host = 0;
#else
s.ll[1] = src1;
s.ll[0] = src2;
host = 15;
#endif
mask = gsr >> 32;
for (i = 0; i < 8; ++i) {
unsigned e = (mask >> (28 - i*4)) & 0xf;
r.VIS_B64(i) = s.b[e ^ host];
}
return r.ll;
}

400
target/sparc/win_helper.c Normal file
View file

@ -0,0 +1,400 @@
/*
* Helpers for CWP and PSTATE handling
*
* 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 "qemu/osdep.h"
#include "cpu.h"
#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "trace.h"
static inline void memcpy32(target_ulong *dst, const target_ulong *src)
{
dst[0] = src[0];
dst[1] = src[1];
dst[2] = src[2];
dst[3] = src[3];
dst[4] = src[4];
dst[5] = src[5];
dst[6] = src[6];
dst[7] = src[7];
}
void cpu_set_cwp(CPUSPARCState *env, int new_cwp)
{
/* put the modified wrap registers at their proper location */
if (env->cwp == env->nwindows - 1) {
memcpy32(env->regbase, env->regbase + env->nwindows * 16);
}
env->cwp = new_cwp;
/* put the wrap registers at their temporary location */
if (new_cwp == env->nwindows - 1) {
memcpy32(env->regbase + env->nwindows * 16, env->regbase);
}
env->regwptr = env->regbase + (new_cwp * 16);
}
target_ulong cpu_get_psr(CPUSPARCState *env)
{
helper_compute_psr(env);
#if !defined(TARGET_SPARC64)
return env->version | (env->psr & PSR_ICC) |
(env->psref ? PSR_EF : 0) |
(env->psrpil << 8) |
(env->psrs ? PSR_S : 0) |
(env->psrps ? PSR_PS : 0) |
(env->psret ? PSR_ET : 0) | env->cwp;
#else
return env->psr & PSR_ICC;
#endif
}
void cpu_put_psr_raw(CPUSPARCState *env, target_ulong val)
{
env->psr = val & PSR_ICC;
#if !defined(TARGET_SPARC64)
env->psref = (val & PSR_EF) ? 1 : 0;
env->psrpil = (val & PSR_PIL) >> 8;
env->psrs = (val & PSR_S) ? 1 : 0;
env->psrps = (val & PSR_PS) ? 1 : 0;
env->psret = (val & PSR_ET) ? 1 : 0;
#endif
env->cc_op = CC_OP_FLAGS;
#if !defined(TARGET_SPARC64)
cpu_set_cwp(env, val & PSR_CWP);
#endif
}
void cpu_put_psr(CPUSPARCState *env, target_ulong val)
{
cpu_put_psr_raw(env, val);
#if ((!defined(TARGET_SPARC64)) && !defined(CONFIG_USER_ONLY))
cpu_check_irqs(env);
#endif
}
int cpu_cwp_inc(CPUSPARCState *env, int cwp)
{
if (unlikely(cwp >= env->nwindows)) {
cwp -= env->nwindows;
}
return cwp;
}
int cpu_cwp_dec(CPUSPARCState *env, int cwp)
{
if (unlikely(cwp < 0)) {
cwp += env->nwindows;
}
return cwp;
}
#ifndef TARGET_SPARC64
void helper_rett(CPUSPARCState *env)
{
unsigned int cwp;
if (env->psret == 1) {
cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC());
}
env->psret = 1;
cwp = cpu_cwp_inc(env, env->cwp + 1) ;
if (env->wim & (1 << cwp)) {
cpu_raise_exception_ra(env, TT_WIN_UNF, GETPC());
}
cpu_set_cwp(env, cwp);
env->psrs = env->psrps;
}
/* XXX: use another pointer for %iN registers to avoid slow wrapping
handling ? */
void helper_save(CPUSPARCState *env)
{
uint32_t cwp;
cwp = cpu_cwp_dec(env, env->cwp - 1);
if (env->wim & (1 << cwp)) {
cpu_raise_exception_ra(env, TT_WIN_OVF, GETPC());
}
cpu_set_cwp(env, cwp);
}
void helper_restore(CPUSPARCState *env)
{
uint32_t cwp;
cwp = cpu_cwp_inc(env, env->cwp + 1);
if (env->wim & (1 << cwp)) {
cpu_raise_exception_ra(env, TT_WIN_UNF, GETPC());
}
cpu_set_cwp(env, cwp);
}
void helper_wrpsr(CPUSPARCState *env, target_ulong new_psr)
{
if ((new_psr & PSR_CWP) >= env->nwindows) {
cpu_raise_exception_ra(env, TT_ILL_INSN, GETPC());
} else {
cpu_put_psr(env, new_psr);
}
}
target_ulong helper_rdpsr(CPUSPARCState *env)
{
return cpu_get_psr(env);
}
#else
/* XXX: use another pointer for %iN registers to avoid slow wrapping
handling ? */
void helper_save(CPUSPARCState *env)
{
uint32_t cwp;
cwp = cpu_cwp_dec(env, env->cwp - 1);
if (env->cansave == 0) {
int tt = TT_SPILL | (env->otherwin != 0
? (TT_WOTHER | ((env->wstate & 0x38) >> 1))
: ((env->wstate & 0x7) << 2));
cpu_raise_exception_ra(env, tt, GETPC());
} else {
if (env->cleanwin - env->canrestore == 0) {
/* XXX Clean windows without trap */
cpu_raise_exception_ra(env, TT_CLRWIN, GETPC());
} else {
env->cansave--;
env->canrestore++;
cpu_set_cwp(env, cwp);
}
}
}
void helper_restore(CPUSPARCState *env)
{
uint32_t cwp;
cwp = cpu_cwp_inc(env, env->cwp + 1);
if (env->canrestore == 0) {
int tt = TT_FILL | (env->otherwin != 0
? (TT_WOTHER | ((env->wstate & 0x38) >> 1))
: ((env->wstate & 0x7) << 2));
cpu_raise_exception_ra(env, tt, GETPC());
} else {
env->cansave++;
env->canrestore--;
cpu_set_cwp(env, cwp);
}
}
void helper_flushw(CPUSPARCState *env)
{
if (env->cansave != env->nwindows - 2) {
int tt = TT_SPILL | (env->otherwin != 0
? (TT_WOTHER | ((env->wstate & 0x38) >> 1))
: ((env->wstate & 0x7) << 2));
cpu_raise_exception_ra(env, tt, GETPC());
}
}
void helper_saved(CPUSPARCState *env)
{
env->cansave++;
if (env->otherwin == 0) {
env->canrestore--;
} else {
env->otherwin--;
}
}
void helper_restored(CPUSPARCState *env)
{
env->canrestore++;
if (env->cleanwin < env->nwindows - 1) {
env->cleanwin++;
}
if (env->otherwin == 0) {
env->cansave--;
} else {
env->otherwin--;
}
}
target_ulong cpu_get_ccr(CPUSPARCState *env)
{
target_ulong psr;
psr = cpu_get_psr(env);
return ((env->xcc >> 20) << 4) | ((psr & PSR_ICC) >> 20);
}
void cpu_put_ccr(CPUSPARCState *env, target_ulong val)
{
env->xcc = (val >> 4) << 20;
env->psr = (val & 0xf) << 20;
CC_OP = CC_OP_FLAGS;
}
target_ulong cpu_get_cwp64(CPUSPARCState *env)
{
return env->nwindows - 1 - env->cwp;
}
void cpu_put_cwp64(CPUSPARCState *env, int cwp)
{
if (unlikely(cwp >= env->nwindows || cwp < 0)) {
cwp %= env->nwindows;
}
cpu_set_cwp(env, env->nwindows - 1 - cwp);
}
target_ulong helper_rdccr(CPUSPARCState *env)
{
return cpu_get_ccr(env);
}
void helper_wrccr(CPUSPARCState *env, target_ulong new_ccr)
{
cpu_put_ccr(env, new_ccr);
}
/* CWP handling is reversed in V9, but we still use the V8 register
order. */
target_ulong helper_rdcwp(CPUSPARCState *env)
{
return cpu_get_cwp64(env);
}
void helper_wrcwp(CPUSPARCState *env, target_ulong new_cwp)
{
cpu_put_cwp64(env, new_cwp);
}
static inline uint64_t *get_gregset(CPUSPARCState *env, uint32_t pstate)
{
switch (pstate) {
default:
trace_win_helper_gregset_error(pstate);
/* pass through to normal set of global registers */
case 0:
return env->bgregs;
case PS_AG:
return env->agregs;
case PS_MG:
return env->mgregs;
case PS_IG:
return env->igregs;
}
}
void cpu_change_pstate(CPUSPARCState *env, uint32_t new_pstate)
{
uint32_t pstate_regs, new_pstate_regs;
uint64_t *src, *dst;
if (env->def->features & CPU_FEATURE_GL) {
/* PS_AG is not implemented in this case */
new_pstate &= ~PS_AG;
}
pstate_regs = env->pstate & 0xc01;
new_pstate_regs = new_pstate & 0xc01;
if (new_pstate_regs != pstate_regs) {
trace_win_helper_switch_pstate(pstate_regs, new_pstate_regs);
/* Switch global register bank */
src = get_gregset(env, new_pstate_regs);
dst = get_gregset(env, pstate_regs);
memcpy32(dst, env->gregs);
memcpy32(env->gregs, src);
} else {
trace_win_helper_no_switch_pstate(new_pstate_regs);
}
env->pstate = new_pstate;
}
void helper_wrpstate(CPUSPARCState *env, target_ulong new_state)
{
cpu_change_pstate(env, new_state & 0xf3f);
#if !defined(CONFIG_USER_ONLY)
if (cpu_interrupts_enabled(env)) {
cpu_check_irqs(env);
}
#endif
}
void helper_wrpil(CPUSPARCState *env, target_ulong new_pil)
{
#if !defined(CONFIG_USER_ONLY)
trace_win_helper_wrpil(env->psrpil, (uint32_t)new_pil);
env->psrpil = new_pil;
if (cpu_interrupts_enabled(env)) {
cpu_check_irqs(env);
}
#endif
}
void helper_done(CPUSPARCState *env)
{
trap_state *tsptr = cpu_tsptr(env);
env->pc = tsptr->tnpc;
env->npc = tsptr->tnpc + 4;
cpu_put_ccr(env, tsptr->tstate >> 32);
env->asi = (tsptr->tstate >> 24) & 0xff;
cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f);
cpu_put_cwp64(env, tsptr->tstate & 0xff);
env->tl--;
trace_win_helper_done(env->tl);
#if !defined(CONFIG_USER_ONLY)
if (cpu_interrupts_enabled(env)) {
cpu_check_irqs(env);
}
#endif
}
void helper_retry(CPUSPARCState *env)
{
trap_state *tsptr = cpu_tsptr(env);
env->pc = tsptr->tpc;
env->npc = tsptr->tnpc;
cpu_put_ccr(env, tsptr->tstate >> 32);
env->asi = (tsptr->tstate >> 24) & 0xff;
cpu_change_pstate(env, (tsptr->tstate >> 8) & 0xf3f);
cpu_put_cwp64(env, tsptr->tstate & 0xff);
env->tl--;
trace_win_helper_retry(env->tl);
#if !defined(CONFIG_USER_ONLY)
if (cpu_interrupts_enabled(env)) {
cpu_check_irqs(env);
}
#endif
}
#endif