aboutsummaryrefslogtreecommitdiff
path: root/target/sparc
diff options
context:
space:
mode:
Diffstat (limited to 'target/sparc')
-rw-r--r--target/sparc/cpu-param.h27
-rw-r--r--target/sparc/cpu.c152
-rw-r--r--target/sparc/cpu.h74
-rw-r--r--target/sparc/fop_helper.c32
-rw-r--r--target/sparc/gdbstub.c18
-rw-r--r--target/sparc/helper.c1
-rw-r--r--target/sparc/helper.h8
-rw-r--r--target/sparc/insns.decode25
-rw-r--r--target/sparc/int32_helper.c42
-rw-r--r--target/sparc/ldst_helper.c15
-rw-r--r--target/sparc/machine.c26
-rw-r--r--target/sparc/mmu_helper.c8
-rw-r--r--target/sparc/translate.c274
-rw-r--r--target/sparc/translate.h17
-rw-r--r--target/sparc/win_helper.c27
15 files changed, 487 insertions, 259 deletions
diff --git a/target/sparc/cpu-param.h b/target/sparc/cpu-param.h
index 82293fb..45eea9d 100644
--- a/target/sparc/cpu-param.h
+++ b/target/sparc/cpu-param.h
@@ -1,14 +1,13 @@
/*
* Sparc cpu parameters for qemu.
*
- * SPDX-License-Identifier: LGPL-2.0+
+ * SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef SPARC_CPU_PARAM_H
#define SPARC_CPU_PARAM_H
#ifdef TARGET_SPARC64
-# define TARGET_LONG_BITS 64
# define TARGET_PAGE_BITS 13 /* 8k */
# define TARGET_PHYS_ADDR_SPACE_BITS 41
# ifdef TARGET_ABI32
@@ -17,33 +16,11 @@
# define TARGET_VIRT_ADDR_SPACE_BITS 44
# endif
#else
-# define TARGET_LONG_BITS 32
# define TARGET_PAGE_BITS 12 /* 4k */
# define TARGET_PHYS_ADDR_SPACE_BITS 36
# define TARGET_VIRT_ADDR_SPACE_BITS 32
#endif
-/*
- * From Oracle SPARC Architecture 2015:
- *
- * Compatibility notes: The PSO memory model described in SPARC V8 and
- * SPARC V9 compatibility architecture specifications was never implemented
- * in a SPARC V9 implementation and is not included in the Oracle SPARC
- * Architecture specification.
- *
- * The RMO memory model described in the SPARC V9 specification was
- * implemented in some non-Sun SPARC V9 implementations, but is not
- * directly supported in Oracle SPARC Architecture 2015 implementations.
- *
- * Therefore always use TSO in QEMU.
- *
- * D.5 Specification of Partial Store Order (PSO)
- * ... [loads] are followed by an implied MEMBAR #LoadLoad | #LoadStore.
- *
- * D.6 Specification of Total Store Order (TSO)
- * ... PSO with the additional requirement that all [stores] are followed
- * by an implied MEMBAR #StoreStore.
- */
-#define TCG_GUEST_DEFAULT_MO (TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST)
+#define TARGET_INSN_START_EXTRA_WORDS 1
#endif
diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c
index 54cb269..ed7701b 100644
--- a/target/sparc/cpu.c
+++ b/target/sparc/cpu.c
@@ -22,10 +22,13 @@
#include "cpu.h"
#include "qemu/module.h"
#include "qemu/qemu-print.h"
-#include "exec/exec-all.h"
+#include "accel/tcg/cpu-mmu-index.h"
+#include "exec/translation-block.h"
#include "hw/qdev-properties.h"
#include "qapi/visitor.h"
#include "tcg/tcg.h"
+#include "fpu/softfloat.h"
+#include "target/sparc/translate.h"
//#define DEBUG_FEATURES
@@ -76,6 +79,7 @@ static void sparc_cpu_reset_hold(Object *obj, ResetType type)
env->npc = env->pc + 4;
#endif
env->cache_control = 0;
+ cpu_put_fsr(env, 0);
}
#ifndef CONFIG_USER_ONLY
@@ -102,6 +106,7 @@ static bool sparc_cpu_exec_interrupt(CPUState *cs, int interrupt_request)
static void cpu_sparc_disas_set_info(CPUState *cpu, disassemble_info *info)
{
info->print_insn = print_insn_sparc;
+ info->endian = BFD_ENDIAN_BIG;
#ifdef TARGET_SPARC64
info->mach = bfd_mach_sparc_v9b;
#endif
@@ -574,7 +579,7 @@ static void print_features(uint32_t features, const char *prefix)
}
}
-void sparc_cpu_list(void)
+static void sparc_cpu_list(void)
{
unsigned int i;
@@ -711,11 +716,77 @@ static void sparc_cpu_synchronize_from_tb(CPUState *cs,
cpu->env.npc = tb->cs_base;
}
+static TCGTBCPUState sparc_get_tb_cpu_state(CPUState *cs)
+{
+ CPUSPARCState *env = cpu_env(cs);
+ uint32_t flags = cpu_mmu_index(cs, false);
+
+#ifndef CONFIG_USER_ONLY
+ if (cpu_supervisor_mode(env)) {
+ flags |= TB_FLAG_SUPER;
+ }
+#endif
+#ifdef TARGET_SPARC64
+#ifndef CONFIG_USER_ONLY
+ if (cpu_hypervisor_mode(env)) {
+ flags |= TB_FLAG_HYPER;
+ }
+#endif
+ if (env->pstate & PS_AM) {
+ flags |= TB_FLAG_AM_ENABLED;
+ }
+ if ((env->pstate & PS_PEF) && (env->fprs & FPRS_FEF)) {
+ flags |= TB_FLAG_FPU_ENABLED;
+ }
+ flags |= env->asi << TB_FLAG_ASI_SHIFT;
+#else
+ if (env->psref) {
+ flags |= TB_FLAG_FPU_ENABLED;
+ }
+#ifndef CONFIG_USER_ONLY
+ if (env->fsr_qne) {
+ flags |= TB_FLAG_FSR_QNE;
+ }
+#endif /* !CONFIG_USER_ONLY */
+#endif /* TARGET_SPARC64 */
+
+ return (TCGTBCPUState){
+ .pc = env->pc,
+ .flags = flags,
+ .cs_base = env->npc,
+ };
+}
+
+static void sparc_restore_state_to_opc(CPUState *cs,
+ const TranslationBlock *tb,
+ const uint64_t *data)
+{
+ CPUSPARCState *env = cpu_env(cs);
+ target_ulong pc = data[0];
+ target_ulong npc = data[1];
+
+ env->pc = pc;
+ if (npc == DYNAMIC_PC) {
+ /* dynamic NPC: already stored */
+ } else if (npc & JUMP_PC) {
+ /* jump PC: use 'cond' and the jump targets of the translation */
+ if (env->cond) {
+ env->npc = npc & ~3;
+ } else {
+ env->npc = pc + 4;
+ }
+ } else {
+ env->npc = npc;
+ }
+}
+
+#ifndef CONFIG_USER_ONLY
static bool sparc_cpu_has_work(CPUState *cs)
{
return (cs->interrupt_request & CPU_INTERRUPT_HARD) &&
cpu_interrupts_enabled(cpu_env(cs));
}
+#endif /* !CONFIG_USER_ONLY */
static int sparc_cpu_mmu_index(CPUState *cs, bool ifetch)
{
@@ -805,7 +876,19 @@ static void sparc_cpu_realizefn(DeviceState *dev, Error **errp)
env->version |= env->def.maxtl << 8;
env->version |= env->def.nwindows - 1;
#endif
- cpu_put_fsr(env, 0);
+
+ /*
+ * Prefer SNaN over QNaN, order B then A. It's OK to do this in realize
+ * rather than reset, because fp_status is after 'end_reset_fields' in
+ * the CPU state struct so it won't get zeroed on reset.
+ */
+ set_float_2nan_prop_rule(float_2nan_prop_s_ba, &env->fp_status);
+ /* For fused-multiply add, prefer SNaN over QNaN, then C->B->A */
+ set_float_3nan_prop_rule(float_3nan_prop_s_cba, &env->fp_status);
+ /* For inf * 0 + NaN, return the input NaN */
+ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status);
+ /* Default NaN value: sign bit clear, all frac bits set */
+ set_float_default_nan_pattern(0b01111111, &env->fp_status);
cpu_exec_realizefn(cs, &local_err);
if (local_err != NULL) {
@@ -860,14 +943,15 @@ static void sparc_set_nwindows(Object *obj, Visitor *v, const char *name,
cpu->env.def.nwindows = value;
}
-static PropertyInfo qdev_prop_nwindows = {
- .name = "int",
+static const PropertyInfo qdev_prop_nwindows = {
+ .type = "int",
+ .description = "Number of register windows",
.get = sparc_get_nwindows,
.set = sparc_set_nwindows,
};
/* This must match feature_name[]. */
-static Property sparc_cpu_properties[] = {
+static const Property sparc_cpu_properties[] = {
DEFINE_PROP_BIT("float128", SPARCCPU, env.def.features,
CPU_FEATURE_BIT_FLOAT128, false),
#ifdef TARGET_SPARC64
@@ -903,30 +987,71 @@ static Property sparc_cpu_properties[] = {
DEFINE_PROP_UINT32("mmu-version", SPARCCPU, env.def.mmu_version, 0),
DEFINE_PROP("nwindows", SPARCCPU, env.def.nwindows,
qdev_prop_nwindows, uint32_t),
- DEFINE_PROP_END_OF_LIST()
};
#ifndef CONFIG_USER_ONLY
#include "hw/core/sysemu-cpu-ops.h"
static const struct SysemuCPUOps sparc_sysemu_ops = {
+ .has_work = sparc_cpu_has_work,
.get_phys_page_debug = sparc_cpu_get_phys_page_debug,
.legacy_vmsd = &vmstate_sparc_cpu,
};
#endif
#ifdef CONFIG_TCG
-#include "hw/core/tcg-cpu-ops.h"
+#include "accel/tcg/cpu-ops.h"
+
+#ifndef CONFIG_USER_ONLY
+static vaddr sparc_pointer_wrap(CPUState *cs, int mmu_idx,
+ vaddr result, vaddr base)
+{
+#ifdef TARGET_SPARC64
+ return cpu_env(cs)->pstate & PS_AM ? (uint32_t)result : result;
+#else
+ return (uint32_t)result;
+#endif
+}
+#endif
static const TCGCPUOps sparc_tcg_ops = {
+ /*
+ * From Oracle SPARC Architecture 2015:
+ *
+ * Compatibility notes: The PSO memory model described in SPARC V8 and
+ * SPARC V9 compatibility architecture specifications was never
+ * implemented in a SPARC V9 implementation and is not included in the
+ * Oracle SPARC Architecture specification.
+ *
+ * The RMO memory model described in the SPARC V9 specification was
+ * implemented in some non-Sun SPARC V9 implementations, but is not
+ * directly supported in Oracle SPARC Architecture 2015 implementations.
+ *
+ * Therefore always use TSO in QEMU.
+ *
+ * D.5 Specification of Partial Store Order (PSO)
+ * ... [loads] are followed by an implied MEMBAR #LoadLoad | #LoadStore.
+ *
+ * D.6 Specification of Total Store Order (TSO)
+ * ... PSO with the additional requirement that all [stores] are followed
+ * by an implied MEMBAR #StoreStore.
+ */
+ .guest_default_memory_order = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST,
+ .mttcg_supported = true,
+
.initialize = sparc_tcg_init,
+ .translate_code = sparc_translate_code,
+ .get_tb_cpu_state = sparc_get_tb_cpu_state,
.synchronize_from_tb = sparc_cpu_synchronize_from_tb,
.restore_state_to_opc = sparc_restore_state_to_opc,
+ .mmu_index = sparc_cpu_mmu_index,
#ifndef CONFIG_USER_ONLY
.tlb_fill = sparc_cpu_tlb_fill,
+ .pointer_wrap = sparc_pointer_wrap,
.cpu_exec_interrupt = sparc_cpu_exec_interrupt,
.cpu_exec_halt = sparc_cpu_has_work,
+ .cpu_exec_reset = cpu_reset,
.do_interrupt = sparc_cpu_do_interrupt,
.do_transaction_failed = sparc_cpu_do_transaction_failed,
.do_unaligned_access = sparc_cpu_do_unaligned_access,
@@ -934,7 +1059,7 @@ static const TCGCPUOps sparc_tcg_ops = {
};
#endif /* CONFIG_TCG */
-static void sparc_cpu_class_init(ObjectClass *oc, void *data)
+static void sparc_cpu_class_init(ObjectClass *oc, const void *data)
{
SPARCCPUClass *scc = SPARC_CPU_CLASS(oc);
CPUClass *cc = CPU_CLASS(oc);
@@ -949,9 +1074,8 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data)
&scc->parent_phases);
cc->class_by_name = sparc_cpu_class_by_name;
+ cc->list_cpus = sparc_cpu_list,
cc->parse_features = sparc_cpu_parse_features;
- cc->has_work = sparc_cpu_has_work;
- cc->mmu_index = sparc_cpu_mmu_index;
cc->dump_state = sparc_cpu_dump_state;
#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
cc->memory_rw_debug = sparc_cpu_memory_rw_debug;
@@ -984,7 +1108,7 @@ static const TypeInfo sparc_cpu_type_info = {
.class_init = sparc_cpu_class_init,
};
-static void sparc_cpu_cpudef_class_init(ObjectClass *oc, void *data)
+static void sparc_cpu_cpudef_class_init(ObjectClass *oc, const void *data)
{
SPARCCPUClass *scc = SPARC_CPU_CLASS(oc);
scc->cpu_def = data;
@@ -997,10 +1121,10 @@ static void sparc_register_cpudef_type(const struct sparc_def_t *def)
.name = typename,
.parent = TYPE_SPARC_CPU,
.class_init = sparc_cpu_cpudef_class_init,
- .class_data = (void *)def,
+ .class_data = def,
};
- type_register(&ti);
+ type_register_static(&ti);
g_free(typename);
}
diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h
index dfd9512..31cb3d9 100644
--- a/target/sparc/cpu.h
+++ b/target/sparc/cpu.h
@@ -3,7 +3,9 @@
#include "qemu/bswap.h"
#include "cpu-qom.h"
+#include "exec/cpu-common.h"
#include "exec/cpu-defs.h"
+#include "exec/cpu-interrupt.h"
#include "qemu/cpu-float.h"
#if !defined(TARGET_SPARC64)
@@ -184,6 +186,8 @@ enum {
#define FSR_FTT_SEQ_ERROR (4ULL << 14)
#define FSR_FTT_INVAL_FPR (6ULL << 14)
+#define FSR_QNE (1ULL << 13)
+
#define FSR_FCC0_SHIFT 10
#define FSR_FCC1_SHIFT 32
#define FSR_FCC2_SHIFT 34
@@ -219,7 +223,6 @@ typedef struct trap_state {
uint32_t tt;
} trap_state;
#endif
-#define TARGET_INSN_START_EXTRA_WORDS 1
typedef struct sparc_def_t {
const char *name;
@@ -438,6 +441,26 @@ struct CPUArchState {
uint32_t fsr_cexc_ftt; /* cexc, ftt */
uint32_t fcc[TARGET_FCCREGS]; /* fcc* */
+#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
+ /*
+ * Single-element FPU fault queue, with address and insn,
+ * packaged into the double-word with which it is stored.
+ */
+ uint32_t fsr_qne; /* qne */
+ union {
+ uint64_t d;
+ struct {
+#if HOST_BIG_ENDIAN
+ uint32_t addr;
+ uint32_t insn;
+#else
+ uint32_t insn;
+ uint32_t addr;
+#endif
+ } s;
+ } fq;
+#endif
+
CPU_DoubleU fpr[TARGET_DPREGS]; /* floating point registers */
uint32_t cwp; /* index of current register window (extracted
from PSR) */
@@ -552,7 +575,7 @@ struct SPARCCPUClass {
DeviceRealize parent_realize;
ResettablePhases parent_phases;
- sparc_def_t *cpu_def;
+ const sparc_def_t *cpu_def;
};
#ifndef CONFIG_USER_ONLY
@@ -572,7 +595,6 @@ G_NORETURN void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t);
/* cpu_init.c */
void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu);
-void sparc_cpu_list(void);
/* mmu_helper.c */
bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
MMUAccessType access_type, int mmu_idx,
@@ -582,15 +604,13 @@ void dump_mmu(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);
+ uint8_t *buf, size_t len, bool is_write);
#endif
-
/* translate.c */
void sparc_tcg_init(void);
-void sparc_restore_state_to_opc(CPUState *cs,
- const TranslationBlock *tb,
- const uint64_t *data);
+void sparc_translate_code(CPUState *cs, TranslationBlock *tb,
+ int *max_insns, vaddr pc, void *host_pc);
/* fop_helper.c */
target_ulong cpu_get_fsr(CPUSPARCState *);
@@ -645,8 +665,6 @@ hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr,
#define CPU_RESOLVING_TYPE TYPE_SPARC_CPU
-#define cpu_list sparc_cpu_list
-
/* MMU modes definitions */
#if defined (TARGET_SPARC64)
#define MMU_USER_IDX 0
@@ -707,8 +725,6 @@ static inline int cpu_pil_allowed(CPUSPARCState *env1, int pil)
#endif
}
-#include "exec/cpu-all.h"
-
#ifdef TARGET_SPARC64
/* sun4u.c */
void cpu_tick_set_count(CPUTimer *timer, uint64_t count);
@@ -722,41 +738,9 @@ trap_state* cpu_tsptr(CPUSPARCState* env);
#define TB_FLAG_AM_ENABLED (1 << 5)
#define TB_FLAG_SUPER (1 << 6)
#define TB_FLAG_HYPER (1 << 7)
+#define TB_FLAG_FSR_QNE (1 << 8)
#define TB_FLAG_ASI_SHIFT 24
-static inline void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc,
- uint64_t *cs_base, uint32_t *pflags)
-{
- uint32_t flags;
- *pc = env->pc;
- *cs_base = env->npc;
- flags = cpu_mmu_index(env_cpu(env), false);
-#ifndef CONFIG_USER_ONLY
- if (cpu_supervisor_mode(env)) {
- flags |= TB_FLAG_SUPER;
- }
-#endif
-#ifdef TARGET_SPARC64
-#ifndef CONFIG_USER_ONLY
- if (cpu_hypervisor_mode(env)) {
- flags |= TB_FLAG_HYPER;
- }
-#endif
- if (env->pstate & PS_AM) {
- flags |= TB_FLAG_AM_ENABLED;
- }
- if ((env->pstate & PS_PEF) && (env->fprs & FPRS_FEF)) {
- flags |= TB_FLAG_FPU_ENABLED;
- }
- flags |= env->asi << TB_FLAG_ASI_SHIFT;
-#else
- if (env->psref) {
- flags |= TB_FLAG_FPU_ENABLED;
- }
-#endif
- *pflags = flags;
-}
-
static inline bool tb_fpu_enabled(int tb_flags)
{
#if defined(CONFIG_USER_ONLY)
diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c
index 0b30665..29fd166 100644
--- a/target/sparc/fop_helper.c
+++ b/target/sparc/fop_helper.c
@@ -19,7 +19,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "fpu/softfloat.h"
@@ -344,17 +343,17 @@ Int128 helper_fsqrtq(CPUSPARCState *env, Int128 src)
}
float32 helper_fmadds(CPUSPARCState *env, float32 s1,
- float32 s2, float32 s3, uint32_t op)
+ float32 s2, float32 s3, int32_t sc, uint32_t op)
{
- float32 ret = float32_muladd(s1, s2, s3, op, &env->fp_status);
+ float32 ret = float32_muladd_scalbn(s1, s2, s3, sc, op, &env->fp_status);
check_ieee_exceptions(env, GETPC());
return ret;
}
float64 helper_fmaddd(CPUSPARCState *env, float64 s1,
- float64 s2, float64 s3, uint32_t op)
+ float64 s2, float64 s3, int32_t sc, uint32_t op)
{
- float64 ret = float64_muladd(s1, s2, s3, op, &env->fp_status);
+ float64 ret = float64_muladd_scalbn(s1, s2, s3, sc, op, &env->fp_status);
check_ieee_exceptions(env, GETPC());
return ret;
}
@@ -446,7 +445,6 @@ static uint32_t finish_fcmp(CPUSPARCState *env, FloatRelation r, uintptr_t ra)
case float_relation_greater:
return 2;
case float_relation_unordered:
- env->fsr |= FSR_NVA;
return 3;
}
g_assert_not_reached();
@@ -490,14 +488,17 @@ uint32_t helper_fcmpeq(CPUSPARCState *env, Int128 src1, Int128 src2)
return finish_fcmp(env, r, GETPC());
}
-uint32_t helper_flcmps(float32 src1, float32 src2)
+uint32_t helper_flcmps(CPUSPARCState *env, float32 src1, float32 src2)
{
/*
* FLCMP never raises an exception nor modifies any FSR fields.
* Perform the comparison with a dummy fp environment.
*/
- float_status discard = { };
- FloatRelation r = float32_compare_quiet(src1, src2, &discard);
+ float_status discard = env->fp_status;
+ FloatRelation r;
+
+ set_float_2nan_prop_rule(float_2nan_prop_s_ba, &discard);
+ r = float32_compare_quiet(src1, src2, &discard);
switch (r) {
case float_relation_equal:
@@ -515,10 +516,13 @@ uint32_t helper_flcmps(float32 src1, float32 src2)
g_assert_not_reached();
}
-uint32_t helper_flcmpd(float64 src1, float64 src2)
+uint32_t helper_flcmpd(CPUSPARCState *env, float64 src1, float64 src2)
{
- float_status discard = { };
- FloatRelation r = float64_compare_quiet(src1, src2, &discard);
+ float_status discard = env->fp_status;
+ FloatRelation r;
+
+ set_float_2nan_prop_rule(float_2nan_prop_s_ba, &discard);
+ r = float64_compare_quiet(src1, src2, &discard);
switch (r) {
case float_relation_equal:
@@ -545,6 +549,8 @@ target_ulong cpu_get_fsr(CPUSPARCState *env)
fsr |= (uint64_t)env->fcc[1] << FSR_FCC1_SHIFT;
fsr |= (uint64_t)env->fcc[2] << FSR_FCC2_SHIFT;
fsr |= (uint64_t)env->fcc[3] << FSR_FCC3_SHIFT;
+#elif !defined(CONFIG_USER_ONLY)
+ fsr |= env->fsr_qne;
#endif
/* VER is kept completely separate until re-assembly. */
@@ -591,6 +597,8 @@ void cpu_put_fsr(CPUSPARCState *env, target_ulong fsr)
env->fcc[1] = extract64(fsr, FSR_FCC1_SHIFT, 2);
env->fcc[2] = extract64(fsr, FSR_FCC2_SHIFT, 2);
env->fcc[3] = extract64(fsr, FSR_FCC3_SHIFT, 2);
+#elif !defined(CONFIG_USER_ONLY)
+ env->fsr_qne = fsr & FSR_QNE;
#endif
set_fsr_nonsplit(env, fsr);
diff --git a/target/sparc/gdbstub.c b/target/sparc/gdbstub.c
index ec0036e..134617f 100644
--- a/target/sparc/gdbstub.c
+++ b/target/sparc/gdbstub.c
@@ -79,8 +79,13 @@ int sparc_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
}
}
if (n < 80) {
- /* f32-f62 (double width, even numbers only) */
- return gdb_get_reg64(mem_buf, env->fpr[(n - 32) / 2].ll);
+ /* f32-f62 (16 double width registers, even register numbers only)
+ * n == 64: f32 : env->fpr[16]
+ * n == 65: f34 : env->fpr[17]
+ * etc...
+ * n == 79: f62 : env->fpr[31]
+ */
+ return gdb_get_reg64(mem_buf, env->fpr[(n - 64) + 16].ll);
}
switch (n) {
case 80:
@@ -173,8 +178,13 @@ int sparc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n)
}
return 4;
} else if (n < 80) {
- /* f32-f62 (double width, even numbers only) */
- env->fpr[(n - 32) / 2].ll = tmp;
+ /* f32-f62 (16 double width registers, even register numbers only)
+ * n == 64: f32 : env->fpr[16]
+ * n == 65: f34 : env->fpr[17]
+ * etc...
+ * n == 79: f62 : env->fpr[31]
+ */
+ env->fpr[(n - 64) + 16].ll = tmp;
} else {
switch (n) {
case 80:
diff --git a/target/sparc/helper.c b/target/sparc/helper.c
index 7846ddd..9163b9d 100644
--- a/target/sparc/helper.c
+++ b/target/sparc/helper.c
@@ -19,7 +19,6 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/exec-all.h"
#include "qemu/timer.h"
#include "qemu/host-utils.h"
#include "exec/helper-proto.h"
diff --git a/target/sparc/helper.h b/target/sparc/helper.h
index 134e519..3a7f7dc 100644
--- a/target/sparc/helper.h
+++ b/target/sparc/helper.h
@@ -51,15 +51,15 @@ DEF_HELPER_FLAGS_3(fcmpd, TCG_CALL_NO_WG, i32, env, f64, f64)
DEF_HELPER_FLAGS_3(fcmped, TCG_CALL_NO_WG, i32, env, f64, f64)
DEF_HELPER_FLAGS_3(fcmpq, TCG_CALL_NO_WG, i32, env, i128, i128)
DEF_HELPER_FLAGS_3(fcmpeq, TCG_CALL_NO_WG, i32, env, i128, i128)
-DEF_HELPER_FLAGS_2(flcmps, TCG_CALL_NO_RWG_SE, i32, f32, f32)
-DEF_HELPER_FLAGS_2(flcmpd, TCG_CALL_NO_RWG_SE, i32, f64, f64)
+DEF_HELPER_FLAGS_3(flcmps, TCG_CALL_NO_RWG_SE, i32, env, f32, f32)
+DEF_HELPER_FLAGS_3(flcmpd, TCG_CALL_NO_RWG_SE, i32, env, f64, f64)
DEF_HELPER_2(raise_exception, noreturn, env, int)
DEF_HELPER_FLAGS_3(faddd, TCG_CALL_NO_WG, f64, env, f64, f64)
DEF_HELPER_FLAGS_3(fsubd, TCG_CALL_NO_WG, f64, env, f64, f64)
DEF_HELPER_FLAGS_3(fmuld, TCG_CALL_NO_WG, f64, env, f64, f64)
DEF_HELPER_FLAGS_3(fdivd, TCG_CALL_NO_WG, f64, env, f64, f64)
-DEF_HELPER_FLAGS_5(fmaddd, TCG_CALL_NO_WG, f64, env, f64, f64, f64, i32)
+DEF_HELPER_FLAGS_6(fmaddd, TCG_CALL_NO_WG, f64, env, f64, f64, f64, s32, i32)
DEF_HELPER_FLAGS_3(fnaddd, TCG_CALL_NO_WG, f64, env, f64, f64)
DEF_HELPER_FLAGS_3(fnmuld, TCG_CALL_NO_WG, f64, env, f64, f64)
@@ -72,7 +72,7 @@ DEF_HELPER_FLAGS_3(fadds, TCG_CALL_NO_WG, f32, env, f32, f32)
DEF_HELPER_FLAGS_3(fsubs, TCG_CALL_NO_WG, f32, env, f32, f32)
DEF_HELPER_FLAGS_3(fmuls, TCG_CALL_NO_WG, f32, env, f32, f32)
DEF_HELPER_FLAGS_3(fdivs, TCG_CALL_NO_WG, f32, env, f32, f32)
-DEF_HELPER_FLAGS_5(fmadds, TCG_CALL_NO_WG, f32, env, f32, f32, f32, i32)
+DEF_HELPER_FLAGS_6(fmadds, TCG_CALL_NO_WG, f32, env, f32, f32, f32, s32, i32)
DEF_HELPER_FLAGS_3(fnadds, TCG_CALL_NO_WG, f32, env, f32, f32)
DEF_HELPER_FLAGS_3(fnmuls, TCG_CALL_NO_WG, f32, env, f32, f32)
diff --git a/target/sparc/insns.decode b/target/sparc/insns.decode
index fbcb4f7..9e39d23 100644
--- a/target/sparc/insns.decode
+++ b/target/sparc/insns.decode
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: LGPL-2.0+
+# SPDX-License-Identifier: LGPL-2.0-or-later
#
# Sparc instruction decode definitions.
# Copyright (c) 2023 Richard Henderson <rth@twiddle.net>
@@ -96,7 +96,10 @@ CALL 01 i:s30
RDTICK 10 rd:5 101000 00100 0 0000000000000
RDPC 10 rd:5 101000 00101 0 0000000000000
RDFPRS 10 rd:5 101000 00110 0 0000000000000
- RDASR17 10 rd:5 101000 10001 0 0000000000000
+ {
+ RDASR17 10 rd:5 101000 10001 0 0000000000000
+ RDPIC 10 rd:5 101000 10001 0 0000000000000
+ }
RDGSR 10 rd:5 101000 10011 0 0000000000000
RDSOFTINT 10 rd:5 101000 10110 0 0000000000000
RDTICK_CMPR 10 rd:5 101000 10111 0 0000000000000
@@ -114,6 +117,8 @@ CALL 01 i:s30
WRCCR 10 00010 110000 ..... . ............. @n_r_ri
WRASI 10 00011 110000 ..... . ............. @n_r_ri
WRFPRS 10 00110 110000 ..... . ............. @n_r_ri
+ WRPCR 10 10000 110000 01000 0 0000000000000
+ WRPIC 10 10001 110000 01000 0 0000000000000
{
WRGSR 10 10011 110000 ..... . ............. @n_r_ri
WRPOWERDOWN 10 10011 110000 ..... . ............. @n_r_ri
@@ -321,12 +326,12 @@ FdMULq 10 ..... 110100 ..... 0 0110 1110 ..... @q_d_d
FNHADDs 10 ..... 110100 ..... 0 0111 0001 ..... @r_r_r
FNHADDd 10 ..... 110100 ..... 0 0111 0010 ..... @d_d_d
FNsMULd 10 ..... 110100 ..... 0 0111 1001 ..... @d_r_r
-FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @r_r2
-FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @r_d2
-FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @r_q2
-FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_r2
-FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @d_r2
-FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @q_r2
+FsTOx 10 ..... 110100 00000 0 1000 0001 ..... @d_r2
+FdTOx 10 ..... 110100 00000 0 1000 0010 ..... @d_d2
+FqTOx 10 ..... 110100 00000 0 1000 0011 ..... @d_q2
+FxTOs 10 ..... 110100 00000 0 1000 0100 ..... @r_d2
+FxTOd 10 ..... 110100 00000 0 1000 1000 ..... @d_d2
+FxTOq 10 ..... 110100 00000 0 1000 1100 ..... @q_d2
FiTOs 10 ..... 110100 00000 0 1100 0100 ..... @r_r2
FdTOs 10 ..... 110100 00000 0 1100 0110 ..... @r_d2
FqTOs 10 ..... 110100 00000 0 1100 0111 ..... @r_q2
@@ -644,8 +649,8 @@ STF 11 ..... 100100 ..... . ............. @r_r_ri_na
STFSR 11 00000 100101 ..... . ............. @n_r_ri
STXFSR 11 00001 100101 ..... . ............. @n_r_ri
{
- STQF 11 ..... 100110 ..... . ............. @q_r_ri_na
- STDFQ 11 ----- 100110 ----- - -------------
+ STQF 11 ..... 100110 ..... . ............. @q_r_ri_na # v9
+ STDFQ 11 ..... 100110 ..... . ............. @r_r_ri # v7,v8
}
STDF 11 ..... 100111 ..... . ............. @d_r_ri_na
diff --git a/target/sparc/int32_helper.c b/target/sparc/int32_helper.c
index 6b7d65b..39db4ff 100644
--- a/target/sparc/int32_helper.c
+++ b/target/sparc/int32_helper.c
@@ -21,9 +21,9 @@
#include "qemu/main-loop.h"
#include "cpu.h"
#include "trace.h"
+#include "accel/tcg/cpu-ldst.h"
#include "exec/log.h"
-#include "sysemu/runstate.h"
-
+#include "system/runstate.h"
static const char * const excp_names[0x80] = {
[TT_TFAULT] = "Instruction Access Fault",
@@ -116,22 +116,9 @@ void sparc_cpu_do_interrupt(CPUState *cs)
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++;
}
-#if !defined(CONFIG_USER_ONLY)
+#ifndef CONFIG_USER_ONLY
if (env->psret == 0) {
if (cs->exception_index == 0x80 &&
env->def.features & CPU_FEATURE_TA0_SHUTDOWN) {
@@ -143,6 +130,29 @@ void sparc_cpu_do_interrupt(CPUState *cs)
}
return;
}
+ if (intno == TT_FP_EXCP) {
+ /*
+ * The sparc32 fpu has three states related to exception handling.
+ * The FPop that signals an exception transitions from fp_execute
+ * to fp_exception_pending. A subsequent FPop transitions from
+ * fp_exception_pending to fp_exception, which forces the trap.
+ *
+ * If the queue is not empty, this trap is due to execution of an
+ * illegal FPop while in fp_exception state. Here we are to
+ * re-enter fp_exception_pending state without queuing the insn.
+ *
+ * We do not model the fp_exception_pending state, but instead
+ * skip directly to fp_exception state. We advance pc/npc to
+ * mimic delayed trap delivery as if by the subsequent insn.
+ */
+ if (!env->fsr_qne) {
+ env->fsr_qne = FSR_QNE;
+ env->fq.s.addr = env->pc;
+ env->fq.s.insn = cpu_ldl_code(env, env->pc);
+ }
+ env->pc = env->npc;
+ env->npc = env->npc + 4;
+ }
#endif
env->psret = 0;
cwp = cpu_cwp_dec(env, env->cwp - 1);
diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c
index d92c9f1..2c63eb9 100644
--- a/target/sparc/ldst_helper.c
+++ b/target/sparc/ldst_helper.c
@@ -23,9 +23,14 @@
#include "cpu.h"
#include "tcg/tcg.h"
#include "exec/helper-proto.h"
-#include "exec/exec-all.h"
+#include "exec/cputlb.h"
#include "exec/page-protection.h"
-#include "exec/cpu_ldst.h"
+#include "exec/target_page.h"
+#include "accel/tcg/cpu-ldst.h"
+#include "system/memory.h"
+#ifdef CONFIG_USER_ONLY
+#include "user/page-protection.h"
+#endif
#include "asi.h"
//#define DEBUG_MMU
@@ -596,6 +601,9 @@ uint64_t helper_ld_asi(CPUSPARCState *env, target_ulong addr,
case 0x0C: /* Leon3 Date Cache config */
if (env->def.features & CPU_FEATURE_CACHE_CTRL) {
ret = leon3_cache_control_ld(env, addr, size);
+ } else {
+ qemu_log_mask(LOG_UNIMP, "0x" TARGET_FMT_lx ": unimplemented"
+ " address, size: %d\n", addr, size);
}
break;
case 0x01c00a00: /* MXCC control register */
@@ -812,6 +820,9 @@ void helper_st_asi(CPUSPARCState *env, target_ulong addr, uint64_t val,
case 0x0C: /* Leon3 Date Cache config */
if (env->def.features & CPU_FEATURE_CACHE_CTRL) {
leon3_cache_control_st(env, addr, val, size);
+ } else {
+ qemu_log_mask(LOG_UNIMP, "0x" TARGET_FMT_lx ": unimplemented"
+ " address, size: %d\n", addr, size);
}
break;
diff --git a/target/sparc/machine.c b/target/sparc/machine.c
index 48e0cf2..4dd75af 100644
--- a/target/sparc/machine.c
+++ b/target/sparc/machine.c
@@ -1,6 +1,5 @@
#include "qemu/osdep.h"
#include "cpu.h"
-#include "exec/exec-all.h"
#include "qemu/timer.h"
#include "migration/cpu.h"
@@ -143,6 +142,24 @@ static const VMStateInfo vmstate_xcc = {
.get = get_xcc,
.put = put_xcc,
};
+#else
+static bool fq_needed(void *opaque)
+{
+ SPARCCPU *cpu = opaque;
+ return cpu->env.fsr_qne;
+}
+
+static const VMStateDescription vmstate_fq = {
+ .name = "cpu/fq",
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .needed = fq_needed,
+ .fields = (const VMStateField[]) {
+ VMSTATE_UINT32(env.fq.s.addr, SPARCCPU),
+ VMSTATE_UINT32(env.fq.s.insn, SPARCCPU),
+ VMSTATE_END_OF_LIST()
+ },
+};
#endif
static int cpu_pre_save(void *opaque)
@@ -265,4 +282,11 @@ const VMStateDescription vmstate_sparc_cpu = {
#endif
VMSTATE_END_OF_LIST()
},
+#ifndef TARGET_SPARC64
+ .subsections = (const VMStateDescription * const []) {
+ &vmstate_fq,
+ NULL
+ },
+#endif
+
};
diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c
index 9ff0602..217580a 100644
--- a/target/sparc/mmu_helper.c
+++ b/target/sparc/mmu_helper.c
@@ -20,8 +20,12 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "cpu.h"
-#include "exec/exec-all.h"
+#include "exec/cputlb.h"
+#include "accel/tcg/cpu-mmu-index.h"
#include "exec/page-protection.h"
+#include "exec/target_page.h"
+#include "exec/tlb-flags.h"
+#include "system/memory.h"
#include "qemu/qemu-print.h"
#include "trace.h"
@@ -389,7 +393,7 @@ void dump_mmu(CPUSPARCState *env)
* that the sparc ABI is followed.
*/
int sparc_cpu_memory_rw_debug(CPUState *cs, vaddr address,
- uint8_t *buf, int len, bool is_write)
+ uint8_t *buf, size_t len, bool is_write)
{
CPUSPARCState *env = cpu_env(cs);
target_ulong addr = address;
diff --git a/target/sparc/translate.c b/target/sparc/translate.c
index 1136390..b922e53 100644
--- a/target/sparc/translate.c
+++ b/target/sparc/translate.c
@@ -22,14 +22,16 @@
#include "cpu.h"
#include "exec/helper-proto.h"
-#include "exec/exec-all.h"
+#include "exec/target_page.h"
#include "tcg/tcg-op.h"
#include "tcg/tcg-op-gvec.h"
#include "exec/helper-gen.h"
#include "exec/translator.h"
+#include "exec/translation-block.h"
#include "exec/log.h"
#include "fpu/softfloat.h"
#include "asi.h"
+#include "target/sparc/translate.h"
#define HELPER_H "helper.h"
#include "exec/helper-info.c.inc"
@@ -101,13 +103,6 @@
# define MAXTL_MASK 0
#endif
-/* Dynamic PC, must exit to main loop. */
-#define DYNAMIC_PC 1
-/* Dynamic PC, one of two values according to jump_pc[T2]. */
-#define JUMP_PC 2
-/* Dynamic PC, may lookup next TB. */
-#define DYNAMIC_PC_LOOKUP 3
-
#define DISAS_EXIT DISAS_TARGET_0
/* global register indexes */
@@ -185,6 +180,8 @@ typedef struct DisasContext {
bool supervisor;
#ifdef TARGET_SPARC64
bool hypervisor;
+#else
+ bool fsr_qne;
#endif
#endif
@@ -398,8 +395,7 @@ static void gen_op_addcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin)
TCGv z = tcg_constant_tl(0);
if (cin) {
- tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, src1, z, cin, z);
- tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, cpu_cc_N, cpu_cc_C, src2, z);
+ tcg_gen_addcio_tl(cpu_cc_N, cpu_cc_C, src1, src2, cin);
} else {
tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, src1, z, src2, z);
}
@@ -1362,93 +1358,109 @@ static void gen_op_fabsq(TCGv_i128 dst, TCGv_i128 src)
static void gen_op_fmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3)
{
- gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(0));
+ TCGv_i32 z = tcg_constant_i32(0);
+ gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, z);
}
static void gen_op_fmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3)
{
- gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(0));
+ TCGv_i32 z = tcg_constant_i32(0);
+ gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, z);
}
static void gen_op_fmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3)
{
- int op = float_muladd_negate_c;
- gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op));
+ TCGv_i32 z = tcg_constant_i32(0);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c);
+ gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, op);
}
static void gen_op_fmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3)
{
- int op = float_muladd_negate_c;
- gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op));
+ TCGv_i32 z = tcg_constant_i32(0);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c);
+ gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, op);
}
static void gen_op_fnmsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3)
{
- int op = float_muladd_negate_c | float_muladd_negate_result;
- gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op));
+ TCGv_i32 z = tcg_constant_i32(0);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c |
+ float_muladd_negate_result);
+ gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, op);
}
static void gen_op_fnmsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3)
{
- int op = float_muladd_negate_c | float_muladd_negate_result;
- gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op));
+ TCGv_i32 z = tcg_constant_i32(0);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c |
+ float_muladd_negate_result);
+ gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, op);
}
static void gen_op_fnmadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2, TCGv_i32 s3)
{
- int op = float_muladd_negate_result;
- gen_helper_fmadds(d, tcg_env, s1, s2, s3, tcg_constant_i32(op));
+ TCGv_i32 z = tcg_constant_i32(0);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result);
+ gen_helper_fmadds(d, tcg_env, s1, s2, s3, z, op);
}
static void gen_op_fnmaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2, TCGv_i64 s3)
{
- int op = float_muladd_negate_result;
- gen_helper_fmaddd(d, tcg_env, s1, s2, s3, tcg_constant_i32(op));
+ TCGv_i32 z = tcg_constant_i32(0);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result);
+ gen_helper_fmaddd(d, tcg_env, s1, s2, s3, z, op);
}
/* Use muladd to compute (1 * src1) + src2 / 2 with one rounding. */
static void gen_op_fhadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2)
{
- TCGv_i32 one = tcg_constant_i32(float32_one);
- int op = float_muladd_halve_result;
- gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op));
+ TCGv_i32 fone = tcg_constant_i32(float32_one);
+ TCGv_i32 mone = tcg_constant_i32(-1);
+ TCGv_i32 op = tcg_constant_i32(0);
+ gen_helper_fmadds(d, tcg_env, fone, s1, s2, mone, op);
}
static void gen_op_fhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2)
{
- TCGv_i64 one = tcg_constant_i64(float64_one);
- int op = float_muladd_halve_result;
- gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op));
+ TCGv_i64 fone = tcg_constant_i64(float64_one);
+ TCGv_i32 mone = tcg_constant_i32(-1);
+ TCGv_i32 op = tcg_constant_i32(0);
+ gen_helper_fmaddd(d, tcg_env, fone, s1, s2, mone, op);
}
/* Use muladd to compute (1 * src1) - src2 / 2 with one rounding. */
static void gen_op_fhsubs(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2)
{
- TCGv_i32 one = tcg_constant_i32(float32_one);
- int op = float_muladd_negate_c | float_muladd_halve_result;
- gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op));
+ TCGv_i32 fone = tcg_constant_i32(float32_one);
+ TCGv_i32 mone = tcg_constant_i32(-1);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c);
+ gen_helper_fmadds(d, tcg_env, fone, s1, s2, mone, op);
}
static void gen_op_fhsubd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2)
{
- TCGv_i64 one = tcg_constant_i64(float64_one);
- int op = float_muladd_negate_c | float_muladd_halve_result;
- gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op));
+ TCGv_i64 fone = tcg_constant_i64(float64_one);
+ TCGv_i32 mone = tcg_constant_i32(-1);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_c);
+ gen_helper_fmaddd(d, tcg_env, fone, s1, s2, mone, op);
}
/* Use muladd to compute -((1 * src1) + src2 / 2) with one rounding. */
static void gen_op_fnhadds(TCGv_i32 d, TCGv_i32 s1, TCGv_i32 s2)
{
- TCGv_i32 one = tcg_constant_i32(float32_one);
- int op = float_muladd_negate_result | float_muladd_halve_result;
- gen_helper_fmadds(d, tcg_env, one, s1, s2, tcg_constant_i32(op));
+ TCGv_i32 fone = tcg_constant_i32(float32_one);
+ TCGv_i32 mone = tcg_constant_i32(-1);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result);
+ gen_helper_fmadds(d, tcg_env, fone, s1, s2, mone, op);
}
static void gen_op_fnhaddd(TCGv_i64 d, TCGv_i64 s1, TCGv_i64 s2)
{
- TCGv_i64 one = tcg_constant_i64(float64_one);
- int op = float_muladd_negate_result | float_muladd_halve_result;
- gen_helper_fmaddd(d, tcg_env, one, s1, s2, tcg_constant_i32(op));
+ TCGv_i64 fone = tcg_constant_i64(float64_one);
+ TCGv_i32 mone = tcg_constant_i32(-1);
+ TCGv_i32 op = tcg_constant_i32(float_muladd_negate_result);
+ gen_helper_fmaddd(d, tcg_env, fone, s1, s2, mone, op);
}
static void gen_op_fpexception_im(DisasContext *dc, int ftt)
@@ -1463,15 +1475,48 @@ static void gen_op_fpexception_im(DisasContext *dc, int ftt)
gen_exception(dc, TT_FP_EXCP);
}
-static int gen_trap_ifnofpu(DisasContext *dc)
+static bool gen_trap_ifnofpu(DisasContext *dc)
{
#if !defined(CONFIG_USER_ONLY)
if (!dc->fpu_enabled) {
gen_exception(dc, TT_NFPU_INSN);
- return 1;
+ return true;
}
#endif
- return 0;
+ return false;
+}
+
+static bool gen_trap_iffpexception(DisasContext *dc)
+{
+#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
+ /*
+ * There are 3 states for the sparc32 fpu:
+ * Normally the fpu is in fp_execute, and all insns are allowed.
+ * When an exception is signaled, it moves to fp_exception_pending state.
+ * Upon seeing the next FPop, the fpu moves to fp_exception state,
+ * populates the FQ, and generates an fp_exception trap.
+ * The fpu remains in fp_exception state until FQ becomes empty
+ * after execution of a STDFQ instruction. While the fpu is in
+ * fp_exception state, and FPop, fp load or fp branch insn will
+ * return to fp_exception_pending state, set FSR.FTT to sequence_error,
+ * and the insn will not be entered into the FQ.
+ *
+ * In QEMU, we do not model the fp_exception_pending state and
+ * instead populate FQ and raise the exception immediately.
+ * But we can still honor fp_exception state by noticing when
+ * the FQ is not empty.
+ */
+ if (dc->fsr_qne) {
+ gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR);
+ return true;
+ }
+#endif
+ return false;
+}
+
+static bool gen_trap_if_nofpu_fpexception(DisasContext *dc)
+{
+ return gen_trap_ifnofpu(dc) || gen_trap_iffpexception(dc);
}
/* asi moves */
@@ -2641,7 +2686,7 @@ static bool do_fbpfcc(DisasContext *dc, arg_bcc *a)
{
DisasCompare cmp;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
gen_fcompare(&cmp, a->cc, a->cond);
@@ -2836,6 +2881,14 @@ static TCGv do_rd_leon3_config(DisasContext *dc, TCGv dst)
TRANS(RDASR17, ASR17, do_rd_special, true, a->rd, do_rd_leon3_config)
+static TCGv do_rdpic(DisasContext *dc, TCGv dst)
+{
+ return tcg_constant_tl(0);
+}
+
+TRANS(RDPIC, HYPV, do_rd_special, supervisor(dc), a->rd, do_rdpic)
+
+
static TCGv do_rdccr(DisasContext *dc, TCGv dst)
{
gen_helper_rdccr(dst, tcg_env);
@@ -3269,6 +3322,17 @@ static void do_wrfprs(DisasContext *dc, TCGv src)
TRANS(WRFPRS, 64, do_wr_special, a, true, do_wrfprs)
+static bool do_priv_nop(DisasContext *dc, bool priv)
+{
+ if (!priv) {
+ return raise_priv(dc);
+ }
+ return advance_pc(dc);
+}
+
+TRANS(WRPCR, HYPV, do_priv_nop, supervisor(dc))
+TRANS(WRPIC, HYPV, do_priv_nop, supervisor(dc))
+
static void do_wrgsr(DisasContext *dc, TCGv src)
{
gen_trap_ifnofpu(dc);
@@ -4480,7 +4544,7 @@ static bool do_ld_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz)
if (addr == NULL) {
return false;
}
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
if (sz == MO_128 && gen_trap_float128(dc)) {
@@ -4508,6 +4572,7 @@ static bool do_st_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz)
if (addr == NULL) {
return false;
}
+ /* Store insns are ok in fp_exception_pending state. */
if (gen_trap_ifnofpu(dc)) {
return true;
}
@@ -4521,7 +4586,7 @@ static bool do_st_fpr(DisasContext *dc, arg_r_r_ri_asi *a, MemOp sz)
TRANS(STF, ALL, do_st_fpr, a, MO_32)
TRANS(STDF, ALL, do_st_fpr, a, MO_64)
-TRANS(STQF, ALL, do_st_fpr, a, MO_128)
+TRANS(STQF, 64, do_st_fpr, a, MO_128)
TRANS(STFA, 64, do_st_fpr, a, MO_32)
TRANS(STDFA, 64, do_st_fpr, a, MO_64)
@@ -4529,17 +4594,41 @@ TRANS(STQFA, 64, do_st_fpr, a, MO_128)
static bool trans_STDFQ(DisasContext *dc, arg_STDFQ *a)
{
+ TCGv addr;
+
if (!avail_32(dc)) {
return false;
}
+ addr = gen_ldst_addr(dc, a->rs1, a->imm, a->rs2_or_imm);
+ if (addr == NULL) {
+ return false;
+ }
if (!supervisor(dc)) {
return raise_priv(dc);
}
+#if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY)
if (gen_trap_ifnofpu(dc)) {
return true;
}
- gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR);
- return true;
+ if (!dc->fsr_qne) {
+ gen_op_fpexception_im(dc, FSR_FTT_SEQ_ERROR);
+ return true;
+ }
+
+ /* Store the single element from the queue. */
+ TCGv_i64 fq = tcg_temp_new_i64();
+ tcg_gen_ld_i64(fq, tcg_env, offsetof(CPUSPARCState, fq.d));
+ tcg_gen_qemu_st_i64(fq, addr, dc->mem_idx, MO_TEUQ | MO_ALIGN_4);
+
+ /* Mark the queue empty, transitioning to fp_execute state. */
+ tcg_gen_st_i32(tcg_constant_i32(0), tcg_env,
+ offsetof(CPUSPARCState, fsr_qne));
+ dc->fsr_qne = 0;
+
+ return advance_pc(dc);
+#else
+ qemu_build_not_reached();
+#endif
}
static bool trans_LDFSR(DisasContext *dc, arg_r_r_ri *a)
@@ -4550,7 +4639,7 @@ static bool trans_LDFSR(DisasContext *dc, arg_r_r_ri *a)
if (addr == NULL) {
return false;
}
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -4574,7 +4663,7 @@ static bool do_ldxfsr(DisasContext *dc, arg_r_r_ri *a, bool entire)
if (addr == NULL) {
return false;
}
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -4611,6 +4700,7 @@ static bool do_stfsr(DisasContext *dc, arg_r_r_ri *a, MemOp mop)
if (addr == NULL) {
return false;
}
+ /* Store insns are ok in fp_exception_pending state. */
if (gen_trap_ifnofpu(dc)) {
return true;
}
@@ -4653,7 +4743,7 @@ static bool do_ff(DisasContext *dc, arg_r_r *a,
{
TCGv_i32 tmp;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -4694,7 +4784,7 @@ static bool do_env_ff(DisasContext *dc, arg_r_r *a,
{
TCGv_i32 tmp;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -4714,7 +4804,7 @@ static bool do_env_fd(DisasContext *dc, arg_r_r *a,
TCGv_i32 dst;
TCGv_i64 src;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -4734,7 +4824,7 @@ static bool do_dd(DisasContext *dc, arg_r_r *a,
{
TCGv_i64 dst, src;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -4756,7 +4846,7 @@ static bool do_env_dd(DisasContext *dc, arg_r_r *a,
{
TCGv_i64 dst, src;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -4796,7 +4886,7 @@ static bool do_env_df(DisasContext *dc, arg_r_r *a,
TCGv_i64 dst;
TCGv_i32 src;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -4839,7 +4929,7 @@ static bool do_env_qq(DisasContext *dc, arg_r_r *a,
{
TCGv_i128 t;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
if (gen_trap_float128(dc)) {
@@ -4860,7 +4950,7 @@ static bool do_env_fq(DisasContext *dc, arg_r_r *a,
TCGv_i128 src;
TCGv_i32 dst;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
if (gen_trap_float128(dc)) {
@@ -4883,7 +4973,7 @@ static bool do_env_dq(DisasContext *dc, arg_r_r *a,
TCGv_i128 src;
TCGv_i64 dst;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
if (gen_trap_float128(dc)) {
@@ -4906,7 +4996,7 @@ static bool do_env_qf(DisasContext *dc, arg_r_r *a,
TCGv_i32 src;
TCGv_i128 dst;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
if (gen_trap_float128(dc)) {
@@ -4929,10 +5019,7 @@ static bool do_env_qd(DisasContext *dc, arg_r_r *a,
TCGv_i64 src;
TCGv_i128 dst;
- if (gen_trap_ifnofpu(dc)) {
- return true;
- }
- if (gen_trap_float128(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -4989,7 +5076,7 @@ static bool do_env_fff(DisasContext *dc, arg_r_r_r *a,
{
TCGv_i32 src1, src2;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -5198,7 +5285,7 @@ static bool do_env_ddd(DisasContext *dc, arg_r_r_r *a,
{
TCGv_i64 dst, src1, src2;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -5222,7 +5309,7 @@ static bool trans_FsMULd(DisasContext *dc, arg_r_r_r *a)
TCGv_i64 dst;
TCGv_i32 src1, src2;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
if (!(dc->def->features & CPU_FEATURE_FSMULD)) {
@@ -5331,7 +5418,7 @@ static bool do_env_qqq(DisasContext *dc, arg_r_r_r *a,
{
TCGv_i128 src1, src2;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
if (gen_trap_float128(dc)) {
@@ -5355,7 +5442,7 @@ static bool trans_FdMULq(DisasContext *dc, arg_r_r_r *a)
TCGv_i64 src1, src2;
TCGv_i128 dst;
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
if (gen_trap_float128(dc)) {
@@ -5445,7 +5532,7 @@ static bool do_fcmps(DisasContext *dc, arg_FCMPs *a, bool e)
if (avail_32(dc) && a->cc != 0) {
return false;
}
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -5469,7 +5556,7 @@ static bool do_fcmpd(DisasContext *dc, arg_FCMPd *a, bool e)
if (avail_32(dc) && a->cc != 0) {
return false;
}
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
@@ -5493,7 +5580,7 @@ static bool do_fcmpq(DisasContext *dc, arg_FCMPq *a, bool e)
if (avail_32(dc) && a->cc != 0) {
return false;
}
- if (gen_trap_ifnofpu(dc)) {
+ if (gen_trap_if_nofpu_fpexception(dc)) {
return true;
}
if (gen_trap_float128(dc)) {
@@ -5526,7 +5613,7 @@ static bool trans_FLCMPs(DisasContext *dc, arg_FLCMPs *a)
src1 = gen_load_fpr_F(dc, a->rs1);
src2 = gen_load_fpr_F(dc, a->rs2);
- gen_helper_flcmps(cpu_fcc[a->cc], src1, src2);
+ gen_helper_flcmps(cpu_fcc[a->cc], tcg_env, src1, src2);
return advance_pc(dc);
}
@@ -5543,7 +5630,7 @@ static bool trans_FLCMPd(DisasContext *dc, arg_FLCMPd *a)
src1 = gen_load_fpr_D(dc, a->rs1);
src2 = gen_load_fpr_D(dc, a->rs2);
- gen_helper_flcmpd(cpu_fcc[a->cc], src1, src2);
+ gen_helper_flcmpd(cpu_fcc[a->cc], tcg_env, src1, src2);
return advance_pc(dc);
}
@@ -5596,13 +5683,15 @@ static void sparc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
dc->address_mask_32bit = tb_am_enabled(dc->base.tb->flags);
#ifndef CONFIG_USER_ONLY
dc->supervisor = (dc->base.tb->flags & TB_FLAG_SUPER) != 0;
+# ifdef TARGET_SPARC64
+ dc->hypervisor = (dc->base.tb->flags & TB_FLAG_HYPER) != 0;
+# else
+ dc->fsr_qne = (dc->base.tb->flags & TB_FLAG_FSR_QNE) != 0;
+# endif
#endif
#ifdef TARGET_SPARC64
dc->fprs_dirty = 0;
dc->asi = (dc->base.tb->flags >> TB_FLAG_ASI_SHIFT) & 0xff;
-#ifndef CONFIG_USER_ONLY
- dc->hypervisor = (dc->base.tb->flags & TB_FLAG_HYPER) != 0;
-#endif
#endif
/*
* if we reach a page boundary, we stop generation so that the
@@ -5748,8 +5837,8 @@ static const TranslatorOps sparc_tr_ops = {
.tb_stop = sparc_tr_tb_stop,
};
-void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- vaddr pc, void *host_pc)
+void sparc_translate_code(CPUState *cs, TranslationBlock *tb,
+ int *max_insns, vaddr pc, void *host_pc)
{
DisasContext dc = {};
@@ -5821,26 +5910,3 @@ void sparc_tcg_init(void)
gregnames[i]);
}
}
-
-void sparc_restore_state_to_opc(CPUState *cs,
- const TranslationBlock *tb,
- const uint64_t *data)
-{
- CPUSPARCState *env = cpu_env(cs);
- target_ulong pc = data[0];
- target_ulong npc = data[1];
-
- env->pc = pc;
- if (npc == DYNAMIC_PC) {
- /* dynamic NPC: already stored */
- } else if (npc & JUMP_PC) {
- /* jump PC: use 'cond' and the jump targets of the translation */
- if (env->cond) {
- env->npc = npc & ~3;
- } else {
- env->npc = pc + 4;
- }
- } else {
- env->npc = npc;
- }
-}
diff --git a/target/sparc/translate.h b/target/sparc/translate.h
new file mode 100644
index 0000000..a46fa4f
--- /dev/null
+++ b/target/sparc/translate.h
@@ -0,0 +1,17 @@
+/*
+ * QEMU translation definitions for SPARC
+ *
+ * Copyright (c) 2024 Linaro, Ltd
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#ifndef SPARC_TRANSLATION_H
+#define SPARC_TRANSLATION_H
+
+/* Dynamic PC, must exit to main loop. */
+#define DYNAMIC_PC 1
+/* Dynamic PC, one of two values according to jump_pc[T2]. */
+#define JUMP_PC 2
+/* Dynamic PC, may lookup next TB. */
+#define DYNAMIC_PC_LOOKUP 3
+
+#endif
diff --git a/target/sparc/win_helper.c b/target/sparc/win_helper.c
index b53fc9c..9ad9d01 100644
--- a/target/sparc/win_helper.c
+++ b/target/sparc/win_helper.c
@@ -20,33 +20,22 @@
#include "qemu/osdep.h"
#include "qemu/main-loop.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);
+ memcpy(env->regbase, env->regbase + env->nwindows * 16,
+ sizeof(env->gregs));
}
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);
+ memcpy(env->regbase + env->nwindows * 16, env->regbase,
+ sizeof(env->gregs));
}
env->regwptr = env->regbase + (new_cwp * 16);
}
@@ -361,8 +350,8 @@ void cpu_gl_switch_gregs(CPUSPARCState *env, uint32_t new_gl)
dst = get_gl_gregset(env, env->gl);
if (src != dst) {
- memcpy32(dst, env->gregs);
- memcpy32(env->gregs, src);
+ memcpy(dst, env->gregs, sizeof(env->gregs));
+ memcpy(env->gregs, src, sizeof(env->gregs));
}
}
@@ -393,8 +382,8 @@ void cpu_change_pstate(CPUSPARCState *env, uint32_t new_pstate)
/* 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);
+ memcpy(dst, env->gregs, sizeof(env->gregs));
+ memcpy(env->gregs, src, sizeof(env->gregs));
} else {
trace_win_helper_no_switch_pstate(new_pstate_regs);
}