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