#define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i64
#define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i64
#define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i64
-#if UINTPTR_MAX == UINT32_MAX
-# define tcg_gen_trunc_reg_ptr(p, r) \
- tcg_gen_trunc_i64_i32(TCGV_PTR_TO_NAT(p), r)
-#else
-# define tcg_gen_trunc_reg_ptr(p, r) \
- tcg_gen_mov_i64(TCGV_PTR_TO_NAT(p), r)
-#endif
+#define tcg_gen_trunc_reg_ptr tcg_gen_trunc_i64_ptr
#else
#define TCGv_reg TCGv_i32
#define tcg_temp_new tcg_temp_new_i32
#define tcg_gen_qemu_ld_reg tcg_gen_qemu_ld_i32
#define tcg_gen_qemu_st_reg tcg_gen_qemu_st_i32
#define tcg_gen_atomic_xchg_reg tcg_gen_atomic_xchg_i32
-#if UINTPTR_MAX == UINT32_MAX
-# define tcg_gen_trunc_reg_ptr(p, r) \
- tcg_gen_mov_i32(TCGV_PTR_TO_NAT(p), r)
-#else
-# define tcg_gen_trunc_reg_ptr(p, r) \
- tcg_gen_extu_i32_i64(TCGV_PTR_TO_NAT(p), r)
-#endif
+#define tcg_gen_trunc_reg_ptr tcg_gen_ext_i32_ptr
#endif /* TARGET_REGISTER_BITS */
typedef struct DisasCond {
TCGv_reg iaoq_n_var;
int ntempr, ntempl;
- TCGv_reg tempr[4];
+ TCGv_reg tempr[8];
TCGv_tl templ[4];
DisasCond null_cond;
TCGLabel *null_lab;
uint32_t insn;
+ uint32_t tb_flags;
int mmu_idx;
int privilege;
bool psw_n_nonzero;
/* global register indexes */
static TCGv_reg cpu_gr[32];
static TCGv_i64 cpu_sr[4];
+static TCGv_i64 cpu_srH;
static TCGv_reg cpu_iaoq_f;
static TCGv_reg cpu_iaoq_b;
static TCGv_i64 cpu_iasq_f;
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"
};
/* SR[4-7] are not global registers so that we can index them. */
- static const char sr_names[4][4] = {
- "sr0", "sr1", "sr2", "sr3"
+ static const char sr_names[5][4] = {
+ "sr0", "sr1", "sr2", "sr3", "srH"
};
int i;
offsetof(CPUHPPAState, sr[i]),
sr_names[i]);
}
+ cpu_srH = tcg_global_mem_new_i64(cpu_env,
+ offsetof(CPUHPPAState, sr[4]),
+ sr_names[4]);
for (i = 0; i < ARRAY_SIZE(vars); ++i) {
const GlobalVar *v = &vars[i];
#else
if (reg < 4) {
tcg_gen_mov_i64(dest, cpu_sr[reg]);
+ } else if (ctx->tb_flags & TB_FLAG_SR_SAME) {
+ tcg_gen_mov_i64(dest, cpu_srH);
} else {
tcg_gen_ld_i64(dest, cpu_env, offsetof(CPUHPPAState, sr[reg]));
}
load_spr(ctx, spc, sp);
return spc;
}
+ if (ctx->tb_flags & TB_FLAG_SR_SAME) {
+ return cpu_srH;
+ }
ptr = tcg_temp_new_ptr();
tmp = tcg_temp_new();
#else
TCGv_tl addr = get_temp_tl(ctx);
tcg_gen_extu_reg_tl(addr, modify <= 0 ? ofs : base);
- if (ctx->base.tb->flags & PSW_W) {
+ if (ctx->tb_flags & PSW_W) {
tcg_gen_andi_tl(addr, addr, 0x3fffffffffffffffull);
}
if (!is_phys) {
*/
static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset)
{
-#ifdef CONFIG_USER_ONLY
- return offset;
-#else
TCGv_reg dest;
switch (ctx->privilege) {
case 0:
break;
}
return dest;
-#endif
}
#ifdef CONFIG_USER_ONLY
goto do_sigill;
}
- switch (ctx->iaoq_f) {
+ switch (ctx->iaoq_f & -4) {
case 0x00: /* Null pointer call */
gen_excp_1(EXCP_IMP);
return DISAS_NORETURN;
case 0xe0: /* SET_THREAD_POINTER */
tcg_gen_st_reg(cpu_gr[26], cpu_env, offsetof(CPUHPPAState, cr[27]));
- tcg_gen_mov_reg(cpu_iaoq_f, cpu_gr[31]);
+ tcg_gen_ori_reg(cpu_iaoq_f, cpu_gr[31], 3);
tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4);
return DISAS_IAQ_N_UPDATED;
if (rs >= 4) {
tcg_gen_st_i64(t64, cpu_env, offsetof(CPUHPPAState, sr[rs]));
+ ctx->tb_flags &= ~TB_FLAG_SR_SAME;
} else {
tcg_gen_mov_i64(cpu_sr[rs], t64);
}
unsigned rt = extract32(insn, 0, 5);
TCGv_reg dest = dest_gpr(ctx, rt);
- /* Since we don't implement space registers, this returns zero. */
+#ifdef CONFIG_USER_ONLY
+ /* We don't implement space registers in user mode. */
tcg_gen_movi_reg(dest, 0);
+#else
+ unsigned rb = extract32(insn, 21, 5);
+ unsigned sp = extract32(insn, 14, 2);
+ TCGv_i64 t0 = tcg_temp_new_i64();
+
+ tcg_gen_mov_i64(t0, space_select(ctx, sp, load_gpr(ctx, rb)));
+ tcg_gen_shri_i64(t0, t0, 32);
+ tcg_gen_trunc_i64_reg(dest, t0);
+
+ tcg_temp_free_i64(t0);
+#endif
save_gpr(ctx, rt, dest);
cond_free(&ctx->null_cond);
/* Exit the TB to recognize new interrupts. */
return nullify_end(ctx, DISAS_NORETURN);
}
+
+static DisasJumpType gen_hlt(DisasContext *ctx, int reset)
+{
+ CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+ nullify_over(ctx);
+ if (reset) {
+ gen_helper_reset(cpu_env);
+ } else {
+ gen_helper_halt(cpu_env);
+ }
+ return nullify_end(ctx, DISAS_NORETURN);
+}
#endif /* !CONFIG_USER_ONLY */
static const DisasInsn table_system[] = {
{ 0x000014a0u, 0xffffffe0u, trans_mfia },
{ 0x000004a0u, 0xffff1fe0u, trans_mfsp },
{ 0x000008a0u, 0xfc1fbfe0u, trans_mfctl },
- { 0x00000400u, 0xffffffffu, trans_sync },
+ { 0x00000400u, 0xffffffffu, trans_sync }, /* sync */
+ { 0x00100400u, 0xffffffffu, trans_sync }, /* syncdma */
{ 0x000010a0u, 0xfc1f3fe0u, trans_ldsid },
#ifndef CONFIG_USER_ONLY
{ 0x00000e60u, 0xfc00ffe0u, trans_rsm },
{
unsigned rt = extract32(insn, 0, 5);
unsigned sp = extract32(insn, 14, 2);
+ unsigned rr = extract32(insn, 16, 5);
unsigned rb = extract32(insn, 21, 5);
unsigned is_write = extract32(insn, 6, 1);
+ unsigned is_imm = extract32(insn, 13, 1);
TCGv_reg dest, ofs;
+ TCGv_i32 level, want;
TCGv_tl addr;
nullify_over(ctx);
- /* ??? Do something with priv level operand. */
dest = dest_gpr(ctx, rt);
form_gva(ctx, &addr, &ofs, rb, 0, 0, 0, sp, 0, false);
- if (is_write) {
- gen_helper_probe_w(dest, addr);
+
+ if (is_imm) {
+ level = tcg_const_i32(extract32(insn, 16, 2));
} else {
- gen_helper_probe_r(dest, addr);
+ level = tcg_temp_new_i32();
+ tcg_gen_trunc_reg_i32(level, load_gpr(ctx, rr));
+ tcg_gen_andi_i32(level, level, 3);
}
+ want = tcg_const_i32(is_write ? PAGE_WRITE : PAGE_READ);
+
+ gen_helper_probe(dest, cpu_env, addr, level, want);
+
+ tcg_temp_free_i32(want);
+ tcg_temp_free_i32(level);
+
save_gpr(ctx, rt, dest);
return nullify_end(ctx, DISAS_NEXT);
}
/* Exit TB for ITLB change if mmu is enabled. This *should* not be
the case, since the OS TLB fill handler runs with mmu disabled. */
- return nullify_end(ctx, !is_data && (ctx->base.tb->flags & PSW_C)
+ return nullify_end(ctx, !is_data && (ctx->tb_flags & PSW_C)
? DISAS_IAQ_N_STALE : DISAS_NEXT);
}
}
/* Exit TB for TLB change if mmu is enabled. */
- return nullify_end(ctx, !is_data && (ctx->base.tb->flags & PSW_C)
+ return nullify_end(ctx, !is_data && (ctx->tb_flags & PSW_C)
? DISAS_IAQ_N_STALE : DISAS_NEXT);
}
return nullify_end(ctx, DISAS_NEXT);
}
+
+static DisasJumpType trans_lci(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned rt = extract32(insn, 0, 5);
+ TCGv_reg ci;
+
+ CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+
+ /* The Coherence Index is an implementation-defined function of the
+ physical address. Two addresses with the same CI have a coherent
+ view of the cache. Our implementation is to return 0 for all,
+ since the entire address space is coherent. */
+ ci = tcg_const_reg(0);
+ save_gpr(ctx, rt, ci);
+ tcg_temp_free(ci);
+
+ return DISAS_NEXT;
+}
#endif /* !CONFIG_USER_ONLY */
static const DisasInsn table_mem_mgmt[] = {
{ 0x04001200u, 0xfc001fdfu, trans_pxtlbx }, /* pdtlb */
{ 0x04001240u, 0xfc001fdfu, trans_pxtlbx }, /* pdtlbe */
{ 0x04001340u, 0xfc003fc0u, trans_lpa },
+ { 0x04001300u, 0xfc003fe0u, trans_lci },
#endif
};
return nullify_end(ctx, DISAS_NEXT);
}
+#ifndef CONFIG_USER_ONLY
+/* These are QEMU extensions and are nops in the real architecture:
+ *
+ * or %r10,%r10,%r10 -- idle loop; wait for interrupt
+ * or %r31,%r31,%r31 -- death loop; offline cpu
+ * currently implemented as idle.
+ */
+static DisasJumpType trans_pause(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ TCGv_i32 tmp;
+
+ /* No need to check for supervisor, as userland can only pause
+ until the next timer interrupt. */
+ nullify_over(ctx);
+
+ /* Advance the instruction queue. */
+ copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b);
+ copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var);
+ nullify_set(ctx, 0);
+
+ /* Tell the qemu main loop to halt until this cpu has work. */
+ tmp = tcg_const_i32(1);
+ tcg_gen_st_i32(tmp, cpu_env, -offsetof(HPPACPU, env) +
+ offsetof(CPUState, halted));
+ tcg_temp_free_i32(tmp);
+ gen_excp_1(EXCP_HALTED);
+
+ return nullify_end(ctx, DISAS_NORETURN);
+}
+#endif
+
static const DisasInsn table_arith_log[] = {
{ 0x08000240u, 0xfc00ffffu, trans_nop }, /* or x,y,0 */
{ 0x08000240u, 0xffe0ffe0u, trans_copy }, /* or x,0,t */
+#ifndef CONFIG_USER_ONLY
+ { 0x094a024au, 0xffffffffu, trans_pause }, /* or r10,r10,r10 */
+ { 0x0bff025fu, 0xffffffffu, trans_pause }, /* or r31,r31,r31 */
+#endif
{ 0x08000000u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_andc_reg },
{ 0x08000200u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_and_reg },
{ 0x08000240u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_or_reg },
ctx->mmu_idx = hold_mmu_idx;
return ret;
}
+
+static DisasJumpType trans_stwa_idx_i(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ int hold_mmu_idx = ctx->mmu_idx;
+ DisasJumpType ret;
+
+ CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR);
+
+ /* ??? needs fixing for hppa64 -- ldda does not follow the same
+ format wrt the sub-opcode in bits 6:9. */
+ ctx->mmu_idx = MMU_PHYS_IDX;
+ ret = trans_st_idx_i(ctx, insn, di);
+ ctx->mmu_idx = hold_mmu_idx;
+ return ret;
+}
#endif
static const DisasInsn table_index_mem[] = {
{ 0x0c0001c0u, 0xfc0003c0, trans_ldcw },
{ 0x0c001300u, 0xfc0013c0, trans_stby },
#ifndef CONFIG_USER_ONLY
- { 0x0c001180u, 0xfc00d3c0, trans_ldwa_idx_i }, /* LDWA, im */
{ 0x0c000180u, 0xfc00d3c0, trans_ldwa_idx_x }, /* LDWA, rx */
+ { 0x0c001180u, 0xfc00d3c0, trans_ldwa_idx_i }, /* LDWA, im */
+ { 0x0c001380u, 0xfc00d3c0, trans_stwa_idx_i }, /* STWA, im */
#endif
};
/* FSTW without modification. */
return do_fstorew(ctx, ext2 * 32 + rt, rb, 0, 0, i, sp, 0);
case 2:
- /* LDW with modification. */
+ /* STW with modification. */
return do_store(ctx, rt, rb, i, sp, (i < 0 ? 1 : -1), MO_TEUL);
default:
return gen_illegal(ctx);
return do_dbranch(ctx, iaoq_dest(ctx, disp), link, n);
}
+static DisasJumpType trans_b_gate(DisasContext *ctx, uint32_t insn,
+ const DisasInsn *di)
+{
+ unsigned n = extract32(insn, 1, 1);
+ unsigned link = extract32(insn, 21, 5);
+ target_sreg disp = assemble_17(insn);
+ target_ureg dest = iaoq_dest(ctx, disp);
+
+ /* Make sure the caller hasn't done something weird with the queue.
+ * ??? This is not quite the same as the PSW[B] bit, which would be
+ * expensive to track. Real hardware will trap for
+ * b gateway
+ * b gateway+4 (in delay slot of first branch)
+ * However, checking for a non-sequential instruction queue *will*
+ * diagnose the security hole
+ * b gateway
+ * b evil
+ * in which instructions at evil would run with increased privs.
+ */
+ if (ctx->iaoq_b == -1 || ctx->iaoq_b != ctx->iaoq_f + 4) {
+ return gen_illegal(ctx);
+ }
+
+#ifndef CONFIG_USER_ONLY
+ if (ctx->tb_flags & PSW_C) {
+ CPUHPPAState *env = ctx->cs->env_ptr;
+ int type = hppa_artype_for_page(env, ctx->base.pc_next);
+ /* If we could not find a TLB entry, then we need to generate an
+ ITLB miss exception so the kernel will provide it.
+ The resulting TLB fill operation will invalidate this TB and
+ we will re-translate, at which point we *will* be able to find
+ the TLB entry and determine if this is in fact a gateway page. */
+ if (type < 0) {
+ return gen_excp(ctx, EXCP_ITLB_MISS);
+ }
+ /* No change for non-gateway pages or for priv decrease. */
+ if (type >= 4 && type - 4 < ctx->privilege) {
+ dest = deposit32(dest, 0, 2, type - 4);
+ }
+ } else {
+ dest &= -4; /* priv = 0 */
+ }
+#endif
+
+ return do_dbranch(ctx, dest, link, n);
+}
+
static DisasJumpType trans_bl_long(DisasContext *ctx, uint32_t insn,
const DisasInsn *di)
{
{ 0xe8004000u, 0xfc00fffdu, trans_blr },
{ 0xe800c000u, 0xfc00fffdu, trans_bv },
{ 0xe800d000u, 0xfc00dffcu, trans_bve },
+ { 0xe8002000u, 0xfc00e000u, trans_b_gate },
};
static DisasJumpType trans_fop_wew_0c(DisasContext *ctx, uint32_t insn,
/* floating point class one */
/* float/float */
{ 0x38000a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_s },
- { 0x38002200, 0xfc1fffc0, FOP_DEW = gen_helper_fcnv_s_d },
+ { 0x38002200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_d },
/* int/float */
- { 0x38008200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_w_s },
+ { 0x38008200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_w_s },
{ 0x38008a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_dw_s },
{ 0x3800a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_w_d },
{ 0x3800aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d },
/* float/int */
- { 0x38010200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_s_w },
+ { 0x38010200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_s_w },
{ 0x38010a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_w },
{ 0x38012200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_dw },
{ 0x38012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw },
/* float/int truncate */
- { 0x38018200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_t_s_w },
+ { 0x38018200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_t_s_w },
{ 0x38018a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_w },
{ 0x3801a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_dw },
{ 0x3801aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw },
/* uint/float */
- { 0x38028200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_uw_s },
+ { 0x38028200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_uw_s },
{ 0x38028a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_udw_s },
{ 0x3802a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_uw_d },
{ 0x3802aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d },
/* float/uint */
- { 0x38030200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_s_uw },
+ { 0x38030200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_s_uw },
{ 0x38030a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_uw },
{ 0x38032200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_udw },
{ 0x38032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw },
/* float/uint truncate */
- { 0x38038200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_t_s_uw },
+ { 0x38038200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_t_s_uw },
{ 0x38038a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_uw },
{ 0x3803a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_udw },
{ 0x3803aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw },
case 0x15: /* unassigned */
case 0x1D: /* unassigned */
case 0x37: /* unassigned */
- case 0x3F: /* unassigned */
+ break;
+ case 0x3F:
+#ifndef CONFIG_USER_ONLY
+ /* Unassigned, but use as system-halt. */
+ if (insn == 0xfffdead0) {
+ return gen_hlt(ctx, 0); /* halt system */
+ }
+ if (insn == 0xfffdead1) {
+ return gen_hlt(ctx, 1); /* reset system */
+ }
+#endif
+ break;
default:
break;
}
return gen_illegal(ctx);
}
-static int hppa_tr_init_disas_context(DisasContextBase *dcbase,
- CPUState *cs, int max_insns)
+static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs)
{
DisasContext *ctx = container_of(dcbase, DisasContext, base);
int bound;
ctx->cs = cs;
+ ctx->tb_flags = ctx->base.tb->flags;
#ifdef CONFIG_USER_ONLY
ctx->privilege = MMU_USER_IDX;
ctx->mmu_idx = MMU_USER_IDX;
- ctx->iaoq_f = ctx->base.pc_first;
- ctx->iaoq_b = ctx->base.tb->cs_base;
+ ctx->iaoq_f = ctx->base.pc_first | MMU_USER_IDX;
+ ctx->iaoq_b = ctx->base.tb->cs_base | MMU_USER_IDX;
#else
- ctx->privilege = (ctx->base.tb->flags >> TB_FLAG_PRIV_SHIFT) & 3;
- ctx->mmu_idx = (ctx->base.tb->flags & PSW_D
- ? ctx->privilege : MMU_PHYS_IDX);
+ ctx->privilege = (ctx->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3;
+ ctx->mmu_idx = (ctx->tb_flags & PSW_D ? ctx->privilege : MMU_PHYS_IDX);
/* Recover the IAOQ values from the GVA + PRIV. */
uint64_t cs_base = ctx->base.tb->cs_base;
/* Bound the number of instructions by those left on the page. */
bound = -(ctx->base.pc_first | TARGET_PAGE_MASK) / 4;
- bound = MIN(max_insns, bound);
+ ctx->base.max_insns = MIN(ctx->base.max_insns, bound);
ctx->ntempr = 0;
ctx->ntempl = 0;
memset(ctx->tempr, 0, sizeof(ctx->tempr));
memset(ctx->templ, 0, sizeof(ctx->templ));
-
- return bound;
}
static void hppa_tr_tb_start(DisasContextBase *dcbase, CPUState *cs)
/* Seed the nullification status from PSW[N], as saved in TB->FLAGS. */
ctx->null_cond = cond_make_f();
ctx->psw_n_nonzero = false;
- if (ctx->base.tb->flags & PSW_N) {
+ if (ctx->tb_flags & PSW_N) {
ctx->null_cond.c = TCG_COND_ALWAYS;
ctx->psw_n_nonzero = true;
}