aboutsummaryrefslogtreecommitdiff
path: root/target/hppa
diff options
context:
space:
mode:
Diffstat (limited to 'target/hppa')
-rw-r--r--target/hppa/cpu-param.h12
-rw-r--r--target/hppa/cpu.c76
-rw-r--r--target/hppa/cpu.h62
-rw-r--r--target/hppa/fpu_helper.c51
-rw-r--r--target/hppa/helper.c28
-rw-r--r--target/hppa/helper.h1
-rw-r--r--target/hppa/insns.decode6
-rw-r--r--target/hppa/int_helper.c16
-rw-r--r--target/hppa/machine.c7
-rw-r--r--target/hppa/mem_helper.c65
-rw-r--r--target/hppa/op_helper.c9
-rw-r--r--target/hppa/sys_helper.c9
-rw-r--r--target/hppa/translate.c58
13 files changed, 275 insertions, 125 deletions
diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h
index 473d489..9bf7ac7 100644
--- a/target/hppa/cpu-param.h
+++ b/target/hppa/cpu-param.h
@@ -2,14 +2,12 @@
* PA-RISC cpu parameters for qemu.
*
* Copyright (c) 2016 Richard Henderson <rth@twiddle.net>
- * SPDX-License-Identifier: LGPL-2.0+
+ * SPDX-License-Identifier: LGPL-2.0-or-later
*/
#ifndef HPPA_CPU_PARAM_H
#define HPPA_CPU_PARAM_H
-#define TARGET_LONG_BITS 64
-
#if defined(CONFIG_USER_ONLY) && defined(TARGET_ABI32)
# define TARGET_PHYS_ADDR_SPACE_BITS 32
# define TARGET_VIRT_ADDR_SPACE_BITS 32
@@ -21,12 +19,6 @@
#define TARGET_PAGE_BITS 12
-/* PA-RISC 1.x processors have a strong memory model. */
-/*
- * ??? While we do not yet implement PA-RISC 2.0, those processors have
- * a weak memory model, but with TLB bits that force ordering on a per-page
- * basis. It's probably easier to fall back to a strong memory model.
- */
-#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL
+#define TARGET_INSN_START_EXTRA_WORDS 2
#endif
diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c
index 7cf2e2f..2477772 100644
--- a/target/hppa/cpu.c
+++ b/target/hppa/cpu.c
@@ -24,9 +24,12 @@
#include "qemu/timer.h"
#include "cpu.h"
#include "qemu/module.h"
-#include "exec/exec-all.h"
+#include "exec/translation-block.h"
+#include "exec/target_page.h"
#include "fpu/softfloat.h"
#include "tcg/tcg.h"
+#include "hw/hppa/hppa_hardware.h"
+#include "accel/tcg/cpu-ops.h"
static void hppa_cpu_set_pc(CPUState *cs, vaddr value)
{
@@ -43,15 +46,17 @@ static vaddr hppa_cpu_get_pc(CPUState *cs)
{
CPUHPPAState *env = cpu_env(cs);
- return hppa_form_gva_psw(env->psw, (env->psw & PSW_C ? env->iasq_f : 0),
- env->iaoq_f & -4);
+ return hppa_form_gva_mask(env->gva_offset_mask,
+ (env->psw & PSW_C ? env->iasq_f : 0),
+ env->iaoq_f & -4);
}
-void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc,
- uint64_t *pcsbase, uint32_t *pflags)
+static TCGTBCPUState hppa_get_tb_cpu_state(CPUState *cs)
{
+ CPUHPPAState *env = cpu_env(cs);
uint32_t flags = 0;
uint64_t cs_base = 0;
+ vaddr pc;
/*
* TB lookup assumes that PC contains the complete virtual address.
@@ -59,7 +64,7 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc,
* incomplete virtual address. This also means that we must separate
* out current cpu privilege from the low bits of IAOQ_F.
*/
- *pc = hppa_cpu_get_pc(env_cpu(env));
+ pc = hppa_cpu_get_pc(env_cpu(env));
flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT;
/*
@@ -89,10 +94,13 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc,
& (env->sr[4] == env->sr[7])) {
flags |= TB_FLAG_SR_SAME;
}
+ if ((env->psw & PSW_W) &&
+ (env->dr[2] & HPPA64_DIAG_SPHASH_ENABLE)) {
+ flags |= TB_FLAG_SPHASH;
+ }
#endif
- *pcsbase = cs_base;
- *pflags = flags;
+ return (TCGTBCPUState){ .pc = pc, .flags = flags, .cs_base = cs_base };
}
static void hppa_cpu_synchronize_from_tb(CPUState *cs,
@@ -124,10 +132,12 @@ static void hppa_restore_state_to_opc(CPUState *cs,
env->psw_n = 0;
}
+#ifndef CONFIG_USER_ONLY
static bool hppa_cpu_has_work(CPUState *cs)
{
return cs->interrupt_request & (CPU_INTERRUPT_HARD | CPU_INTERRUPT_NMI);
}
+#endif /* !CONFIG_USER_ONLY */
static int hppa_cpu_mmu_index(CPUState *cs, bool ifetch)
{
@@ -143,6 +153,7 @@ static int hppa_cpu_mmu_index(CPUState *cs, bool ifetch)
static void hppa_cpu_disas_set_info(CPUState *cs, disassemble_info *info)
{
info->mach = bfd_mach_hppa20;
+ info->endian = BFD_ENDIAN_BIG;
info->print_insn = print_insn_hppa;
}
@@ -194,13 +205,33 @@ static void hppa_cpu_realizefn(DeviceState *dev, Error **errp)
static void hppa_cpu_initfn(Object *obj)
{
+ CPUHPPAState *env = cpu_env(CPU(obj));
+
+ env->is_pa20 = !!object_dynamic_cast(obj, TYPE_HPPA64_CPU);
+}
+
+static void hppa_cpu_reset_hold(Object *obj, ResetType type)
+{
+ HPPACPUClass *scc = HPPA_CPU_GET_CLASS(obj);
CPUState *cs = CPU(obj);
HPPACPU *cpu = HPPA_CPU(obj);
CPUHPPAState *env = &cpu->env;
+ if (scc->parent_phases.hold) {
+ scc->parent_phases.hold(obj, type);
+ }
cs->exception_index = -1;
+ cs->halted = 0;
+ cpu_set_pc(cs, 0xf0000004);
+
+ memset(env, 0, offsetof(CPUHPPAState, end_reset_fields));
+
cpu_hppa_loaded_fr0(env);
- cpu_hppa_put_psw(env, PSW_W);
+
+ /* 64-bit machines start with space-register hashing enabled in %dr2 */
+ env->dr[2] = hppa_is_pa20(env) ? HPPA64_DIAG_SPHASH_ENABLE : 0;
+
+ cpu_hppa_put_psw(env, PSW_M);
}
static ObjectClass *hppa_cpu_class_by_name(const char *cpu_model)
@@ -214,39 +245,54 @@ static ObjectClass *hppa_cpu_class_by_name(const char *cpu_model)
#include "hw/core/sysemu-cpu-ops.h"
static const struct SysemuCPUOps hppa_sysemu_ops = {
+ .has_work = hppa_cpu_has_work,
.get_phys_page_debug = hppa_cpu_get_phys_page_debug,
};
#endif
-#include "hw/core/tcg-cpu-ops.h"
-
static const TCGCPUOps hppa_tcg_ops = {
+ /* PA-RISC 1.x processors have a strong memory model. */
+ /*
+ * ??? While we do not yet implement PA-RISC 2.0, those processors have
+ * a weak memory model, but with TLB bits that force ordering on a per-page
+ * basis. It's probably easier to fall back to a strong memory model.
+ */
+ .guest_default_memory_order = TCG_MO_ALL,
+ .mttcg_supported = true,
+
.initialize = hppa_translate_init,
+ .translate_code = hppa_translate_code,
+ .get_tb_cpu_state = hppa_get_tb_cpu_state,
.synchronize_from_tb = hppa_cpu_synchronize_from_tb,
.restore_state_to_opc = hppa_restore_state_to_opc,
+ .mmu_index = hppa_cpu_mmu_index,
#ifndef CONFIG_USER_ONLY
- .tlb_fill = hppa_cpu_tlb_fill,
+ .tlb_fill_align = hppa_cpu_tlb_fill_align,
+ .pointer_wrap = cpu_pointer_wrap_notreached,
.cpu_exec_interrupt = hppa_cpu_exec_interrupt,
.cpu_exec_halt = hppa_cpu_has_work,
+ .cpu_exec_reset = cpu_reset,
.do_interrupt = hppa_cpu_do_interrupt,
.do_unaligned_access = hppa_cpu_do_unaligned_access,
.do_transaction_failed = hppa_cpu_do_transaction_failed,
#endif /* !CONFIG_USER_ONLY */
};
-static void hppa_cpu_class_init(ObjectClass *oc, void *data)
+static void hppa_cpu_class_init(ObjectClass *oc, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
CPUClass *cc = CPU_CLASS(oc);
HPPACPUClass *acc = HPPA_CPU_CLASS(oc);
+ ResettableClass *rc = RESETTABLE_CLASS(oc);
device_class_set_parent_realize(dc, hppa_cpu_realizefn,
&acc->parent_realize);
+ resettable_class_set_parent_phases(rc, NULL, hppa_cpu_reset_hold, NULL,
+ &acc->parent_phases);
+
cc->class_by_name = hppa_cpu_class_by_name;
- cc->has_work = hppa_cpu_has_work;
- cc->mmu_index = hppa_cpu_mmu_index;
cc->dump_state = hppa_cpu_dump_state;
cc->set_pc = hppa_cpu_set_pc;
cc->get_pc = hppa_cpu_get_pc;
diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h
index 2bcb3b6..11d59d1 100644
--- a/target/hppa/cpu.h
+++ b/target/hppa/cpu.h
@@ -21,7 +21,10 @@
#define HPPA_CPU_H
#include "cpu-qom.h"
+#include "exec/cpu-common.h"
#include "exec/cpu-defs.h"
+#include "exec/cpu-interrupt.h"
+#include "system/memory.h"
#include "qemu/cpu-float.h"
#include "qemu/interval-tree.h"
#include "hw/registerfields.h"
@@ -45,8 +48,6 @@
#define PRIV_KERNEL 0
#define PRIV_USER 3
-#define TARGET_INSN_START_EXTRA_WORDS 2
-
/* No need to flush MMU_ABS*_IDX */
#define HPPA_MMU_FLUSH_MASK \
(1 << MMU_KERNEL_IDX | 1 << MMU_KERNEL_P_IDX | \
@@ -211,7 +212,7 @@ typedef struct CPUArchState {
uint32_t psw; /* All psw bits except the following: */
uint32_t psw_xb; /* X and B, in their normal positions */
target_ulong psw_n; /* boolean */
- target_long psw_v; /* in most significant bit */
+ target_long psw_v; /* in bit 31 */
/* Splitting the carry-borrow field into the MSB and "the rest", allows
* for "the rest" to be deleted when it is unused, but the MSB is in use.
@@ -223,6 +224,7 @@ typedef struct CPUArchState {
target_ulong psw_cb; /* in least significant bit of next nibble */
target_ulong psw_cb_msb; /* boolean */
+ uint64_t gva_offset_mask; /* cached address mask based on PSW and %dr2 */
uint64_t iasq_f;
uint64_t iasq_b;
@@ -232,6 +234,7 @@ typedef struct CPUArchState {
target_ulong cr[32]; /* control registers */
target_ulong cr_back[2]; /* back of cr17/cr18 */
target_ulong shadow[7]; /* shadow registers */
+ target_ulong dr[32]; /* diagnose registers */
/*
* During unwind of a memory insn, the base register of the address.
@@ -263,6 +266,15 @@ typedef struct CPUArchState {
IntervalTreeRoot tlb_root;
HPPATLBEntry tlb[HPPA_TLB_ENTRIES];
+
+ /* Fields up to this point are cleared by a CPU reset */
+ struct {} end_reset_fields;
+
+ bool is_pa20;
+
+ target_ulong kernel_entry; /* Linux kernel was loaded here */
+ target_ulong cmdline_or_bootorder;
+ target_ulong initrd_base, initrd_end;
} CPUHPPAState;
/**
@@ -281,7 +293,7 @@ struct ArchCPU {
/**
* HPPACPUClass:
* @parent_realize: The parent class' realize handler.
- * @parent_reset: The parent class' reset handler.
+ * @parent_phases: The parent class' reset phase handlers.
*
* An HPPA CPU model.
*/
@@ -289,14 +301,12 @@ struct HPPACPUClass {
CPUClass parent_class;
DeviceRealize parent_realize;
- DeviceReset parent_reset;
+ ResettablePhases parent_phases;
};
-#include "exec/cpu-all.h"
-
-static inline bool hppa_is_pa20(CPUHPPAState *env)
+static inline bool hppa_is_pa20(const CPUHPPAState *env)
{
- return object_dynamic_cast(OBJECT(env_cpu(env)), TYPE_HPPA64_CPU) != NULL;
+ return env->is_pa20;
}
static inline int HPPA_BTLB_ENTRIES(CPUHPPAState *env)
@@ -305,30 +315,25 @@ static inline int HPPA_BTLB_ENTRIES(CPUHPPAState *env)
}
void hppa_translate_init(void);
+void hppa_translate_code(CPUState *cs, TranslationBlock *tb,
+ int *max_insns, vaddr pc, void *host_pc);
#define CPU_RESOLVING_TYPE TYPE_HPPA_CPU
-static inline uint64_t gva_offset_mask(target_ulong psw)
-{
- return (psw & PSW_W
- ? MAKE_64BIT_MASK(0, 62)
- : MAKE_64BIT_MASK(0, 32));
-}
-
-static inline target_ulong hppa_form_gva_psw(target_ulong psw, uint64_t spc,
- target_ulong off)
+static inline target_ulong hppa_form_gva_mask(uint64_t gva_offset_mask,
+ uint64_t spc, target_ulong off)
{
#ifdef CONFIG_USER_ONLY
- return off;
+ return off & gva_offset_mask;
#else
- return spc | (off & gva_offset_mask(psw));
+ return spc | (off & gva_offset_mask);
#endif
}
static inline target_ulong hppa_form_gva(CPUHPPAState *env, uint64_t spc,
target_ulong off)
{
- return hppa_form_gva_psw(env->psw, spc, off);
+ return hppa_form_gva_mask(env->gva_offset_mask, spc, off);
}
hwaddr hppa_abs_to_phys_pa2_w0(vaddr addr);
@@ -342,14 +347,13 @@ hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr);
#define TB_FLAG_SR_SAME PSW_I
#define TB_FLAG_PRIV_SHIFT 8
#define TB_FLAG_UNALIGN 0x400
+#define TB_FLAG_SPHASH 0x800
#define CS_BASE_DIFFPAGE (1 << 12)
#define CS_BASE_DIFFSPACE (1 << 13)
-void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc,
- uint64_t *cs_base, uint32_t *pflags);
-
target_ulong cpu_hppa_get_psw(CPUHPPAState *env);
void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong);
+void update_gva_offset_mask(CPUHPPAState *env);
void cpu_hppa_loaded_fr0(CPUHPPAState *env);
#ifdef CONFIG_USER_ONLY
@@ -365,13 +369,13 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int);
void hppa_ptlbe(CPUHPPAState *env);
hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr);
void hppa_set_ior_and_isr(CPUHPPAState *env, vaddr addr, bool mmu_disabled);
-bool hppa_cpu_tlb_fill(CPUState *cs, vaddr address, int size,
- MMUAccessType access_type, int mmu_idx,
- bool probe, uintptr_t retaddr);
+bool hppa_cpu_tlb_fill_align(CPUState *cs, CPUTLBEntryFull *out, vaddr addr,
+ MMUAccessType access_type, int mmu_idx,
+ MemOp memop, int size, bool probe, uintptr_t ra);
void hppa_cpu_do_interrupt(CPUState *cpu);
bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req);
int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
- int type, hwaddr *pphys, int *pprot);
+ int type, MemOp mop, hwaddr *pphys, int *pprot);
void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
vaddr addr, unsigned size,
MMUAccessType access_type,
@@ -383,6 +387,4 @@ void hppa_cpu_alarm_timer(void *);
#endif
G_NORETURN void hppa_dynamic_excp(CPUHPPAState *env, int excp, uintptr_t ra);
-#define CPU_RESOLVING_TYPE TYPE_HPPA_CPU
-
#endif /* HPPA_CPU_H */
diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c
index deaed2b..4535320 100644
--- a/target/hppa/fpu_helper.c
+++ b/target/hppa/fpu_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"
@@ -49,6 +48,36 @@ void HELPER(loaded_fr0)(CPUHPPAState *env)
d = FIELD_EX32(shadow, FPSR, D);
set_flush_to_zero(d, &env->fp_status);
set_flush_inputs_to_zero(d, &env->fp_status);
+
+ /*
+ * TODO: we only need to do this at CPU reset, but currently
+ * HPPA does note implement a CPU reset method at all...
+ */
+ set_float_2nan_prop_rule(float_2nan_prop_s_ab, &env->fp_status);
+ /*
+ * TODO: The HPPA architecture reference only documents its NaN
+ * propagation rule for 2-operand operations. Testing on real hardware
+ * might be necessary to confirm whether this order for muladd is correct.
+ * Not preferring the SNaN is almost certainly incorrect as it diverges
+ * from the documented rules for 2-operand operations.
+ */
+ set_float_3nan_prop_rule(float_3nan_prop_abc, &env->fp_status);
+ /* For inf * 0 + NaN, return the input NaN */
+ set_float_infzeronan_rule(float_infzeronan_dnan_never, &env->fp_status);
+ /* Default NaN: sign bit clear, msb-1 frac bit set */
+ set_float_default_nan_pattern(0b00100000, &env->fp_status);
+ set_snan_bit_is_one(true, &env->fp_status);
+ /*
+ * "PA-RISC 2.0 Architecture" says it is IMPDEF whether the flushing
+ * enabled by FPSR.D happens before or after rounding. We pick "before"
+ * for consistency with tininess detection.
+ */
+ set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status);
+ /*
+ * TODO: "PA-RISC 2.0 Architecture" chapter 10 says that we should
+ * detect tininess before rounding, but we don't set that here so we
+ * get the default tininess after rounding.
+ */
}
void cpu_hppa_loaded_fr0(CPUHPPAState *env)
@@ -65,7 +94,8 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra)
{
uint32_t soft_exp = get_float_exception_flags(&env->fp_status);
uint32_t hard_exp = 0;
- uint32_t shadow = env->fr0_shadow;
+ uint32_t shadow = env->fr0_shadow & 0x3ffffff;
+ uint32_t fr1 = 0;
if (likely(soft_exp == 0)) {
env->fr[0] = (uint64_t)shadow << 32;
@@ -78,9 +108,22 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra)
hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK);
hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK);
hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK);
- shadow |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT);
+ if (hard_exp & shadow) {
+ shadow = FIELD_DP32(shadow, FPSR, T, 1);
+ /* fill exception register #1, which is lower 32-bits of fr[0] */
+#if !defined(CONFIG_USER_ONLY)
+ if (hard_exp & (R_FPSR_ENA_O_MASK | R_FPSR_ENA_U_MASK)) {
+ /* over- and underflow both set overflow flag only */
+ fr1 = FIELD_DP32(fr1, FPSR, C, 1);
+ fr1 = FIELD_DP32(fr1, FPSR, FLG_O, 1);
+ } else
+#endif
+ {
+ fr1 |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT);
+ }
+ }
env->fr0_shadow = shadow;
- env->fr[0] = (uint64_t)shadow << 32;
+ env->fr[0] = (uint64_t)shadow << 32 | fr1;
if (hard_exp & shadow) {
hppa_dynamic_excp(env, EXCP_ASSIST, ra);
diff --git a/target/hppa/helper.c b/target/hppa/helper.c
index b79ddd8..d7f8495 100644
--- a/target/hppa/helper.c
+++ b/target/hppa/helper.c
@@ -21,9 +21,9 @@
#include "qemu/log.h"
#include "cpu.h"
#include "fpu/softfloat.h"
-#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "qemu/qemu-print.h"
+#include "hw/hppa/hppa_hardware.h"
target_ulong cpu_hppa_get_psw(CPUHPPAState *env)
{
@@ -53,12 +53,28 @@ target_ulong cpu_hppa_get_psw(CPUHPPAState *env)
}
psw |= env->psw_n * PSW_N;
- psw |= (env->psw_v < 0) * PSW_V;
+ psw |= ((env->psw_v >> 31) & 1) * PSW_V;
psw |= env->psw | env->psw_xb;
return psw;
}
+void update_gva_offset_mask(CPUHPPAState *env)
+{
+ uint64_t gom;
+
+ if (env->psw & PSW_W) {
+ gom = (env->dr[2] & HPPA64_DIAG_SPHASH_ENABLE)
+ ? MAKE_64BIT_MASK(0, 62) &
+ ~((uint64_t)HPPA64_PDC_CACHE_RET_SPID_VAL << 48)
+ : MAKE_64BIT_MASK(0, 62);
+ } else {
+ gom = MAKE_64BIT_MASK(0, 32);
+ }
+
+ env->gva_offset_mask = gom;
+}
+
void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw)
{
uint64_t reserved;
@@ -98,6 +114,8 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw)
cb |= ((psw >> 9) & 1) << 8;
cb |= ((psw >> 8) & 1) << 4;
env->psw_cb = cb;
+
+ update_gva_offset_mask(env);
}
void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags)
@@ -133,9 +151,11 @@ void hppa_cpu_dump_state(CPUState *cs, FILE *f, int flags)
qemu_fprintf(f, "IA_F %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n"
"IA_B %08" PRIx64 ":%0*" PRIx64 " (" TARGET_FMT_lx ")\n",
env->iasq_f >> 32, w, m & env->iaoq_f,
- hppa_form_gva_psw(psw, env->iasq_f, env->iaoq_f),
+ hppa_form_gva_mask(env->gva_offset_mask, env->iasq_f,
+ env->iaoq_f),
env->iasq_b >> 32, w, m & env->iaoq_b,
- hppa_form_gva_psw(psw, env->iasq_b, env->iaoq_b));
+ hppa_form_gva_mask(env->gva_offset_mask, env->iasq_b,
+ env->iaoq_b));
psw_c[0] = (psw & PSW_W ? 'W' : '-');
psw_c[1] = (psw & PSW_E ? 'E' : '-');
diff --git a/target/hppa/helper.h b/target/hppa/helper.h
index de41192..8369855 100644
--- a/target/hppa/helper.h
+++ b/target/hppa/helper.h
@@ -99,6 +99,7 @@ DEF_HELPER_FLAGS_2(ptlb_l, TCG_CALL_NO_RWG, void, env, tl)
DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tl, env, tl)
DEF_HELPER_FLAGS_1(change_prot_id, TCG_CALL_NO_RWG, void, env)
+DEF_HELPER_FLAGS_1(update_gva_offset_mask, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_1(diag_btlb, void, env)
DEF_HELPER_1(diag_console_output, void, env)
#endif
diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode
index 71074a6..4eaac75 100644
--- a/target/hppa/insns.decode
+++ b/target/hppa/insns.decode
@@ -644,10 +644,12 @@ xmpyu 001110 ..... ..... 010 .0111 .00 t:5 r1=%ra64 r2=%rb64
# For 32-bit PA-7300LC (PCX-L2)
diag_getshadowregs_pa1 000101 00 0000 0000 0001 1010 0000 0000
diag_putshadowregs_pa1 000101 00 0000 0000 0001 1010 0100 0000
+ diag_mfdiag 000101 dr:5 rt:5 0000 0110 0000 0000
+ diag_mtdiag 000101 dr:5 r1:5 0001 0110 0000 0000
# For 64-bit PA8700 (PCX-W2)
- diag_getshadowregs_pa2 000101 00 0111 1000 0001 1000 0100 0000
- diag_putshadowregs_pa2 000101 00 0111 0000 0001 1000 0100 0000
+ diag_mfdiag 000101 dr:5 0 0000 0000 1000 101 rt:5
+ diag_mtdiag 000101 dr:5 r1:5 0001 1000 0100 0000
]
diag_unimp 000101 i:26
}
diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c
index 391f32f..191ae19 100644
--- a/target/hppa/int_helper.c
+++ b/target/hppa/int_helper.c
@@ -94,11 +94,12 @@ void hppa_cpu_do_interrupt(CPUState *cs)
HPPACPU *cpu = HPPA_CPU(cs);
CPUHPPAState *env = &cpu->env;
int i = cs->exception_index;
- uint64_t old_psw;
+ uint64_t old_psw, old_gva_offset_mask;
/* As documented in pa2.0 -- interruption handling. */
/* step 1 */
env->cr[CR_IPSW] = old_psw = cpu_hppa_get_psw(env);
+ old_gva_offset_mask = env->gva_offset_mask;
/* step 2 -- Note PSW_W is masked out again for pa1.x */
cpu_hppa_put_psw(env,
@@ -112,9 +113,9 @@ void hppa_cpu_do_interrupt(CPUState *cs)
*/
if (old_psw & PSW_C) {
env->cr[CR_IIASQ] =
- hppa_form_gva_psw(old_psw, env->iasq_f, env->iaoq_f) >> 32;
+ hppa_form_gva_mask(old_gva_offset_mask, env->iasq_f, env->iaoq_f) >> 32;
env->cr_back[0] =
- hppa_form_gva_psw(old_psw, env->iasq_b, env->iaoq_b) >> 32;
+ hppa_form_gva_mask(old_gva_offset_mask, env->iasq_b, env->iaoq_b) >> 32;
} else {
env->cr[CR_IIASQ] = 0;
env->cr_back[0] = 0;
@@ -165,9 +166,10 @@ void hppa_cpu_do_interrupt(CPUState *cs)
if (old_psw & PSW_C) {
int prot, t;
- vaddr = hppa_form_gva_psw(old_psw, env->iasq_f, vaddr);
+ vaddr = hppa_form_gva_mask(old_gva_offset_mask,
+ env->iasq_f, vaddr);
t = hppa_get_physical_address(env, vaddr, MMU_KERNEL_IDX,
- 0, &paddr, &prot);
+ 0, 0, &paddr, &prot);
if (t >= 0) {
/* We can't re-load the instruction. */
env->cr[CR_IIR] = 0;
@@ -175,6 +177,10 @@ void hppa_cpu_do_interrupt(CPUState *cs)
}
}
env->cr[CR_IIR] = ldl_phys(cs->as, paddr);
+ if (i == EXCP_ASSIST) {
+ /* stuff insn code into bits of FP exception register #1 */
+ env->fr[0] |= (env->cr[CR_IIR] & 0x03ffffff);
+ }
}
break;
diff --git a/target/hppa/machine.c b/target/hppa/machine.c
index 211bfcf..13e5551 100644
--- a/target/hppa/machine.c
+++ b/target/hppa/machine.c
@@ -198,6 +198,7 @@ static const VMStateField vmstate_env_fields[] = {
VMSTATE_UINT64(iasq_b, CPUHPPAState),
VMSTATE_UINT32(fr0_shadow, CPUHPPAState),
+ VMSTATE_UINT64_ARRAY(dr, CPUHPPAState, 32),
VMSTATE_END_OF_LIST()
};
@@ -208,14 +209,14 @@ static const VMStateDescription * const vmstate_env_subsections[] = {
static const VMStateDescription vmstate_env = {
.name = "env",
- .version_id = 3,
- .minimum_version_id = 3,
+ .version_id = 4,
+ .minimum_version_id = 4,
.fields = vmstate_env_fields,
.subsections = vmstate_env_subsections,
};
static const VMStateField vmstate_cpu_fields[] = {
- VMSTATE_CPU(),
+ VMSTATE_STRUCT(parent_obj, HPPACPU, 0, vmstate_cpu_common, CPUState),
VMSTATE_STRUCT(env, HPPACPU, 1, vmstate_env, CPUHPPAState),
VMSTATE_END_OF_LIST()
};
diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c
index b984f73..9bdd0a6 100644
--- a/target/hppa/mem_helper.c
+++ b/target/hppa/mem_helper.c
@@ -20,8 +20,11 @@
#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 "accel/tcg/probe.h"
#include "exec/page-protection.h"
+#include "exec/target_page.h"
#include "exec/helper-proto.h"
#include "hw/core/cpu.h"
#include "trace.h"
@@ -197,7 +200,7 @@ static int match_prot_id64(CPUHPPAState *env, uint32_t access_id)
}
int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
- int type, hwaddr *pphys, int *pprot)
+ int type, MemOp mop, hwaddr *pphys, int *pprot)
{
hwaddr phys;
int prot, r_prot, w_prot, x_prot, priv;
@@ -221,7 +224,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
g_assert_not_reached();
}
prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
- goto egress;
+ goto egress_align;
}
/* Find a valid tlb entry that matches the virtual address. */
@@ -267,6 +270,12 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
goto egress;
}
+ if (unlikely(!(prot & type))) {
+ /* Not allowed -- Inst/Data Memory Access Rights Fault. */
+ ret = (type & PAGE_EXEC) ? EXCP_IMP : EXCP_DMAR;
+ goto egress;
+ }
+
/* access_id == 0 means public page and no check is performed */
if (ent->access_id && MMU_IDX_TO_P(mmu_idx)) {
int access_prot = (hppa_is_pa20(env)
@@ -281,14 +290,8 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
prot &= access_prot;
}
- if (unlikely(!(prot & type))) {
- /* Not allowed -- Inst/Data Memory Access Rights Fault. */
- ret = (type & PAGE_EXEC) ? EXCP_IMP : EXCP_DMAR;
- goto egress;
- }
-
/*
- * In priority order, check for conditions which raise faults.
+ * In reverse priority order, check for conditions which raise faults.
* Remove PROT bits that cover the condition we want to check,
* so that the resulting PROT will force a re-check of the
* architectural TLB entry for the next access.
@@ -299,13 +302,15 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
/* The T bit is set -- Page Reference Fault. */
ret = EXCP_PAGE_REF;
}
- } else if (!ent->d) {
+ }
+ if (unlikely(!ent->d)) {
prot &= PAGE_READ | PAGE_EXEC;
if (type & PAGE_WRITE) {
/* The D bit is not set -- TLB Dirty Bit Fault. */
ret = EXCP_TLB_DIRTY;
}
- } else if (unlikely(ent->b)) {
+ }
+ if (unlikely(ent->b)) {
prot &= PAGE_READ | PAGE_EXEC;
if (type & PAGE_WRITE) {
/*
@@ -321,6 +326,11 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx,
}
}
+ egress_align:
+ if (addr & ((1u << memop_alignment_bits(mop)) - 1)) {
+ ret = EXCP_UNALIGN;
+ }
+
egress:
*pphys = phys;
*pprot = prot;
@@ -340,7 +350,7 @@ hwaddr hppa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
mmu_idx = (cpu->env.psw & PSW_D ? MMU_KERNEL_IDX :
cpu->env.psw & PSW_W ? MMU_ABS_W_IDX : MMU_ABS_IDX);
- excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx, 0,
+ excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx, 0, 0,
&phys, &prot);
/* Since we're translating for debugging, the only error that is a
@@ -417,12 +427,11 @@ void hppa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr,
}
}
-bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
- MMUAccessType type, int mmu_idx,
- bool probe, uintptr_t retaddr)
+bool hppa_cpu_tlb_fill_align(CPUState *cs, CPUTLBEntryFull *out, vaddr addr,
+ MMUAccessType type, int mmu_idx,
+ MemOp memop, int size, bool probe, uintptr_t ra)
{
- HPPACPU *cpu = HPPA_CPU(cs);
- CPUHPPAState *env = &cpu->env;
+ CPUHPPAState *env = cpu_env(cs);
int prot, excp, a_prot;
hwaddr phys;
@@ -438,7 +447,8 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
break;
}
- excp = hppa_get_physical_address(env, addr, mmu_idx, a_prot, &phys, &prot);
+ excp = hppa_get_physical_address(env, addr, mmu_idx, a_prot, memop,
+ &phys, &prot);
if (unlikely(excp >= 0)) {
if (probe) {
return false;
@@ -446,7 +456,7 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
trace_hppa_tlb_fill_excp(env, addr, size, type, mmu_idx);
/* Failure. Raise the indicated exception. */
- raise_exception_with_ior(env, excp, retaddr, addr,
+ raise_exception_with_ior(env, excp, ra, addr,
MMU_IDX_MMU_DISABLED(mmu_idx));
}
@@ -460,8 +470,12 @@ bool hppa_cpu_tlb_fill(CPUState *cs, vaddr addr, int size,
* the large page protection mask. We do not require this,
* because we record the large page here in the hppa tlb.
*/
- tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK,
- prot, mmu_idx, TARGET_PAGE_SIZE);
+ memset(out, 0, sizeof(*out));
+ out->phys_addr = phys;
+ out->prot = prot;
+ out->attrs = MEMTXATTRS_UNSPECIFIED;
+ out->lg_page_size = TARGET_PAGE_BITS;
+
return true;
}
@@ -678,7 +692,7 @@ target_ulong HELPER(lpa)(CPUHPPAState *env, target_ulong addr)
hwaddr phys;
int prot, excp;
- excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0,
+ excp = hppa_get_physical_address(env, addr, MMU_KERNEL_IDX, 0, 0,
&phys, &prot);
if (excp >= 0) {
if (excp == EXCP_DTLB_MISS) {
@@ -813,3 +827,8 @@ uint64_t HELPER(b_gate_priv)(CPUHPPAState *env, uint64_t iaoq_f)
}
return iaoq_f;
}
+
+void HELPER(update_gva_offset_mask)(CPUHPPAState *env)
+{
+ update_gva_offset_mask(env);
+}
diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c
index 7f79196..0458378 100644
--- a/target/hppa/op_helper.c
+++ b/target/hppa/op_helper.c
@@ -20,11 +20,14 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "cpu.h"
-#include "exec/exec-all.h"
#include "exec/helper-proto.h"
-#include "exec/cpu_ldst.h"
+#include "accel/tcg/cpu-ldst.h"
+#include "accel/tcg/probe.h"
#include "qemu/timer.h"
#include "trace.h"
+#ifdef CONFIG_USER_ONLY
+#include "user/page-protection.h"
+#endif
G_NORETURN void HELPER(excp)(CPUHPPAState *env, int excp)
{
@@ -334,7 +337,7 @@ target_ulong HELPER(probe)(CPUHPPAState *env, target_ulong addr,
}
mmu_idx = PRIV_P_TO_MMU_IDX(level, env->psw & PSW_P);
- excp = hppa_get_physical_address(env, addr, mmu_idx, 0, &phys, &prot);
+ excp = hppa_get_physical_address(env, addr, mmu_idx, 0, 0, &phys, &prot);
if (excp >= 0) {
cpu_restore_state(env_cpu(env), GETPC());
hppa_set_ior_and_isr(env, addr, MMU_IDX_MMU_DISABLED(mmu_idx));
diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c
index 9b43b55..6e65fad 100644
--- a/target/hppa/sys_helper.c
+++ b/target/hppa/sys_helper.c
@@ -20,11 +20,10 @@
#include "qemu/osdep.h"
#include "qemu/log.h"
#include "cpu.h"
-#include "exec/exec-all.h"
#include "exec/helper-proto.h"
#include "qemu/timer.h"
-#include "sysemu/runstate.h"
-#include "sysemu/sysemu.h"
+#include "system/runstate.h"
+#include "system/system.h"
#include "chardev/char-fe.h"
void HELPER(write_interval_timer)(CPUHPPAState *env, target_ulong val)
@@ -73,7 +72,7 @@ target_ulong HELPER(swap_system_mask)(CPUHPPAState *env, target_ulong nsm)
* machines set the Q bit from 0 to 1 without an exception,
* so let this go without comment.
*/
- env->psw = (psw & ~PSW_SM) | (nsm & PSW_SM);
+ cpu_hppa_put_psw(env, (psw & ~PSW_SM) | (nsm & PSW_SM));
return psw & PSW_SM;
}
@@ -88,7 +87,7 @@ void HELPER(rfi)(CPUHPPAState *env)
* To recreate the space identifier, remove the offset bits.
* For pa1.x, the mask reduces to no change to space.
*/
- mask = gva_offset_mask(env->psw);
+ mask = env->gva_offset_mask;
env->iaoq_f = env->cr[CR_IIAOQ];
env->iaoq_b = env->cr_back[1];
diff --git a/target/hppa/translate.c b/target/hppa/translate.c
index 51c1762..7a81cfc 100644
--- a/target/hppa/translate.c
+++ b/target/hppa/translate.c
@@ -20,13 +20,14 @@
#include "qemu/osdep.h"
#include "cpu.h"
#include "qemu/host-utils.h"
-#include "exec/exec-all.h"
#include "exec/page-protection.h"
#include "tcg/tcg-op.h"
#include "tcg/tcg-op-gvec.h"
#include "exec/helper-proto.h"
#include "exec/helper-gen.h"
#include "exec/translator.h"
+#include "exec/translation-block.h"
+#include "exec/target_page.h"
#include "exec/log.h"
#define HELPER_H "helper.h"
@@ -72,6 +73,7 @@ typedef struct DisasContext {
/* IAOQ_Front at entry to TB. */
uint64_t iaoq_first;
+ uint64_t gva_offset_mask;
DisasCond null_cond;
TCGLabel *null_lab;
@@ -1206,10 +1208,10 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1,
cb_msb = tcg_temp_new_i64();
cb = tcg_temp_new_i64();
- tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero);
if (is_c) {
- tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb,
- get_psw_carry(ctx, d), ctx->zero);
+ tcg_gen_addcio_i64(dest, cb_msb, in1, in2, get_psw_carry(ctx, d));
+ } else {
+ tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero);
}
tcg_gen_xor_i64(cb, in1, in2);
tcg_gen_xor_i64(cb, cb, dest);
@@ -1305,9 +1307,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1,
if (is_b) {
/* DEST,C = IN1 + ~IN2 + C. */
tcg_gen_not_i64(cb, in2);
- tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero,
- get_psw_carry(ctx, d), ctx->zero);
- tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, cb, ctx->zero);
+ tcg_gen_addcio_i64(dest, cb_msb, in1, cb, get_psw_carry(ctx, d));
tcg_gen_xor_i64(cb, cb, in1);
tcg_gen_xor_i64(cb, cb, dest);
} else {
@@ -1576,7 +1576,7 @@ static void form_gva(DisasContext *ctx, TCGv_i64 *pgva, TCGv_i64 *pofs,
*pofs = ofs;
*pgva = addr = tcg_temp_new_i64();
tcg_gen_andi_i64(addr, modify <= 0 ? ofs : base,
- gva_offset_mask(ctx->tb_flags));
+ ctx->gva_offset_mask);
#ifndef CONFIG_USER_ONLY
if (!is_phys) {
tcg_gen_or_i64(addr, addr, space_select(ctx, sp, base));
@@ -3005,9 +3005,7 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a)
tcg_gen_xor_i64(add2, in2, addc);
tcg_gen_andi_i64(addc, addc, 1);
- tcg_gen_add2_i64(dest, cpu_psw_cb_msb, add1, ctx->zero, add2, ctx->zero);
- tcg_gen_add2_i64(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb,
- addc, ctx->zero);
+ tcg_gen_addcio_i64(dest, cpu_psw_cb_msb, add1, add2, addc);
/* Write back the result register. */
save_gpr(ctx, a->t, dest);
@@ -3550,8 +3548,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_i64 in1,
TCGv_i64 cb = tcg_temp_new_i64();
TCGv_i64 cb_msb = tcg_temp_new_i64();
- tcg_gen_movi_i64(cb_msb, 0);
- tcg_gen_add2_i64(dest, cb_msb, in1, cb_msb, in2, cb_msb);
+ tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero);
tcg_gen_xor_i64(cb, in1, in2);
tcg_gen_xor_i64(cb, cb, dest);
cb_cond = get_carry(ctx, d, cb, cb_msb);
@@ -4592,19 +4589,37 @@ static bool trans_diag_getshadowregs_pa1(DisasContext *ctx, arg_empty *a)
return !ctx->is_pa20 && do_getshadowregs(ctx);
}
-static bool trans_diag_getshadowregs_pa2(DisasContext *ctx, arg_empty *a)
+static bool trans_diag_putshadowregs_pa1(DisasContext *ctx, arg_empty *a)
{
- return ctx->is_pa20 && do_getshadowregs(ctx);
+ return !ctx->is_pa20 && do_putshadowregs(ctx);
}
-static bool trans_diag_putshadowregs_pa1(DisasContext *ctx, arg_empty *a)
+static bool trans_diag_mfdiag(DisasContext *ctx, arg_diag_mfdiag *a)
{
- return !ctx->is_pa20 && do_putshadowregs(ctx);
+ CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+ nullify_over(ctx);
+ TCGv_i64 dest = dest_gpr(ctx, a->rt);
+ tcg_gen_ld_i64(dest, tcg_env,
+ offsetof(CPUHPPAState, dr[a->dr]));
+ save_gpr(ctx, a->rt, dest);
+ return nullify_end(ctx);
}
-static bool trans_diag_putshadowregs_pa2(DisasContext *ctx, arg_empty *a)
+static bool trans_diag_mtdiag(DisasContext *ctx, arg_diag_mtdiag *a)
{
- return ctx->is_pa20 && do_putshadowregs(ctx);
+ CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+ nullify_over(ctx);
+ tcg_gen_st_i64(load_gpr(ctx, a->r1), tcg_env,
+ offsetof(CPUHPPAState, dr[a->dr]));
+#ifndef CONFIG_USER_ONLY
+ if (ctx->is_pa20 && (a->dr == 2)) {
+ /* Update gva_offset_mask from the new value of %dr2 */
+ gen_helper_update_gva_offset_mask(tcg_env);
+ /* Exit to capture the new value for the next TB. */
+ ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT;
+ }
+#endif
+ return nullify_end(ctx);
}
static bool trans_diag_unimp(DisasContext *ctx, arg_diag_unimp *a)
@@ -4624,6 +4639,7 @@ static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
ctx->tb_flags = ctx->base.tb->flags;
ctx->is_pa20 = hppa_is_pa20(cpu_env(cs));
ctx->psw_xb = ctx->tb_flags & (PSW_X | PSW_B);
+ ctx->gva_offset_mask = cpu_env(cs)->gva_offset_mask;
#ifdef CONFIG_USER_ONLY
ctx->privilege = PRIV_USER;
@@ -4868,8 +4884,8 @@ static const TranslatorOps hppa_tr_ops = {
#endif
};
-void gen_intermediate_code(CPUState *cs, TranslationBlock *tb, int *max_insns,
- vaddr pc, void *host_pc)
+void hppa_translate_code(CPUState *cs, TranslationBlock *tb,
+ int *max_insns, vaddr pc, void *host_pc)
{
DisasContext ctx = { };
translator_loop(cs, tb, max_insns, pc, host_pc, &hppa_tr_ops, &ctx.base);