* CPU's index into a TCG temp, since the first callback did it already.
*/
#include "qemu/osdep.h"
+#include "cpu.h"
#include "tcg/tcg.h"
+#include "tcg/tcg-temp-internal.h"
#include "tcg/tcg-op.h"
#include "exec/exec-all.h"
#include "exec/plugin-gen.h"
#include "exec/translator.h"
+#include "exec/helper-proto-common.h"
+
+#define HELPER_H "accel/tcg/plugin-helpers.h"
+#include "exec/helper-info.c.inc"
+#undef HELPER_H
#ifdef CONFIG_SOFTMMU
# define CONFIG_SOFTMMU_GATE 1
void *userdata)
{ }
-static void do_gen_mem_cb(TCGv vaddr, uint32_t info)
-{
- TCGv_i32 cpu_index = tcg_temp_new_i32();
- TCGv_i32 meminfo = tcg_const_i32(info);
- TCGv_i64 vaddr64 = tcg_temp_new_i64();
- TCGv_ptr udata = tcg_const_ptr(NULL);
-
- tcg_gen_ld_i32(cpu_index, cpu_env,
- -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
- tcg_gen_extu_tl_i64(vaddr64, vaddr);
-
- gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, vaddr64, udata);
-
- tcg_temp_free_ptr(udata);
- tcg_temp_free_i64(vaddr64);
- tcg_temp_free_i32(meminfo);
- tcg_temp_free_i32(cpu_index);
-}
-
static void gen_empty_udata_cb(void)
{
- TCGv_i32 cpu_index = tcg_temp_new_i32();
- TCGv_ptr udata = tcg_const_ptr(NULL); /* will be overwritten later */
+ TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
+ TCGv_ptr udata = tcg_temp_ebb_new_ptr();
- tcg_gen_ld_i32(cpu_index, cpu_env,
+ tcg_gen_movi_ptr(udata, 0);
+ tcg_gen_ld_i32(cpu_index, tcg_env,
-offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
gen_helper_plugin_vcpu_udata_cb(cpu_index, udata);
*/
static void gen_empty_inline_cb(void)
{
- TCGv_i64 val = tcg_temp_new_i64();
- TCGv_ptr ptr = tcg_const_ptr(NULL); /* overwritten later */
+ TCGv_i64 val = tcg_temp_ebb_new_i64();
+ TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
+ tcg_gen_movi_ptr(ptr, 0);
tcg_gen_ld_i64(val, ptr, 0);
/* pass an immediate != 0 so that it doesn't get optimized away */
tcg_gen_addi_i64(val, val, 0xdeadface);
tcg_temp_free_i64(val);
}
-static void gen_empty_mem_cb(TCGv addr, uint32_t info)
+static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info)
{
- do_gen_mem_cb(addr, info);
+ TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
+ TCGv_i32 meminfo = tcg_temp_ebb_new_i32();
+ TCGv_ptr udata = tcg_temp_ebb_new_ptr();
+
+ tcg_gen_movi_i32(meminfo, info);
+ tcg_gen_movi_ptr(udata, 0);
+ tcg_gen_ld_i32(cpu_index, tcg_env,
+ -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
+
+ gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, addr, udata);
+
+ tcg_temp_free_ptr(udata);
+ tcg_temp_free_i32(meminfo);
+ tcg_temp_free_i32(cpu_index);
}
/*
*/
static void gen_empty_mem_helper(void)
{
- TCGv_ptr ptr;
+ TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
- ptr = tcg_const_ptr(NULL);
- tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) -
+ tcg_gen_movi_ptr(ptr, 0);
+ tcg_gen_st_ptr(ptr, tcg_env, offsetof(CPUState, plugin_mem_cbs) -
offsetof(ArchCPU, env));
tcg_temp_free_ptr(ptr);
}
}
}
-union mem_gen_fn {
- void (*mem_fn)(TCGv, uint32_t);
- void (*inline_fn)(void);
-};
-
-static void gen_mem_wrapped(enum plugin_gen_cb type,
- const union mem_gen_fn *f, TCGv addr,
- uint32_t info, bool is_mem)
+void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info)
{
enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info);
- gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, type, rw);
- if (is_mem) {
- f->mem_fn(addr, info);
- } else {
- f->inline_fn();
- }
+ gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_MEM, rw);
+ gen_empty_mem_cb(addr, info);
tcg_gen_plugin_cb_end();
-}
-void plugin_gen_empty_mem_callback(TCGv addr, uint32_t info)
-{
- union mem_gen_fn fn;
-
- fn.mem_fn = gen_empty_mem_cb;
- gen_mem_wrapped(PLUGIN_GEN_CB_MEM, &fn, addr, info, true);
-
- fn.inline_fn = gen_empty_inline_cb;
- gen_mem_wrapped(PLUGIN_GEN_CB_INLINE, &fn, 0, info, false);
+ gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_INLINE, rw);
+ gen_empty_inline_cb();
+ tcg_gen_plugin_cb_end();
}
static TCGOp *find_op(TCGOp *op, TCGOpcode opc)
static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op)
{
- *begin_op = QTAILQ_NEXT(*begin_op, link);
- tcg_debug_assert(*begin_op);
- op = tcg_op_insert_after(tcg_ctx, op, (*begin_op)->opc);
- memcpy(op->args, (*begin_op)->args, sizeof(op->args));
+ TCGOp *old_op = QTAILQ_NEXT(*begin_op, link);
+ unsigned nargs = old_op->nargs;
+
+ *begin_op = old_op;
+ op = tcg_op_insert_after(tcg_ctx, op, old_op->opc, nargs);
+ memcpy(op->args, old_op->args, sizeof(op->args[0]) * nargs);
+
return op;
}
return op;
}
-static TCGOp *copy_extu_i32_i64(TCGOp **begin_op, TCGOp *op)
-{
- if (TCG_TARGET_REG_BITS == 32) {
- /* mov_i32 */
- op = copy_op(begin_op, op, INDEX_op_mov_i32);
- /* mov_i32 w/ $0 */
- op = copy_op(begin_op, op, INDEX_op_mov_i32);
- } else {
- /* extu_i32_i64 */
- op = copy_op(begin_op, op, INDEX_op_extu_i32_i64);
- }
- return op;
-}
-
-static TCGOp *copy_mov_i64(TCGOp **begin_op, TCGOp *op)
-{
- if (TCG_TARGET_REG_BITS == 32) {
- /* 2x mov_i32 */
- op = copy_op(begin_op, op, INDEX_op_mov_i32);
- op = copy_op(begin_op, op, INDEX_op_mov_i32);
- } else {
- /* mov_i64 */
- op = copy_op(begin_op, op, INDEX_op_mov_i64);
- }
- return op;
-}
-
static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr)
{
if (UINTPTR_MAX == UINT32_MAX) {
return op;
}
-static TCGOp *copy_extu_tl_i64(TCGOp **begin_op, TCGOp *op)
-{
- if (TARGET_LONG_BITS == 32) {
- /* extu_i32_i64 */
- op = copy_extu_i32_i64(begin_op, op);
- } else {
- /* mov_i64 */
- op = copy_mov_i64(begin_op, op);
- }
- return op;
-}
-
static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op)
{
if (TCG_TARGET_REG_BITS == 32) {
static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func,
void *func, int *cb_idx)
{
+ TCGOp *old_op;
+ int func_idx;
+
/* copy all ops until the call */
do {
op = copy_op_nocheck(begin_op, op);
} while (op->opc != INDEX_op_call);
/* fill in the op call */
- op->param1 = (*begin_op)->param1;
- op->param2 = (*begin_op)->param2;
+ old_op = *begin_op;
+ TCGOP_CALLI(op) = TCGOP_CALLI(old_op);
+ TCGOP_CALLO(op) = TCGOP_CALLO(old_op);
tcg_debug_assert(op->life == 0);
- if (*cb_idx == -1) {
- int i;
- /*
- * Instead of working out the position of the callback in args[], just
- * look for @empty_func, since it should be a unique pointer.
- */
- for (i = 0; i < MAX_OPC_PARAM_ARGS; i++) {
- if ((uintptr_t)(*begin_op)->args[i] == (uintptr_t)empty_func) {
- *cb_idx = i;
- break;
- }
- }
- tcg_debug_assert(i < MAX_OPC_PARAM_ARGS);
- }
- op->args[*cb_idx] = (uintptr_t)func;
- op->args[*cb_idx + 1] = (*begin_op)->args[*cb_idx + 1];
+ func_idx = TCGOP_CALLO(op) + TCGOP_CALLI(op);
+ *cb_idx = func_idx;
+ op->args[func_idx] = (uintptr_t)func;
return op;
}
op = copy_const_ptr(&begin_op, op, cb->userp);
/* copy the ld_i32, but note that we only have to copy it once */
- begin_op = QTAILQ_NEXT(begin_op, link);
- tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
if (*cb_idx == -1) {
- op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
- memcpy(op->args, begin_op->args, sizeof(op->args));
+ op = copy_op(&begin_op, op, INDEX_op_ld_i32);
+ } else {
+ begin_op = QTAILQ_NEXT(begin_op, link);
+ tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
}
/* call */
op = copy_const_ptr(&begin_op, op, cb->userp);
/* copy the ld_i32, but note that we only have to copy it once */
- begin_op = QTAILQ_NEXT(begin_op, link);
- tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
if (*cb_idx == -1) {
- op = tcg_op_insert_after(tcg_ctx, op, INDEX_op_ld_i32);
- memcpy(op->args, begin_op->args, sizeof(op->args));
+ op = copy_op(&begin_op, op, INDEX_op_ld_i32);
+ } else {
+ begin_op = QTAILQ_NEXT(begin_op, link);
+ tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32);
}
- /* extu_tl_i64 */
- op = copy_extu_tl_i64(&begin_op, op);
-
if (type == PLUGIN_GEN_CB_MEM) {
/* call */
op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb),
* is possible that the code we generate after the instruction is
* dead, we also add checks before generating tb_exit etc.
*/
-static void inject_mem_enable_helper(struct qemu_plugin_insn *plugin_insn,
+static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb,
+ struct qemu_plugin_insn *plugin_insn,
TCGOp *begin_op)
{
GArray *cbs[2];
rm_ops(begin_op);
return;
}
+ ptb->mem_helper = true;
arr = g_array_sized_new(false, false,
sizeof(struct qemu_plugin_dyn_cb), n_cbs);
/* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
void plugin_gen_disable_mem_helpers(void)
{
- TCGv_ptr ptr;
-
- if (likely(tcg_ctx->plugin_insn == NULL ||
- !tcg_ctx->plugin_insn->mem_helper)) {
+ /*
+ * We could emit the clearing unconditionally and be done. However, this can
+ * be wasteful if for instance plugins don't track memory accesses, or if
+ * most TBs don't use helpers. Instead, emit the clearing iff the TB calls
+ * helpers that might access guest memory.
+ *
+ * Note: we do not reset plugin_tb->mem_helper here; a TB might have several
+ * exit points, and we want to emit the clearing from all of them.
+ */
+ if (!tcg_ctx->plugin_tb->mem_helper) {
return;
}
- ptr = tcg_const_ptr(NULL);
- tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) -
- offsetof(ArchCPU, env));
- tcg_temp_free_ptr(ptr);
- tcg_ctx->plugin_insn->mem_helper = false;
+ tcg_gen_st_ptr(tcg_constant_ptr(NULL), tcg_env,
+ offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env));
}
static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb,
inject_inline_cb(cbs, begin_op, op_rw);
}
-static void plugin_gen_enable_mem_helper(const struct qemu_plugin_tb *ptb,
+static void plugin_gen_enable_mem_helper(struct qemu_plugin_tb *ptb,
TCGOp *begin_op, int insn_idx)
{
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
- inject_mem_enable_helper(insn, begin_op);
+ inject_mem_enable_helper(ptb, insn, begin_op);
}
-static void plugin_gen_disable_mem_helper(const struct qemu_plugin_tb *ptb,
+static void plugin_gen_disable_mem_helper(struct qemu_plugin_tb *ptb,
TCGOp *begin_op, int insn_idx)
{
struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx);
#endif
}
-static void plugin_gen_inject(const struct qemu_plugin_tb *plugin_tb)
+static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)
{
TCGOp *op;
int insn_idx = -1;
pr_ops();
}
-bool plugin_gen_tb_start(CPUState *cpu, const TranslationBlock *tb, bool mem_only)
+bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db,
+ bool mem_only)
{
- struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
bool ret = false;
if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) {
+ struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
+ int i;
+
+ /* reset callbacks */
+ for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) {
+ if (ptb->cbs[i]) {
+ g_array_set_size(ptb->cbs[i], 0);
+ }
+ }
+ ptb->n = 0;
+
ret = true;
- ptb->vaddr = tb->pc;
+ ptb->vaddr = db->pc_first;
ptb->vaddr2 = -1;
- get_page_addr_code_hostp(cpu->env_ptr, tb->pc, &ptb->haddr1);
+ ptb->haddr1 = db->host_addr[0];
ptb->haddr2 = NULL;
ptb->mem_only = mem_only;
+ ptb->mem_helper = false;
plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB);
}
+
+ tcg_ctx->plugin_insn = NULL;
+
return ret;
}
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
struct qemu_plugin_insn *pinsn;
- pinsn = qemu_plugin_tb_insn_get(ptb);
+ pinsn = qemu_plugin_tb_insn_get(ptb, db->pc_next);
tcg_ctx->plugin_insn = pinsn;
- pinsn->vaddr = db->pc_next;
plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
/*
* Note that we skip this when haddr1 == NULL, e.g. when we're
* fetching instructions from a region not backed by RAM.
*/
- if (likely(ptb->haddr1 != NULL && ptb->vaddr2 == -1) &&
- unlikely((db->pc_next & TARGET_PAGE_MASK) !=
- (db->pc_first & TARGET_PAGE_MASK))) {
- get_page_addr_code_hostp(cpu->env_ptr, db->pc_next,
- &ptb->haddr2);
- ptb->vaddr2 = db->pc_next;
- }
- if (likely(ptb->vaddr2 == -1)) {
+ if (ptb->haddr1 == NULL) {
+ pinsn->haddr = NULL;
+ } else if (is_same_page(db, db->pc_next)) {
pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr;
} else {
+ if (ptb->vaddr2 == -1) {
+ ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first);
+ get_page_addr_code_hostp(cpu_env(cpu), ptb->vaddr2, &ptb->haddr2);
+ }
pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2;
}
}
plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN);
}
+/*
+ * There are cases where we never get to finalise a translation - for
+ * example a page fault during translation. As a result we shouldn't
+ * do any clean-up here and make sure things are reset in
+ * plugin_gen_tb_start.
+ */
void plugin_gen_tb_end(CPUState *cpu)
{
struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
- int i;
/* collect instrumentation requests */
qemu_plugin_tb_trans_cb(cpu, ptb);
/* inject the instrumentation at the appropriate places */
plugin_gen_inject(ptb);
-
- /* clean up */
- for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) {
- if (ptb->cbs[i]) {
- g_array_set_size(ptb->cbs[i], 0);
- }
- }
- ptb->n = 0;
- tcg_ctx->plugin_insn = NULL;
}