]>
Commit | Line | Data |
---|---|---|
38b47b19 EC |
1 | /* |
2 | * plugin-gen.c - TCG-related bits of plugin infrastructure | |
3 | * | |
4 | * Copyright (C) 2018, Emilio G. Cota <cota@braap.org> | |
5 | * License: GNU GPL, version 2 or later. | |
6 | * See the COPYING file in the top-level directory. | |
7 | * | |
8 | * We support instrumentation at an instruction granularity. That is, | |
9 | * if a plugin wants to instrument the memory accesses performed by a | |
10 | * particular instruction, it can just do that instead of instrumenting | |
11 | * all memory accesses. Thus, in order to do this we first have to | |
12 | * translate a TB, so that plugins can decide what/where to instrument. | |
13 | * | |
14 | * Injecting the desired instrumentation could be done with a second | |
15 | * translation pass that combined the instrumentation requests, but that | |
16 | * would be ugly and inefficient since we would decode the guest code twice. | |
17 | * Instead, during TB translation we add "empty" instrumentation calls for all | |
18 | * possible instrumentation events, and then once we collect the instrumentation | |
19 | * requests from plugins, we either "fill in" those empty events or remove them | |
20 | * if they have no requests. | |
21 | * | |
22 | * When "filling in" an event we first copy the empty callback's TCG ops. This | |
23 | * might seem unnecessary, but it is done to support an arbitrary number | |
24 | * of callbacks per event. Take for example a regular instruction callback. | |
25 | * We first generate a callback to an empty helper function. Then, if two | |
26 | * plugins register one callback each for this instruction, we make two copies | |
27 | * of the TCG ops generated for the empty callback, substituting the function | |
28 | * pointer that points to the empty helper function with the plugins' desired | |
29 | * callback functions. After that we remove the empty callback's ops. | |
30 | * | |
31 | * Note that the location in TCGOp.args[] of the pointer to a helper function | |
32 | * varies across different guest and host architectures. Instead of duplicating | |
33 | * the logic that figures this out, we rely on the fact that the empty | |
34 | * callbacks point to empty functions that are unique pointers in the program. | |
35 | * Thus, to find the right location we just have to look for a match in | |
36 | * TCGOp.args[]. This is the main reason why we first copy an empty callback's | |
37 | * TCG ops and then fill them in; regardless of whether we have one or many | |
38 | * callbacks for that event, the logic to add all of them is the same. | |
39 | * | |
40 | * When generating more than one callback per event, we make a small | |
41 | * optimization to avoid generating redundant operations. For instance, for the | |
42 | * second and all subsequent callbacks of an event, we do not need to reload the | |
43 | * CPU's index into a TCG temp, since the first callback did it already. | |
44 | */ | |
45 | #include "qemu/osdep.h" | |
cac9b0fd | 46 | #include "cpu.h" |
38b47b19 | 47 | #include "tcg/tcg.h" |
47f7313d | 48 | #include "tcg/tcg-temp-internal.h" |
38b47b19 | 49 | #include "tcg/tcg-op.h" |
38b47b19 EC |
50 | #include "exec/exec-all.h" |
51 | #include "exec/plugin-gen.h" | |
52 | #include "exec/translator.h" | |
c213ee2d | 53 | #include "exec/helper-proto-common.h" |
d53106c9 RH |
54 | |
55 | #define HELPER_H "accel/tcg/plugin-helpers.h" | |
56 | #include "exec/helper-info.c.inc" | |
57 | #undef HELPER_H | |
38b47b19 EC |
58 | |
59 | #ifdef CONFIG_SOFTMMU | |
60 | # define CONFIG_SOFTMMU_GATE 1 | |
61 | #else | |
62 | # define CONFIG_SOFTMMU_GATE 0 | |
63 | #endif | |
64 | ||
65 | /* | |
66 | * plugin_cb_start TCG op args[]: | |
67 | * 0: enum plugin_gen_from | |
68 | * 1: enum plugin_gen_cb | |
69 | * 2: set to 1 for mem callback that is a write, 0 otherwise. | |
70 | */ | |
71 | ||
72 | enum plugin_gen_from { | |
73 | PLUGIN_GEN_FROM_TB, | |
74 | PLUGIN_GEN_FROM_INSN, | |
75 | PLUGIN_GEN_FROM_MEM, | |
76 | PLUGIN_GEN_AFTER_INSN, | |
77 | PLUGIN_GEN_N_FROMS, | |
78 | }; | |
79 | ||
80 | enum plugin_gen_cb { | |
81 | PLUGIN_GEN_CB_UDATA, | |
82 | PLUGIN_GEN_CB_INLINE, | |
83 | PLUGIN_GEN_CB_MEM, | |
84 | PLUGIN_GEN_ENABLE_MEM_HELPER, | |
85 | PLUGIN_GEN_DISABLE_MEM_HELPER, | |
86 | PLUGIN_GEN_N_CBS, | |
87 | }; | |
88 | ||
89 | /* | |
90 | * These helpers are stubs that get dynamically switched out for calls | |
91 | * direct to the plugin if they are subscribed to. | |
92 | */ | |
93 | void HELPER(plugin_vcpu_udata_cb)(uint32_t cpu_index, void *udata) | |
94 | { } | |
95 | ||
96 | void HELPER(plugin_vcpu_mem_cb)(unsigned int vcpu_index, | |
97 | qemu_plugin_meminfo_t info, uint64_t vaddr, | |
98 | void *userdata) | |
99 | { } | |
100 | ||
38b47b19 EC |
101 | static void gen_empty_udata_cb(void) |
102 | { | |
713f263a RH |
103 | TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); |
104 | TCGv_ptr udata = tcg_temp_ebb_new_ptr(); | |
38b47b19 | 105 | |
713f263a | 106 | tcg_gen_movi_ptr(udata, 0); |
38b47b19 EC |
107 | tcg_gen_ld_i32(cpu_index, cpu_env, |
108 | -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); | |
109 | gen_helper_plugin_vcpu_udata_cb(cpu_index, udata); | |
110 | ||
111 | tcg_temp_free_ptr(udata); | |
112 | tcg_temp_free_i32(cpu_index); | |
113 | } | |
114 | ||
115 | /* | |
116 | * For now we only support addi_i64. | |
117 | * When we support more ops, we can generate one empty inline cb for each. | |
118 | */ | |
119 | static void gen_empty_inline_cb(void) | |
120 | { | |
713f263a RH |
121 | TCGv_i64 val = tcg_temp_ebb_new_i64(); |
122 | TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); | |
38b47b19 | 123 | |
713f263a | 124 | tcg_gen_movi_ptr(ptr, 0); |
38b47b19 EC |
125 | tcg_gen_ld_i64(val, ptr, 0); |
126 | /* pass an immediate != 0 so that it doesn't get optimized away */ | |
127 | tcg_gen_addi_i64(val, val, 0xdeadface); | |
128 | tcg_gen_st_i64(val, ptr, 0); | |
129 | tcg_temp_free_ptr(ptr); | |
130 | tcg_temp_free_i64(val); | |
131 | } | |
132 | ||
fcdab382 | 133 | static void gen_empty_mem_cb(TCGv_i64 addr, uint32_t info) |
38b47b19 | 134 | { |
b6d91645 RH |
135 | TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); |
136 | TCGv_i32 meminfo = tcg_temp_ebb_new_i32(); | |
b6d91645 RH |
137 | TCGv_ptr udata = tcg_temp_ebb_new_ptr(); |
138 | ||
139 | tcg_gen_movi_i32(meminfo, info); | |
140 | tcg_gen_movi_ptr(udata, 0); | |
141 | tcg_gen_ld_i32(cpu_index, cpu_env, | |
142 | -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); | |
b6d91645 | 143 | |
fcdab382 | 144 | gen_helper_plugin_vcpu_mem_cb(cpu_index, meminfo, addr, udata); |
b6d91645 RH |
145 | |
146 | tcg_temp_free_ptr(udata); | |
b6d91645 RH |
147 | tcg_temp_free_i32(meminfo); |
148 | tcg_temp_free_i32(cpu_index); | |
38b47b19 EC |
149 | } |
150 | ||
151 | /* | |
152 | * Share the same function for enable/disable. When enabling, the NULL | |
153 | * pointer will be overwritten later. | |
154 | */ | |
155 | static void gen_empty_mem_helper(void) | |
156 | { | |
713f263a | 157 | TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); |
38b47b19 | 158 | |
713f263a | 159 | tcg_gen_movi_ptr(ptr, 0); |
38b47b19 EC |
160 | tcg_gen_st_ptr(ptr, cpu_env, offsetof(CPUState, plugin_mem_cbs) - |
161 | offsetof(ArchCPU, env)); | |
162 | tcg_temp_free_ptr(ptr); | |
163 | } | |
164 | ||
9a3ee366 RH |
165 | static void gen_plugin_cb_start(enum plugin_gen_from from, |
166 | enum plugin_gen_cb type, unsigned wr) | |
38b47b19 | 167 | { |
38b47b19 | 168 | tcg_gen_plugin_cb_start(from, type, wr); |
38b47b19 EC |
169 | } |
170 | ||
171 | static void gen_wrapped(enum plugin_gen_from from, | |
172 | enum plugin_gen_cb type, void (*func)(void)) | |
173 | { | |
174 | gen_plugin_cb_start(from, type, 0); | |
175 | func(); | |
176 | tcg_gen_plugin_cb_end(); | |
177 | } | |
178 | ||
9a3ee366 | 179 | static void plugin_gen_empty_callback(enum plugin_gen_from from) |
38b47b19 EC |
180 | { |
181 | switch (from) { | |
182 | case PLUGIN_GEN_AFTER_INSN: | |
183 | gen_wrapped(from, PLUGIN_GEN_DISABLE_MEM_HELPER, | |
184 | gen_empty_mem_helper); | |
185 | break; | |
186 | case PLUGIN_GEN_FROM_INSN: | |
187 | /* | |
188 | * Note: plugin_gen_inject() relies on ENABLE_MEM_HELPER being | |
189 | * the first callback of an instruction | |
190 | */ | |
191 | gen_wrapped(from, PLUGIN_GEN_ENABLE_MEM_HELPER, | |
192 | gen_empty_mem_helper); | |
193 | /* fall through */ | |
194 | case PLUGIN_GEN_FROM_TB: | |
195 | gen_wrapped(from, PLUGIN_GEN_CB_UDATA, gen_empty_udata_cb); | |
196 | gen_wrapped(from, PLUGIN_GEN_CB_INLINE, gen_empty_inline_cb); | |
197 | break; | |
198 | default: | |
199 | g_assert_not_reached(); | |
200 | } | |
201 | } | |
202 | ||
fcdab382 | 203 | void plugin_gen_empty_mem_callback(TCGv_i64 addr, uint32_t info) |
38b47b19 | 204 | { |
37aff087 | 205 | enum qemu_plugin_mem_rw rw = get_plugin_meminfo_rw(info); |
38b47b19 | 206 | |
f5c346ac RH |
207 | gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_MEM, rw); |
208 | gen_empty_mem_cb(addr, info); | |
38b47b19 | 209 | tcg_gen_plugin_cb_end(); |
38b47b19 | 210 | |
f5c346ac RH |
211 | gen_plugin_cb_start(PLUGIN_GEN_FROM_MEM, PLUGIN_GEN_CB_INLINE, rw); |
212 | gen_empty_inline_cb(); | |
213 | tcg_gen_plugin_cb_end(); | |
38b47b19 EC |
214 | } |
215 | ||
216 | static TCGOp *find_op(TCGOp *op, TCGOpcode opc) | |
217 | { | |
218 | while (op) { | |
219 | if (op->opc == opc) { | |
220 | return op; | |
221 | } | |
222 | op = QTAILQ_NEXT(op, link); | |
223 | } | |
224 | return NULL; | |
225 | } | |
226 | ||
227 | static TCGOp *rm_ops_range(TCGOp *begin, TCGOp *end) | |
228 | { | |
229 | TCGOp *ret = QTAILQ_NEXT(end, link); | |
230 | ||
231 | QTAILQ_REMOVE_SEVERAL(&tcg_ctx->ops, begin, end, link); | |
232 | return ret; | |
233 | } | |
234 | ||
235 | /* remove all ops until (and including) plugin_cb_end */ | |
236 | static TCGOp *rm_ops(TCGOp *op) | |
237 | { | |
238 | TCGOp *end_op = find_op(op, INDEX_op_plugin_cb_end); | |
239 | ||
240 | tcg_debug_assert(end_op); | |
241 | return rm_ops_range(op, end_op); | |
242 | } | |
243 | ||
244 | static TCGOp *copy_op_nocheck(TCGOp **begin_op, TCGOp *op) | |
245 | { | |
cb10bc63 RH |
246 | TCGOp *old_op = QTAILQ_NEXT(*begin_op, link); |
247 | unsigned nargs = old_op->nargs; | |
d4478943 | 248 | |
cb10bc63 RH |
249 | *begin_op = old_op; |
250 | op = tcg_op_insert_after(tcg_ctx, op, old_op->opc, nargs); | |
251 | memcpy(op->args, old_op->args, sizeof(op->args[0]) * nargs); | |
d4478943 | 252 | |
38b47b19 EC |
253 | return op; |
254 | } | |
255 | ||
256 | static TCGOp *copy_op(TCGOp **begin_op, TCGOp *op, TCGOpcode opc) | |
257 | { | |
258 | op = copy_op_nocheck(begin_op, op); | |
259 | tcg_debug_assert((*begin_op)->opc == opc); | |
260 | return op; | |
261 | } | |
262 | ||
38b47b19 EC |
263 | static TCGOp *copy_const_ptr(TCGOp **begin_op, TCGOp *op, void *ptr) |
264 | { | |
265 | if (UINTPTR_MAX == UINT32_MAX) { | |
80c44bba RH |
266 | /* mov_i32 */ |
267 | op = copy_op(begin_op, op, INDEX_op_mov_i32); | |
268 | op->args[1] = tcgv_i32_arg(tcg_constant_i32((uintptr_t)ptr)); | |
38b47b19 | 269 | } else { |
80c44bba RH |
270 | /* mov_i64 */ |
271 | op = copy_op(begin_op, op, INDEX_op_mov_i64); | |
272 | op->args[1] = tcgv_i64_arg(tcg_constant_i64((uintptr_t)ptr)); | |
38b47b19 EC |
273 | } |
274 | return op; | |
275 | } | |
276 | ||
38b47b19 EC |
277 | static TCGOp *copy_ld_i64(TCGOp **begin_op, TCGOp *op) |
278 | { | |
279 | if (TCG_TARGET_REG_BITS == 32) { | |
280 | /* 2x ld_i32 */ | |
281 | op = copy_op(begin_op, op, INDEX_op_ld_i32); | |
282 | op = copy_op(begin_op, op, INDEX_op_ld_i32); | |
283 | } else { | |
284 | /* ld_i64 */ | |
285 | op = copy_op(begin_op, op, INDEX_op_ld_i64); | |
286 | } | |
287 | return op; | |
288 | } | |
289 | ||
290 | static TCGOp *copy_st_i64(TCGOp **begin_op, TCGOp *op) | |
291 | { | |
292 | if (TCG_TARGET_REG_BITS == 32) { | |
293 | /* 2x st_i32 */ | |
294 | op = copy_op(begin_op, op, INDEX_op_st_i32); | |
295 | op = copy_op(begin_op, op, INDEX_op_st_i32); | |
296 | } else { | |
297 | /* st_i64 */ | |
298 | op = copy_op(begin_op, op, INDEX_op_st_i64); | |
299 | } | |
300 | return op; | |
301 | } | |
302 | ||
0d6e6cb7 | 303 | static TCGOp *copy_add_i64(TCGOp **begin_op, TCGOp *op, uint64_t v) |
38b47b19 EC |
304 | { |
305 | if (TCG_TARGET_REG_BITS == 32) { | |
306 | /* all 32-bit backends must implement add2_i32 */ | |
307 | g_assert(TCG_TARGET_HAS_add2_i32); | |
308 | op = copy_op(begin_op, op, INDEX_op_add2_i32); | |
0d6e6cb7 AB |
309 | op->args[4] = tcgv_i32_arg(tcg_constant_i32(v)); |
310 | op->args[5] = tcgv_i32_arg(tcg_constant_i32(v >> 32)); | |
38b47b19 EC |
311 | } else { |
312 | op = copy_op(begin_op, op, INDEX_op_add_i64); | |
0d6e6cb7 | 313 | op->args[2] = tcgv_i64_arg(tcg_constant_i64(v)); |
38b47b19 EC |
314 | } |
315 | return op; | |
316 | } | |
317 | ||
318 | static TCGOp *copy_st_ptr(TCGOp **begin_op, TCGOp *op) | |
319 | { | |
320 | if (UINTPTR_MAX == UINT32_MAX) { | |
321 | /* st_i32 */ | |
322 | op = copy_op(begin_op, op, INDEX_op_st_i32); | |
323 | } else { | |
324 | /* st_i64 */ | |
325 | op = copy_st_i64(begin_op, op); | |
326 | } | |
327 | return op; | |
328 | } | |
329 | ||
330 | static TCGOp *copy_call(TCGOp **begin_op, TCGOp *op, void *empty_func, | |
c7bb41b4 | 331 | void *func, int *cb_idx) |
38b47b19 | 332 | { |
05d019ab RH |
333 | TCGOp *old_op; |
334 | int func_idx; | |
335 | ||
38b47b19 EC |
336 | /* copy all ops until the call */ |
337 | do { | |
338 | op = copy_op_nocheck(begin_op, op); | |
339 | } while (op->opc != INDEX_op_call); | |
340 | ||
341 | /* fill in the op call */ | |
05d019ab RH |
342 | old_op = *begin_op; |
343 | TCGOP_CALLI(op) = TCGOP_CALLI(old_op); | |
344 | TCGOP_CALLO(op) = TCGOP_CALLO(old_op); | |
38b47b19 | 345 | tcg_debug_assert(op->life == 0); |
38b47b19 | 346 | |
05d019ab RH |
347 | func_idx = TCGOP_CALLO(op) + TCGOP_CALLI(op); |
348 | *cb_idx = func_idx; | |
05d019ab | 349 | op->args[func_idx] = (uintptr_t)func; |
38b47b19 EC |
350 | |
351 | return op; | |
352 | } | |
353 | ||
0d6e6cb7 AB |
354 | /* |
355 | * When we append/replace ops here we are sensitive to changing patterns of | |
356 | * TCGOps generated by the tcg_gen_FOO calls when we generated the | |
357 | * empty callbacks. This will assert very quickly in a debug build as | |
358 | * we assert the ops we are replacing are the correct ones. | |
359 | */ | |
38b47b19 EC |
360 | static TCGOp *append_udata_cb(const struct qemu_plugin_dyn_cb *cb, |
361 | TCGOp *begin_op, TCGOp *op, int *cb_idx) | |
362 | { | |
363 | /* const_ptr */ | |
364 | op = copy_const_ptr(&begin_op, op, cb->userp); | |
365 | ||
366 | /* copy the ld_i32, but note that we only have to copy it once */ | |
38b47b19 | 367 | if (*cb_idx == -1) { |
f266bec8 RH |
368 | op = copy_op(&begin_op, op, INDEX_op_ld_i32); |
369 | } else { | |
370 | begin_op = QTAILQ_NEXT(begin_op, link); | |
371 | tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); | |
38b47b19 EC |
372 | } |
373 | ||
374 | /* call */ | |
375 | op = copy_call(&begin_op, op, HELPER(plugin_vcpu_udata_cb), | |
c7bb41b4 | 376 | cb->f.vcpu_udata, cb_idx); |
38b47b19 EC |
377 | |
378 | return op; | |
379 | } | |
380 | ||
381 | static TCGOp *append_inline_cb(const struct qemu_plugin_dyn_cb *cb, | |
382 | TCGOp *begin_op, TCGOp *op, | |
383 | int *unused) | |
384 | { | |
385 | /* const_ptr */ | |
386 | op = copy_const_ptr(&begin_op, op, cb->userp); | |
387 | ||
388 | /* ld_i64 */ | |
389 | op = copy_ld_i64(&begin_op, op); | |
390 | ||
38b47b19 | 391 | /* add_i64 */ |
0d6e6cb7 | 392 | op = copy_add_i64(&begin_op, op, cb->inline_insn.imm); |
38b47b19 EC |
393 | |
394 | /* st_i64 */ | |
395 | op = copy_st_i64(&begin_op, op); | |
396 | ||
397 | return op; | |
398 | } | |
399 | ||
400 | static TCGOp *append_mem_cb(const struct qemu_plugin_dyn_cb *cb, | |
401 | TCGOp *begin_op, TCGOp *op, int *cb_idx) | |
402 | { | |
403 | enum plugin_gen_cb type = begin_op->args[1]; | |
404 | ||
405 | tcg_debug_assert(type == PLUGIN_GEN_CB_MEM); | |
406 | ||
80c44bba RH |
407 | /* const_i32 == mov_i32 ("info", so it remains as is) */ |
408 | op = copy_op(&begin_op, op, INDEX_op_mov_i32); | |
38b47b19 EC |
409 | |
410 | /* const_ptr */ | |
411 | op = copy_const_ptr(&begin_op, op, cb->userp); | |
412 | ||
413 | /* copy the ld_i32, but note that we only have to copy it once */ | |
38b47b19 | 414 | if (*cb_idx == -1) { |
f266bec8 RH |
415 | op = copy_op(&begin_op, op, INDEX_op_ld_i32); |
416 | } else { | |
417 | begin_op = QTAILQ_NEXT(begin_op, link); | |
418 | tcg_debug_assert(begin_op && begin_op->opc == INDEX_op_ld_i32); | |
38b47b19 EC |
419 | } |
420 | ||
38b47b19 EC |
421 | if (type == PLUGIN_GEN_CB_MEM) { |
422 | /* call */ | |
423 | op = copy_call(&begin_op, op, HELPER(plugin_vcpu_mem_cb), | |
c7bb41b4 | 424 | cb->f.vcpu_udata, cb_idx); |
38b47b19 EC |
425 | } |
426 | ||
427 | return op; | |
428 | } | |
429 | ||
430 | typedef TCGOp *(*inject_fn)(const struct qemu_plugin_dyn_cb *cb, | |
431 | TCGOp *begin_op, TCGOp *op, int *intp); | |
432 | typedef bool (*op_ok_fn)(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb); | |
433 | ||
434 | static bool op_ok(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb) | |
435 | { | |
436 | return true; | |
437 | } | |
438 | ||
439 | static bool op_rw(const TCGOp *op, const struct qemu_plugin_dyn_cb *cb) | |
440 | { | |
441 | int w; | |
442 | ||
443 | w = op->args[2]; | |
444 | return !!(cb->rw & (w + 1)); | |
445 | } | |
446 | ||
9a3ee366 RH |
447 | static void inject_cb_type(const GArray *cbs, TCGOp *begin_op, |
448 | inject_fn inject, op_ok_fn ok) | |
38b47b19 EC |
449 | { |
450 | TCGOp *end_op; | |
451 | TCGOp *op; | |
452 | int cb_idx = -1; | |
453 | int i; | |
454 | ||
455 | if (!cbs || cbs->len == 0) { | |
456 | rm_ops(begin_op); | |
457 | return; | |
458 | } | |
459 | ||
460 | end_op = find_op(begin_op, INDEX_op_plugin_cb_end); | |
461 | tcg_debug_assert(end_op); | |
462 | ||
463 | op = end_op; | |
464 | for (i = 0; i < cbs->len; i++) { | |
465 | struct qemu_plugin_dyn_cb *cb = | |
466 | &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); | |
467 | ||
468 | if (!ok(begin_op, cb)) { | |
469 | continue; | |
470 | } | |
471 | op = inject(cb, begin_op, op, &cb_idx); | |
472 | } | |
473 | rm_ops_range(begin_op, end_op); | |
474 | } | |
475 | ||
476 | static void | |
477 | inject_udata_cb(const GArray *cbs, TCGOp *begin_op) | |
478 | { | |
479 | inject_cb_type(cbs, begin_op, append_udata_cb, op_ok); | |
480 | } | |
481 | ||
482 | static void | |
483 | inject_inline_cb(const GArray *cbs, TCGOp *begin_op, op_ok_fn ok) | |
484 | { | |
485 | inject_cb_type(cbs, begin_op, append_inline_cb, ok); | |
486 | } | |
487 | ||
488 | static void | |
489 | inject_mem_cb(const GArray *cbs, TCGOp *begin_op) | |
490 | { | |
491 | inject_cb_type(cbs, begin_op, append_mem_cb, op_rw); | |
492 | } | |
493 | ||
494 | /* we could change the ops in place, but we can reuse more code by copying */ | |
495 | static void inject_mem_helper(TCGOp *begin_op, GArray *arr) | |
496 | { | |
497 | TCGOp *orig_op = begin_op; | |
498 | TCGOp *end_op; | |
499 | TCGOp *op; | |
500 | ||
501 | end_op = find_op(begin_op, INDEX_op_plugin_cb_end); | |
502 | tcg_debug_assert(end_op); | |
503 | ||
504 | /* const ptr */ | |
505 | op = copy_const_ptr(&begin_op, end_op, arr); | |
506 | ||
507 | /* st_ptr */ | |
508 | op = copy_st_ptr(&begin_op, op); | |
509 | ||
510 | rm_ops_range(orig_op, end_op); | |
511 | } | |
512 | ||
513 | /* | |
514 | * Tracking memory accesses performed from helpers requires extra work. | |
515 | * If an instruction is emulated with helpers, we do two things: | |
516 | * (1) copy the CB descriptors, and keep track of it so that they can be | |
517 | * freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptors, so | |
518 | * that we can read them at run-time (i.e. when the helper executes). | |
519 | * This run-time access is performed from qemu_plugin_vcpu_mem_cb. | |
520 | * | |
521 | * Note that plugin_gen_disable_mem_helpers undoes (2). Since it | |
522 | * is possible that the code we generate after the instruction is | |
523 | * dead, we also add checks before generating tb_exit etc. | |
524 | */ | |
3fd62e73 EC |
525 | static void inject_mem_enable_helper(struct qemu_plugin_tb *ptb, |
526 | struct qemu_plugin_insn *plugin_insn, | |
38b47b19 EC |
527 | TCGOp *begin_op) |
528 | { | |
529 | GArray *cbs[2]; | |
530 | GArray *arr; | |
531 | size_t n_cbs, i; | |
532 | ||
533 | cbs[0] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; | |
534 | cbs[1] = plugin_insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; | |
535 | ||
536 | n_cbs = 0; | |
537 | for (i = 0; i < ARRAY_SIZE(cbs); i++) { | |
538 | n_cbs += cbs[i]->len; | |
539 | } | |
540 | ||
541 | plugin_insn->mem_helper = plugin_insn->calls_helpers && n_cbs; | |
542 | if (likely(!plugin_insn->mem_helper)) { | |
543 | rm_ops(begin_op); | |
544 | return; | |
545 | } | |
3fd62e73 | 546 | ptb->mem_helper = true; |
38b47b19 EC |
547 | |
548 | arr = g_array_sized_new(false, false, | |
549 | sizeof(struct qemu_plugin_dyn_cb), n_cbs); | |
550 | ||
551 | for (i = 0; i < ARRAY_SIZE(cbs); i++) { | |
552 | g_array_append_vals(arr, cbs[i]->data, cbs[i]->len); | |
553 | } | |
554 | ||
555 | qemu_plugin_add_dyn_cb_arr(arr); | |
556 | inject_mem_helper(begin_op, arr); | |
557 | } | |
558 | ||
559 | static void inject_mem_disable_helper(struct qemu_plugin_insn *plugin_insn, | |
560 | TCGOp *begin_op) | |
561 | { | |
562 | if (likely(!plugin_insn->mem_helper)) { | |
563 | rm_ops(begin_op); | |
564 | return; | |
565 | } | |
566 | inject_mem_helper(begin_op, NULL); | |
567 | } | |
568 | ||
569 | /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ | |
570 | void plugin_gen_disable_mem_helpers(void) | |
571 | { | |
3fd62e73 EC |
572 | /* |
573 | * We could emit the clearing unconditionally and be done. However, this can | |
574 | * be wasteful if for instance plugins don't track memory accesses, or if | |
575 | * most TBs don't use helpers. Instead, emit the clearing iff the TB calls | |
576 | * helpers that might access guest memory. | |
577 | * | |
578 | * Note: we do not reset plugin_tb->mem_helper here; a TB might have several | |
579 | * exit points, and we want to emit the clearing from all of them. | |
580 | */ | |
581 | if (!tcg_ctx->plugin_tb->mem_helper) { | |
38b47b19 EC |
582 | return; |
583 | } | |
40138843 RH |
584 | tcg_gen_st_ptr(tcg_constant_ptr(NULL), cpu_env, |
585 | offsetof(CPUState, plugin_mem_cbs) - offsetof(ArchCPU, env)); | |
38b47b19 EC |
586 | } |
587 | ||
588 | static void plugin_gen_tb_udata(const struct qemu_plugin_tb *ptb, | |
589 | TCGOp *begin_op) | |
590 | { | |
591 | inject_udata_cb(ptb->cbs[PLUGIN_CB_REGULAR], begin_op); | |
592 | } | |
593 | ||
594 | static void plugin_gen_tb_inline(const struct qemu_plugin_tb *ptb, | |
595 | TCGOp *begin_op) | |
596 | { | |
597 | inject_inline_cb(ptb->cbs[PLUGIN_CB_INLINE], begin_op, op_ok); | |
598 | } | |
599 | ||
600 | static void plugin_gen_insn_udata(const struct qemu_plugin_tb *ptb, | |
601 | TCGOp *begin_op, int insn_idx) | |
602 | { | |
603 | struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); | |
604 | ||
605 | inject_udata_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR], begin_op); | |
606 | } | |
607 | ||
608 | static void plugin_gen_insn_inline(const struct qemu_plugin_tb *ptb, | |
609 | TCGOp *begin_op, int insn_idx) | |
610 | { | |
611 | struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); | |
612 | inject_inline_cb(insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE], | |
613 | begin_op, op_ok); | |
614 | } | |
615 | ||
616 | static void plugin_gen_mem_regular(const struct qemu_plugin_tb *ptb, | |
617 | TCGOp *begin_op, int insn_idx) | |
618 | { | |
619 | struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); | |
620 | inject_mem_cb(insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR], begin_op); | |
621 | } | |
622 | ||
623 | static void plugin_gen_mem_inline(const struct qemu_plugin_tb *ptb, | |
624 | TCGOp *begin_op, int insn_idx) | |
625 | { | |
626 | const GArray *cbs; | |
627 | struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); | |
628 | ||
629 | cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; | |
630 | inject_inline_cb(cbs, begin_op, op_rw); | |
631 | } | |
632 | ||
3fd62e73 | 633 | static void plugin_gen_enable_mem_helper(struct qemu_plugin_tb *ptb, |
38b47b19 EC |
634 | TCGOp *begin_op, int insn_idx) |
635 | { | |
636 | struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); | |
3fd62e73 | 637 | inject_mem_enable_helper(ptb, insn, begin_op); |
38b47b19 EC |
638 | } |
639 | ||
3fd62e73 | 640 | static void plugin_gen_disable_mem_helper(struct qemu_plugin_tb *ptb, |
38b47b19 EC |
641 | TCGOp *begin_op, int insn_idx) |
642 | { | |
643 | struct qemu_plugin_insn *insn = g_ptr_array_index(ptb->insns, insn_idx); | |
644 | inject_mem_disable_helper(insn, begin_op); | |
645 | } | |
646 | ||
38b47b19 EC |
647 | /* #define DEBUG_PLUGIN_GEN_OPS */ |
648 | static void pr_ops(void) | |
649 | { | |
650 | #ifdef DEBUG_PLUGIN_GEN_OPS | |
651 | TCGOp *op; | |
652 | int i = 0; | |
653 | ||
654 | QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { | |
655 | const char *name = ""; | |
656 | const char *type = ""; | |
657 | ||
658 | if (op->opc == INDEX_op_plugin_cb_start) { | |
659 | switch (op->args[0]) { | |
660 | case PLUGIN_GEN_FROM_TB: | |
661 | name = "tb"; | |
662 | break; | |
663 | case PLUGIN_GEN_FROM_INSN: | |
664 | name = "insn"; | |
665 | break; | |
666 | case PLUGIN_GEN_FROM_MEM: | |
667 | name = "mem"; | |
668 | break; | |
669 | case PLUGIN_GEN_AFTER_INSN: | |
670 | name = "after insn"; | |
671 | break; | |
672 | default: | |
673 | break; | |
674 | } | |
675 | switch (op->args[1]) { | |
676 | case PLUGIN_GEN_CB_UDATA: | |
677 | type = "udata"; | |
678 | break; | |
679 | case PLUGIN_GEN_CB_INLINE: | |
680 | type = "inline"; | |
681 | break; | |
682 | case PLUGIN_GEN_CB_MEM: | |
683 | type = "mem"; | |
684 | break; | |
685 | case PLUGIN_GEN_ENABLE_MEM_HELPER: | |
686 | type = "enable mem helper"; | |
687 | break; | |
688 | case PLUGIN_GEN_DISABLE_MEM_HELPER: | |
689 | type = "disable mem helper"; | |
690 | break; | |
691 | default: | |
692 | break; | |
693 | } | |
694 | } | |
695 | printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type); | |
696 | i++; | |
697 | } | |
698 | #endif | |
699 | } | |
700 | ||
3fd62e73 | 701 | static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) |
38b47b19 EC |
702 | { |
703 | TCGOp *op; | |
453d50ce | 704 | int insn_idx = -1; |
38b47b19 EC |
705 | |
706 | pr_ops(); | |
453d50ce AB |
707 | |
708 | QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { | |
709 | switch (op->opc) { | |
710 | case INDEX_op_insn_start: | |
38b47b19 | 711 | insn_idx++; |
453d50ce AB |
712 | break; |
713 | case INDEX_op_plugin_cb_start: | |
714 | { | |
715 | enum plugin_gen_from from = op->args[0]; | |
716 | enum plugin_gen_cb type = op->args[1]; | |
717 | ||
718 | switch (from) { | |
719 | case PLUGIN_GEN_FROM_TB: | |
720 | { | |
721 | g_assert(insn_idx == -1); | |
722 | ||
723 | switch (type) { | |
724 | case PLUGIN_GEN_CB_UDATA: | |
725 | plugin_gen_tb_udata(plugin_tb, op); | |
726 | break; | |
727 | case PLUGIN_GEN_CB_INLINE: | |
728 | plugin_gen_tb_inline(plugin_tb, op); | |
729 | break; | |
730 | default: | |
731 | g_assert_not_reached(); | |
732 | } | |
733 | break; | |
734 | } | |
735 | case PLUGIN_GEN_FROM_INSN: | |
736 | { | |
737 | g_assert(insn_idx >= 0); | |
738 | ||
739 | switch (type) { | |
740 | case PLUGIN_GEN_CB_UDATA: | |
741 | plugin_gen_insn_udata(plugin_tb, op, insn_idx); | |
742 | break; | |
743 | case PLUGIN_GEN_CB_INLINE: | |
744 | plugin_gen_insn_inline(plugin_tb, op, insn_idx); | |
745 | break; | |
746 | case PLUGIN_GEN_ENABLE_MEM_HELPER: | |
747 | plugin_gen_enable_mem_helper(plugin_tb, op, insn_idx); | |
748 | break; | |
749 | default: | |
750 | g_assert_not_reached(); | |
751 | } | |
752 | break; | |
753 | } | |
754 | case PLUGIN_GEN_FROM_MEM: | |
755 | { | |
756 | g_assert(insn_idx >= 0); | |
757 | ||
758 | switch (type) { | |
759 | case PLUGIN_GEN_CB_MEM: | |
760 | plugin_gen_mem_regular(plugin_tb, op, insn_idx); | |
761 | break; | |
762 | case PLUGIN_GEN_CB_INLINE: | |
763 | plugin_gen_mem_inline(plugin_tb, op, insn_idx); | |
764 | break; | |
765 | default: | |
766 | g_assert_not_reached(); | |
767 | } | |
768 | ||
769 | break; | |
770 | } | |
771 | case PLUGIN_GEN_AFTER_INSN: | |
772 | { | |
773 | g_assert(insn_idx >= 0); | |
774 | ||
775 | switch (type) { | |
776 | case PLUGIN_GEN_DISABLE_MEM_HELPER: | |
777 | plugin_gen_disable_mem_helper(plugin_tb, op, insn_idx); | |
778 | break; | |
779 | default: | |
780 | g_assert_not_reached(); | |
781 | } | |
782 | break; | |
783 | } | |
784 | default: | |
785 | g_assert_not_reached(); | |
786 | } | |
787 | break; | |
788 | } | |
789 | default: | |
790 | /* plugins don't care about any other ops */ | |
791 | break; | |
38b47b19 | 792 | } |
38b47b19 EC |
793 | } |
794 | pr_ops(); | |
795 | } | |
796 | ||
b21af662 RH |
797 | bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, |
798 | bool mem_only) | |
38b47b19 | 799 | { |
38b47b19 EC |
800 | bool ret = false; |
801 | ||
802 | if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_mask)) { | |
6f15c076 AB |
803 | struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; |
804 | int i; | |
805 | ||
806 | /* reset callbacks */ | |
807 | for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) { | |
808 | if (ptb->cbs[i]) { | |
809 | g_array_set_size(ptb->cbs[i], 0); | |
810 | } | |
811 | } | |
812 | ptb->n = 0; | |
813 | ||
38b47b19 EC |
814 | ret = true; |
815 | ||
b21af662 | 816 | ptb->vaddr = db->pc_first; |
38b47b19 | 817 | ptb->vaddr2 = -1; |
b21af662 | 818 | ptb->haddr1 = db->host_addr[0]; |
38b47b19 | 819 | ptb->haddr2 = NULL; |
cfd405ea | 820 | ptb->mem_only = mem_only; |
3fd62e73 | 821 | ptb->mem_helper = false; |
38b47b19 EC |
822 | |
823 | plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); | |
824 | } | |
6f15c076 AB |
825 | |
826 | tcg_ctx->plugin_insn = NULL; | |
827 | ||
38b47b19 EC |
828 | return ret; |
829 | } | |
830 | ||
831 | void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) | |
832 | { | |
833 | struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; | |
834 | struct qemu_plugin_insn *pinsn; | |
835 | ||
357af9be | 836 | pinsn = qemu_plugin_tb_insn_get(ptb, db->pc_next); |
38b47b19 | 837 | tcg_ctx->plugin_insn = pinsn; |
38b47b19 EC |
838 | plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN); |
839 | ||
840 | /* | |
841 | * Detect page crossing to get the new host address. | |
842 | * Note that we skip this when haddr1 == NULL, e.g. when we're | |
843 | * fetching instructions from a region not backed by RAM. | |
844 | */ | |
b21af662 RH |
845 | if (ptb->haddr1 == NULL) { |
846 | pinsn->haddr = NULL; | |
847 | } else if (is_same_page(db, db->pc_next)) { | |
38b47b19 EC |
848 | pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr; |
849 | } else { | |
b21af662 RH |
850 | if (ptb->vaddr2 == -1) { |
851 | ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first); | |
852 | get_page_addr_code_hostp(cpu->env_ptr, ptb->vaddr2, &ptb->haddr2); | |
853 | } | |
38b47b19 EC |
854 | pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2; |
855 | } | |
856 | } | |
857 | ||
858 | void plugin_gen_insn_end(void) | |
859 | { | |
860 | plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN); | |
861 | } | |
862 | ||
6f15c076 AB |
863 | /* |
864 | * There are cases where we never get to finalise a translation - for | |
865 | * example a page fault during translation. As a result we shouldn't | |
866 | * do any clean-up here and make sure things are reset in | |
867 | * plugin_gen_tb_start. | |
868 | */ | |
38b47b19 EC |
869 | void plugin_gen_tb_end(CPUState *cpu) |
870 | { | |
871 | struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; | |
38b47b19 EC |
872 | |
873 | /* collect instrumentation requests */ | |
874 | qemu_plugin_tb_trans_cb(cpu, ptb); | |
875 | ||
876 | /* inject the instrumentation at the appropriate places */ | |
877 | plugin_gen_inject(ptb); | |
38b47b19 | 878 | } |