diff options
Diffstat (limited to 'target/hppa')
-rw-r--r-- | target/hppa/cpu-param.h | 12 | ||||
-rw-r--r-- | target/hppa/cpu.c | 76 | ||||
-rw-r--r-- | target/hppa/cpu.h | 62 | ||||
-rw-r--r-- | target/hppa/fpu_helper.c | 51 | ||||
-rw-r--r-- | target/hppa/helper.c | 28 | ||||
-rw-r--r-- | target/hppa/helper.h | 1 | ||||
-rw-r--r-- | target/hppa/insns.decode | 6 | ||||
-rw-r--r-- | target/hppa/int_helper.c | 16 | ||||
-rw-r--r-- | target/hppa/machine.c | 7 | ||||
-rw-r--r-- | target/hppa/mem_helper.c | 65 | ||||
-rw-r--r-- | target/hppa/op_helper.c | 9 | ||||
-rw-r--r-- | target/hppa/sys_helper.c | 9 | ||||
-rw-r--r-- | target/hppa/translate.c | 58 |
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); |