#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;
tcg_gen_goto_tb(which);
tcg_gen_movi_reg(cpu_iaoq_f, f);
tcg_gen_movi_reg(cpu_iaoq_b, b);
- tcg_gen_exit_tb((uintptr_t)ctx->base.tb + which);
+ tcg_gen_exit_tb(ctx->base.tb, which);
} else {
copy_iaoq_entry(cpu_iaoq_f, f, cpu_iaoq_b);
copy_iaoq_entry(cpu_iaoq_b, b, ctx->iaoq_n_var);
*/
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;
/* FIXME: Respect PSW_S bit. */
nullify_over(ctx);
tmp = dest_gpr(ctx, rt);
- if (ctx->base.tb->cflags & CF_USE_ICOUNT) {
+ if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) {
gen_io_start();
gen_helper_read_interval_timer(tmp);
gen_io_end();
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);
if (ctx->base.singlestep_enabled) {
gen_excp_1(EXCP_DEBUG);
} else {
- tcg_gen_exit_tb(0);
+ tcg_gen_exit_tb(NULL, 0);
}
/* Exit the TB to recognize new interrupts. */
{
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);
}
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 },
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;
#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->tb_flags >> TB_FLAG_PRIV_SHIFT) & 3;
ctx->mmu_idx = (ctx->tb_flags & PSW_D ? ctx->privilege : MMU_PHYS_IDX);
/* 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)
if (ctx->base.singlestep_enabled) {
gen_excp_1(EXCP_DEBUG);
} else if (is_jmp == DISAS_IAQ_N_STALE_EXIT) {
- tcg_gen_exit_tb(0);
+ tcg_gen_exit_tb(NULL, 0);
} else {
tcg_gen_lookup_and_goto_ptr();
}