xref: /qemu/accel/tcg/plugin-gen.c (revision 5e379b08bceb04631401fda674c4c9f7ab1e3f94)
138b47b19SEmilio G. Cota /*
238b47b19SEmilio G. Cota  * plugin-gen.c - TCG-related bits of plugin infrastructure
338b47b19SEmilio G. Cota  *
438b47b19SEmilio G. Cota  * Copyright (C) 2018, Emilio G. Cota <cota@braap.org>
538b47b19SEmilio G. Cota  * License: GNU GPL, version 2 or later.
638b47b19SEmilio G. Cota  *   See the COPYING file in the top-level directory.
738b47b19SEmilio G. Cota  *
838b47b19SEmilio G. Cota  * We support instrumentation at an instruction granularity. That is,
938b47b19SEmilio G. Cota  * if a plugin wants to instrument the memory accesses performed by a
1038b47b19SEmilio G. Cota  * particular instruction, it can just do that instead of instrumenting
1138b47b19SEmilio G. Cota  * all memory accesses. Thus, in order to do this we first have to
1238b47b19SEmilio G. Cota  * translate a TB, so that plugins can decide what/where to instrument.
1338b47b19SEmilio G. Cota  *
1438b47b19SEmilio G. Cota  * Injecting the desired instrumentation could be done with a second
1538b47b19SEmilio G. Cota  * translation pass that combined the instrumentation requests, but that
1638b47b19SEmilio G. Cota  * would be ugly and inefficient since we would decode the guest code twice.
1738b47b19SEmilio G. Cota  * Instead, during TB translation we add "empty" instrumentation calls for all
1838b47b19SEmilio G. Cota  * possible instrumentation events, and then once we collect the instrumentation
1938b47b19SEmilio G. Cota  * requests from plugins, we either "fill in" those empty events or remove them
2038b47b19SEmilio G. Cota  * if they have no requests.
2138b47b19SEmilio G. Cota  *
2238b47b19SEmilio G. Cota  * When "filling in" an event we first copy the empty callback's TCG ops. This
2338b47b19SEmilio G. Cota  * might seem unnecessary, but it is done to support an arbitrary number
2438b47b19SEmilio G. Cota  * of callbacks per event. Take for example a regular instruction callback.
2538b47b19SEmilio G. Cota  * We first generate a callback to an empty helper function. Then, if two
2638b47b19SEmilio G. Cota  * plugins register one callback each for this instruction, we make two copies
2738b47b19SEmilio G. Cota  * of the TCG ops generated for the empty callback, substituting the function
2838b47b19SEmilio G. Cota  * pointer that points to the empty helper function with the plugins' desired
2938b47b19SEmilio G. Cota  * callback functions. After that we remove the empty callback's ops.
3038b47b19SEmilio G. Cota  *
3138b47b19SEmilio G. Cota  * Note that the location in TCGOp.args[] of the pointer to a helper function
3238b47b19SEmilio G. Cota  * varies across different guest and host architectures. Instead of duplicating
3338b47b19SEmilio G. Cota  * the logic that figures this out, we rely on the fact that the empty
3438b47b19SEmilio G. Cota  * callbacks point to empty functions that are unique pointers in the program.
3538b47b19SEmilio G. Cota  * Thus, to find the right location we just have to look for a match in
3638b47b19SEmilio G. Cota  * TCGOp.args[]. This is the main reason why we first copy an empty callback's
3738b47b19SEmilio G. Cota  * TCG ops and then fill them in; regardless of whether we have one or many
3838b47b19SEmilio G. Cota  * callbacks for that event, the logic to add all of them is the same.
3938b47b19SEmilio G. Cota  *
4038b47b19SEmilio G. Cota  * When generating more than one callback per event, we make a small
4138b47b19SEmilio G. Cota  * optimization to avoid generating redundant operations. For instance, for the
4238b47b19SEmilio G. Cota  * second and all subsequent callbacks of an event, we do not need to reload the
4338b47b19SEmilio G. Cota  * CPU's index into a TCG temp, since the first callback did it already.
4438b47b19SEmilio G. Cota  */
4538b47b19SEmilio G. Cota #include "qemu/osdep.h"
46c0061471SAlex Bennée #include "qemu/plugin.h"
47b384c734SRichard Henderson #include "qemu/log.h"
48cac9b0fdSRichard Henderson #include "cpu.h"
4938b47b19SEmilio G. Cota #include "tcg/tcg.h"
5047f7313dSRichard Henderson #include "tcg/tcg-temp-internal.h"
5138b47b19SEmilio G. Cota #include "tcg/tcg-op.h"
5238b47b19SEmilio G. Cota #include "exec/exec-all.h"
5338b47b19SEmilio G. Cota #include "exec/plugin-gen.h"
5438b47b19SEmilio G. Cota #include "exec/translator.h"
5538b47b19SEmilio G. Cota 
5638b47b19SEmilio G. Cota enum plugin_gen_from {
5738b47b19SEmilio G. Cota     PLUGIN_GEN_FROM_TB,
5838b47b19SEmilio G. Cota     PLUGIN_GEN_FROM_INSN,
5938b47b19SEmilio G. Cota     PLUGIN_GEN_AFTER_INSN,
6074bb8accSRichard Henderson     PLUGIN_GEN_AFTER_TB,
6138b47b19SEmilio G. Cota };
6238b47b19SEmilio G. Cota 
639a3ee366SRichard Henderson static void plugin_gen_empty_callback(enum plugin_gen_from from)
6438b47b19SEmilio G. Cota {
6538b47b19SEmilio G. Cota     switch (from) {
6638b47b19SEmilio G. Cota     case PLUGIN_GEN_AFTER_INSN:
6721a3f62fSRichard Henderson     case PLUGIN_GEN_FROM_TB:
6838b47b19SEmilio G. Cota     case PLUGIN_GEN_FROM_INSN:
69ac977170SRichard Henderson         tcg_gen_plugin_cb(from);
7038b47b19SEmilio G. Cota         break;
7138b47b19SEmilio G. Cota     default:
7238b47b19SEmilio G. Cota         g_assert_not_reached();
7338b47b19SEmilio G. Cota     }
7438b47b19SEmilio G. Cota }
7538b47b19SEmilio G. Cota 
7638b47b19SEmilio G. Cota /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */
7738b47b19SEmilio G. Cota void plugin_gen_disable_mem_helpers(void)
7838b47b19SEmilio G. Cota {
7974bb8accSRichard Henderson     if (tcg_ctx->plugin_insn) {
8074bb8accSRichard Henderson         tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_TB);
8138b47b19SEmilio G. Cota     }
8238b47b19SEmilio G. Cota }
8338b47b19SEmilio G. Cota 
84ac977170SRichard Henderson static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb,
85ac977170SRichard Henderson                                   struct qemu_plugin_insn *insn)
8638b47b19SEmilio G. Cota {
87ac977170SRichard Henderson     GArray *arr;
88db409c01SRichard Henderson     size_t len;
89ac977170SRichard Henderson 
90ac977170SRichard Henderson     /*
91ac977170SRichard Henderson      * Tracking memory accesses performed from helpers requires extra work.
92ac977170SRichard Henderson      * If an instruction is emulated with helpers, we do two things:
93ac977170SRichard Henderson      * (1) copy the CB descriptors, and keep track of it so that they can be
94ac977170SRichard Henderson      * freed later on, and (2) point CPUState.plugin_mem_cbs to the
95ac977170SRichard Henderson      * descriptors, so that we can read them at run-time
96ac977170SRichard Henderson      * (i.e. when the helper executes).
97ac977170SRichard Henderson      * This run-time access is performed from qemu_plugin_vcpu_mem_cb.
98ac977170SRichard Henderson      *
99ac977170SRichard Henderson      * Note that plugin_gen_disable_mem_helpers undoes (2). Since it
100ac977170SRichard Henderson      * is possible that the code we generate after the instruction is
101ac977170SRichard Henderson      * dead, we also add checks before generating tb_exit etc.
102ac977170SRichard Henderson      */
103ac977170SRichard Henderson     if (!insn->calls_helpers) {
104ac977170SRichard Henderson         return;
105ac977170SRichard Henderson     }
106ac977170SRichard Henderson 
107db409c01SRichard Henderson     if (!insn->mem_cbs || !insn->mem_cbs->len) {
108ac977170SRichard Henderson         insn->mem_helper = false;
109ac977170SRichard Henderson         return;
110ac977170SRichard Henderson     }
111ac977170SRichard Henderson     insn->mem_helper = true;
112ac977170SRichard Henderson     ptb->mem_helper = true;
113ac977170SRichard Henderson 
114db409c01SRichard Henderson     /*
115db409c01SRichard Henderson      * TODO: It seems like we should be able to use ref/unref
116db409c01SRichard Henderson      * to avoid needing to actually copy this array.
117db409c01SRichard Henderson      * Alternately, perhaps we could allocate new memory adjacent
118db409c01SRichard Henderson      * to the TranslationBlock itself, so that we do not have to
119db409c01SRichard Henderson      * actively manage the lifetime after this.
120db409c01SRichard Henderson      */
121db409c01SRichard Henderson     len = insn->mem_cbs->len;
122ac977170SRichard Henderson     arr = g_array_sized_new(false, false,
123db409c01SRichard Henderson                             sizeof(struct qemu_plugin_dyn_cb), len);
124db409c01SRichard Henderson     memcpy(arr->data, insn->mem_cbs->data,
125db409c01SRichard Henderson            len * sizeof(struct qemu_plugin_dyn_cb));
126ac977170SRichard Henderson     qemu_plugin_add_dyn_cb_arr(arr);
127ac977170SRichard Henderson 
128ac977170SRichard Henderson     tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env,
129ac977170SRichard Henderson                    offsetof(CPUState, plugin_mem_cbs) -
130ac977170SRichard Henderson                    offsetof(ArchCPU, env));
13138b47b19SEmilio G. Cota }
13238b47b19SEmilio G. Cota 
13374bb8accSRichard Henderson static void gen_disable_mem_helper(void)
13438b47b19SEmilio G. Cota {
135a0948bb7SRichard Henderson     tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env,
136a0948bb7SRichard Henderson                    offsetof(CPUState, plugin_mem_cbs) -
137a0948bb7SRichard Henderson                    offsetof(ArchCPU, env));
138a0948bb7SRichard Henderson }
13938b47b19SEmilio G. Cota 
14021a3f62fSRichard Henderson static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb)
14121a3f62fSRichard Henderson {
14221a3f62fSRichard Henderson     TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
14321a3f62fSRichard Henderson 
14421a3f62fSRichard Henderson     tcg_gen_ld_i32(cpu_index, tcg_env,
14521a3f62fSRichard Henderson                    -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
14621a3f62fSRichard Henderson     tcg_gen_call2(cb->regular.f.vcpu_udata, cb->regular.info, NULL,
14721a3f62fSRichard Henderson                   tcgv_i32_temp(cpu_index),
14821a3f62fSRichard Henderson                   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
14921a3f62fSRichard Henderson     tcg_temp_free_i32(cpu_index);
15021a3f62fSRichard Henderson }
15121a3f62fSRichard Henderson 
15221a3f62fSRichard Henderson static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb)
15321a3f62fSRichard Henderson {
15421a3f62fSRichard Henderson     GArray *arr = cb->inline_insn.entry.score->data;
15521a3f62fSRichard Henderson     size_t offset = cb->inline_insn.entry.offset;
15621a3f62fSRichard Henderson     TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
15721a3f62fSRichard Henderson     TCGv_i64 val = tcg_temp_ebb_new_i64();
15821a3f62fSRichard Henderson     TCGv_ptr ptr = tcg_temp_ebb_new_ptr();
15921a3f62fSRichard Henderson 
16021a3f62fSRichard Henderson     tcg_gen_ld_i32(cpu_index, tcg_env,
16121a3f62fSRichard Henderson                    -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
16221a3f62fSRichard Henderson     tcg_gen_muli_i32(cpu_index, cpu_index, g_array_get_element_size(arr));
16321a3f62fSRichard Henderson     tcg_gen_ext_i32_ptr(ptr, cpu_index);
16421a3f62fSRichard Henderson     tcg_temp_free_i32(cpu_index);
16521a3f62fSRichard Henderson 
16621a3f62fSRichard Henderson     tcg_gen_addi_ptr(ptr, ptr, (intptr_t)arr->data);
16721a3f62fSRichard Henderson     tcg_gen_ld_i64(val, ptr, offset);
16821a3f62fSRichard Henderson     tcg_gen_addi_i64(val, val, cb->inline_insn.imm);
16921a3f62fSRichard Henderson     tcg_gen_st_i64(val, ptr, offset);
17021a3f62fSRichard Henderson 
17121a3f62fSRichard Henderson     tcg_temp_free_i64(val);
17221a3f62fSRichard Henderson     tcg_temp_free_ptr(ptr);
17321a3f62fSRichard Henderson }
17421a3f62fSRichard Henderson 
1758a2927f2SRichard Henderson static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb,
1768a2927f2SRichard Henderson                        qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
1778a2927f2SRichard Henderson {
1788a2927f2SRichard Henderson     TCGv_i32 cpu_index = tcg_temp_ebb_new_i32();
1798a2927f2SRichard Henderson 
1808a2927f2SRichard Henderson     tcg_gen_ld_i32(cpu_index, tcg_env,
1818a2927f2SRichard Henderson                    -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index));
1828a2927f2SRichard Henderson     tcg_gen_call4(cb->regular.f.vcpu_mem, cb->regular.info, NULL,
1838a2927f2SRichard Henderson                   tcgv_i32_temp(cpu_index),
1848a2927f2SRichard Henderson                   tcgv_i32_temp(tcg_constant_i32(meminfo)),
1858a2927f2SRichard Henderson                   tcgv_i64_temp(addr),
1868a2927f2SRichard Henderson                   tcgv_ptr_temp(tcg_constant_ptr(cb->userp)));
1878a2927f2SRichard Henderson     tcg_temp_free_i32(cpu_index);
1888a2927f2SRichard Henderson }
1898a2927f2SRichard Henderson 
1907e53aa21SRichard Henderson static void inject_cb(struct qemu_plugin_dyn_cb *cb)
1917e53aa21SRichard Henderson 
1927e53aa21SRichard Henderson {
1937e53aa21SRichard Henderson     switch (cb->type) {
1947e53aa21SRichard Henderson     case PLUGIN_CB_REGULAR:
1957e53aa21SRichard Henderson         gen_udata_cb(cb);
1967e53aa21SRichard Henderson         break;
1977e53aa21SRichard Henderson     case PLUGIN_CB_INLINE:
1987e53aa21SRichard Henderson         gen_inline_cb(cb);
1997e53aa21SRichard Henderson         break;
2007e53aa21SRichard Henderson     default:
2017e53aa21SRichard Henderson         g_assert_not_reached();
2027e53aa21SRichard Henderson     }
2037e53aa21SRichard Henderson }
2047e53aa21SRichard Henderson 
2057e53aa21SRichard Henderson static void inject_mem_cb(struct qemu_plugin_dyn_cb *cb,
2067e53aa21SRichard Henderson                           enum qemu_plugin_mem_rw rw,
2077e53aa21SRichard Henderson                           qemu_plugin_meminfo_t meminfo, TCGv_i64 addr)
2087e53aa21SRichard Henderson {
2097e53aa21SRichard Henderson     if (cb->rw & rw) {
2107e53aa21SRichard Henderson         switch (cb->type) {
2117e53aa21SRichard Henderson         case PLUGIN_CB_MEM_REGULAR:
2127e53aa21SRichard Henderson             gen_mem_cb(cb, meminfo, addr);
2137e53aa21SRichard Henderson             break;
2147e53aa21SRichard Henderson         default:
2157e53aa21SRichard Henderson             inject_cb(cb);
2167e53aa21SRichard Henderson             break;
2177e53aa21SRichard Henderson         }
2187e53aa21SRichard Henderson     }
2197e53aa21SRichard Henderson }
2207e53aa21SRichard Henderson 
2213fd62e73SEmilio Cota static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb)
22238b47b19SEmilio G. Cota {
223a0948bb7SRichard Henderson     TCGOp *op, *next;
224453d50ceSAlex Bennée     int insn_idx = -1;
22538b47b19SEmilio G. Cota 
226b384c734SRichard Henderson     if (unlikely(qemu_loglevel_mask(LOG_TB_OP_PLUGIN)
227b384c734SRichard Henderson                  && qemu_log_in_addr_range(plugin_tb->vaddr))) {
228b384c734SRichard Henderson         FILE *logfile = qemu_log_trylock();
229b384c734SRichard Henderson         if (logfile) {
230b384c734SRichard Henderson             fprintf(logfile, "OP before plugin injection:\n");
231b384c734SRichard Henderson             tcg_dump_ops(tcg_ctx, logfile, false);
232b384c734SRichard Henderson             fprintf(logfile, "\n");
233b384c734SRichard Henderson             qemu_log_unlock(logfile);
234b384c734SRichard Henderson         }
235b384c734SRichard Henderson     }
236453d50ceSAlex Bennée 
237a0948bb7SRichard Henderson     /*
238a0948bb7SRichard Henderson      * While injecting code, we cannot afford to reuse any ebb temps
239a0948bb7SRichard Henderson      * that might be live within the existing opcode stream.
240a0948bb7SRichard Henderson      * The simplest solution is to release them all and create new.
241a0948bb7SRichard Henderson      */
242a0948bb7SRichard Henderson     memset(tcg_ctx->free_temps, 0, sizeof(tcg_ctx->free_temps));
243a0948bb7SRichard Henderson 
244a0948bb7SRichard Henderson     QTAILQ_FOREACH_SAFE(op, &tcg_ctx->ops, link, next) {
245453d50ceSAlex Bennée         switch (op->opc) {
246453d50ceSAlex Bennée         case INDEX_op_insn_start:
247453d50ceSAlex Bennée             insn_idx++;
248453d50ceSAlex Bennée             break;
249a0948bb7SRichard Henderson 
250a0948bb7SRichard Henderson         case INDEX_op_plugin_cb:
251a0948bb7SRichard Henderson         {
252a0948bb7SRichard Henderson             enum plugin_gen_from from = op->args[0];
253a0948bb7SRichard Henderson             struct qemu_plugin_insn *insn = NULL;
25421a3f62fSRichard Henderson             const GArray *cbs;
25521a3f62fSRichard Henderson             int i, n;
256a0948bb7SRichard Henderson 
257a0948bb7SRichard Henderson             if (insn_idx >= 0) {
258a0948bb7SRichard Henderson                 insn = g_ptr_array_index(plugin_tb->insns, insn_idx);
259a0948bb7SRichard Henderson             }
260a0948bb7SRichard Henderson 
261a0948bb7SRichard Henderson             tcg_ctx->emit_before_op = op;
262a0948bb7SRichard Henderson 
263a0948bb7SRichard Henderson             switch (from) {
26474bb8accSRichard Henderson             case PLUGIN_GEN_AFTER_TB:
26574bb8accSRichard Henderson                 if (plugin_tb->mem_helper) {
26674bb8accSRichard Henderson                     gen_disable_mem_helper();
26774bb8accSRichard Henderson                 }
26874bb8accSRichard Henderson                 break;
26974bb8accSRichard Henderson 
270a0948bb7SRichard Henderson             case PLUGIN_GEN_AFTER_INSN:
271a0948bb7SRichard Henderson                 assert(insn != NULL);
27274bb8accSRichard Henderson                 if (insn->mem_helper) {
27374bb8accSRichard Henderson                     gen_disable_mem_helper();
27474bb8accSRichard Henderson                 }
275a0948bb7SRichard Henderson                 break;
27621a3f62fSRichard Henderson 
27721a3f62fSRichard Henderson             case PLUGIN_GEN_FROM_TB:
27821a3f62fSRichard Henderson                 assert(insn == NULL);
27921a3f62fSRichard Henderson 
280db409c01SRichard Henderson                 cbs = plugin_tb->cbs;
28121a3f62fSRichard Henderson                 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
2827e53aa21SRichard Henderson                     inject_cb(
2837e53aa21SRichard Henderson                         &g_array_index(cbs, struct qemu_plugin_dyn_cb, i));
28421a3f62fSRichard Henderson                 }
28521a3f62fSRichard Henderson                 break;
28621a3f62fSRichard Henderson 
287ac977170SRichard Henderson             case PLUGIN_GEN_FROM_INSN:
288ac977170SRichard Henderson                 assert(insn != NULL);
289ac977170SRichard Henderson 
290ac977170SRichard Henderson                 gen_enable_mem_helper(plugin_tb, insn);
291ac977170SRichard Henderson 
292db409c01SRichard Henderson                 cbs = insn->insn_cbs;
293ac977170SRichard Henderson                 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
2947e53aa21SRichard Henderson                     inject_cb(
2957e53aa21SRichard Henderson                         &g_array_index(cbs, struct qemu_plugin_dyn_cb, i));
296ac977170SRichard Henderson                 }
297ac977170SRichard Henderson                 break;
298ac977170SRichard Henderson 
299a0948bb7SRichard Henderson             default:
300a0948bb7SRichard Henderson                 g_assert_not_reached();
301a0948bb7SRichard Henderson             }
302a0948bb7SRichard Henderson 
303a0948bb7SRichard Henderson             tcg_ctx->emit_before_op = NULL;
304a0948bb7SRichard Henderson             tcg_op_remove(tcg_ctx, op);
305a0948bb7SRichard Henderson             break;
306a0948bb7SRichard Henderson         }
307a0948bb7SRichard Henderson 
3088a2927f2SRichard Henderson         case INDEX_op_plugin_mem_cb:
309453d50ceSAlex Bennée         {
3108a2927f2SRichard Henderson             TCGv_i64 addr = temp_tcgv_i64(arg_temp(op->args[0]));
3118a2927f2SRichard Henderson             qemu_plugin_meminfo_t meminfo = op->args[1];
3127e53aa21SRichard Henderson             enum qemu_plugin_mem_rw rw =
3137e53aa21SRichard Henderson                 (qemu_plugin_mem_is_store(meminfo)
3147e53aa21SRichard Henderson                  ? QEMU_PLUGIN_MEM_W : QEMU_PLUGIN_MEM_R);
3158a2927f2SRichard Henderson             struct qemu_plugin_insn *insn;
3168a2927f2SRichard Henderson             const GArray *cbs;
3177e53aa21SRichard Henderson             int i, n;
31838b47b19SEmilio G. Cota 
3198a2927f2SRichard Henderson             assert(insn_idx >= 0);
3208a2927f2SRichard Henderson             insn = g_ptr_array_index(plugin_tb->insns, insn_idx);
321453d50ceSAlex Bennée 
3228a2927f2SRichard Henderson             tcg_ctx->emit_before_op = op;
3238a2927f2SRichard Henderson 
324db409c01SRichard Henderson             cbs = insn->mem_cbs;
3258a2927f2SRichard Henderson             for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) {
3267e53aa21SRichard Henderson                 inject_mem_cb(&g_array_index(cbs, struct qemu_plugin_dyn_cb, i),
3277e53aa21SRichard Henderson                               rw, meminfo, addr);
3288a2927f2SRichard Henderson             }
3298a2927f2SRichard Henderson 
3308a2927f2SRichard Henderson             tcg_ctx->emit_before_op = NULL;
3318a2927f2SRichard Henderson             tcg_op_remove(tcg_ctx, op);
332453d50ceSAlex Bennée             break;
333453d50ceSAlex Bennée         }
3348a2927f2SRichard Henderson 
335453d50ceSAlex Bennée         default:
336453d50ceSAlex Bennée             /* plugins don't care about any other ops */
337453d50ceSAlex Bennée             break;
338453d50ceSAlex Bennée         }
33938b47b19SEmilio G. Cota     }
34038b47b19SEmilio G. Cota }
34138b47b19SEmilio G. Cota 
342b21af662SRichard Henderson bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db,
343b21af662SRichard Henderson                          bool mem_only)
34438b47b19SEmilio G. Cota {
34538b47b19SEmilio G. Cota     bool ret = false;
34638b47b19SEmilio G. Cota 
347c0061471SAlex Bennée     if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_state->event_mask)) {
3486f15c076SAlex Bennée         struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
3496f15c076SAlex Bennée 
3506f15c076SAlex Bennée         /* reset callbacks */
351db409c01SRichard Henderson         if (ptb->cbs) {
352db409c01SRichard Henderson             g_array_set_size(ptb->cbs, 0);
3536f15c076SAlex Bennée         }
3546f15c076SAlex Bennée         ptb->n = 0;
3556f15c076SAlex Bennée 
35638b47b19SEmilio G. Cota         ret = true;
35738b47b19SEmilio G. Cota 
358b21af662SRichard Henderson         ptb->vaddr = db->pc_first;
35938b47b19SEmilio G. Cota         ptb->vaddr2 = -1;
360b21af662SRichard Henderson         ptb->haddr1 = db->host_addr[0];
36138b47b19SEmilio G. Cota         ptb->haddr2 = NULL;
362cfd405eaSAlex Bennée         ptb->mem_only = mem_only;
3633fd62e73SEmilio Cota         ptb->mem_helper = false;
36438b47b19SEmilio G. Cota 
36538b47b19SEmilio G. Cota         plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB);
36638b47b19SEmilio G. Cota     }
3676f15c076SAlex Bennée 
3686f15c076SAlex Bennée     tcg_ctx->plugin_insn = NULL;
3696f15c076SAlex Bennée 
37038b47b19SEmilio G. Cota     return ret;
37138b47b19SEmilio G. Cota }
37238b47b19SEmilio G. Cota 
37338b47b19SEmilio G. Cota void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db)
37438b47b19SEmilio G. Cota {
37538b47b19SEmilio G. Cota     struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
376*5e379b08SRichard Henderson     struct qemu_plugin_insn *insn;
377*5e379b08SRichard Henderson     size_t n = db->num_insns;
378*5e379b08SRichard Henderson     vaddr pc;
37938b47b19SEmilio G. Cota 
380*5e379b08SRichard Henderson     assert(n >= 1);
381*5e379b08SRichard Henderson     ptb->n = n;
382*5e379b08SRichard Henderson     if (n <= ptb->insns->len) {
383*5e379b08SRichard Henderson         insn = g_ptr_array_index(ptb->insns, n - 1);
384*5e379b08SRichard Henderson         g_byte_array_set_size(insn->data, 0);
385*5e379b08SRichard Henderson     } else {
386*5e379b08SRichard Henderson         assert(n - 1 == ptb->insns->len);
387*5e379b08SRichard Henderson         insn = g_new0(struct qemu_plugin_insn, 1);
388*5e379b08SRichard Henderson         insn->data = g_byte_array_sized_new(4);
389*5e379b08SRichard Henderson         g_ptr_array_add(ptb->insns, insn);
390*5e379b08SRichard Henderson     }
391*5e379b08SRichard Henderson 
392*5e379b08SRichard Henderson     tcg_ctx->plugin_insn = insn;
393*5e379b08SRichard Henderson     insn->calls_helpers = false;
394*5e379b08SRichard Henderson     insn->mem_helper = false;
395*5e379b08SRichard Henderson     if (insn->insn_cbs) {
396*5e379b08SRichard Henderson         g_array_set_size(insn->insn_cbs, 0);
397*5e379b08SRichard Henderson     }
398*5e379b08SRichard Henderson     if (insn->mem_cbs) {
399*5e379b08SRichard Henderson         g_array_set_size(insn->mem_cbs, 0);
400*5e379b08SRichard Henderson     }
401*5e379b08SRichard Henderson 
402*5e379b08SRichard Henderson     pc = db->pc_next;
403*5e379b08SRichard Henderson     insn->vaddr = pc;
40438b47b19SEmilio G. Cota 
40538b47b19SEmilio G. Cota     /*
40638b47b19SEmilio G. Cota      * Detect page crossing to get the new host address.
40738b47b19SEmilio G. Cota      * Note that we skip this when haddr1 == NULL, e.g. when we're
40838b47b19SEmilio G. Cota      * fetching instructions from a region not backed by RAM.
40938b47b19SEmilio G. Cota      */
410b21af662SRichard Henderson     if (ptb->haddr1 == NULL) {
411*5e379b08SRichard Henderson         insn->haddr = NULL;
412b21af662SRichard Henderson     } else if (is_same_page(db, db->pc_next)) {
413*5e379b08SRichard Henderson         insn->haddr = ptb->haddr1 + pc - ptb->vaddr;
41438b47b19SEmilio G. Cota     } else {
415b21af662SRichard Henderson         if (ptb->vaddr2 == -1) {
416b21af662SRichard Henderson             ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first);
417b77af26eSRichard Henderson             get_page_addr_code_hostp(cpu_env(cpu), ptb->vaddr2, &ptb->haddr2);
418b21af662SRichard Henderson         }
419*5e379b08SRichard Henderson         insn->haddr = ptb->haddr2 + pc - ptb->vaddr2;
42038b47b19SEmilio G. Cota     }
421*5e379b08SRichard Henderson 
422*5e379b08SRichard Henderson     plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN);
42338b47b19SEmilio G. Cota }
42438b47b19SEmilio G. Cota 
42538b47b19SEmilio G. Cota void plugin_gen_insn_end(void)
42638b47b19SEmilio G. Cota {
42738b47b19SEmilio G. Cota     plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN);
42838b47b19SEmilio G. Cota }
42938b47b19SEmilio G. Cota 
4306f15c076SAlex Bennée /*
4316f15c076SAlex Bennée  * There are cases where we never get to finalise a translation - for
4326f15c076SAlex Bennée  * example a page fault during translation. As a result we shouldn't
4336f15c076SAlex Bennée  * do any clean-up here and make sure things are reset in
4346f15c076SAlex Bennée  * plugin_gen_tb_start.
4356f15c076SAlex Bennée  */
436a392277dSMatt Borgerson void plugin_gen_tb_end(CPUState *cpu, size_t num_insns)
43738b47b19SEmilio G. Cota {
43838b47b19SEmilio G. Cota     struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb;
43938b47b19SEmilio G. Cota 
440a392277dSMatt Borgerson     /* translator may have removed instructions, update final count */
441a392277dSMatt Borgerson     g_assert(num_insns <= ptb->n);
442a392277dSMatt Borgerson     ptb->n = num_insns;
443a392277dSMatt Borgerson 
44438b47b19SEmilio G. Cota     /* collect instrumentation requests */
44538b47b19SEmilio G. Cota     qemu_plugin_tb_trans_cb(cpu, ptb);
44638b47b19SEmilio G. Cota 
44738b47b19SEmilio G. Cota     /* inject the instrumentation at the appropriate places */
44838b47b19SEmilio G. Cota     plugin_gen_inject(ptb);
44938b47b19SEmilio G. Cota }
450