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" 46 #include "qemu/plugin.h" 47 #include "cpu.h" 48 #include "tcg/tcg.h" 49 #include "tcg/tcg-temp-internal.h" 50 #include "tcg/tcg-op.h" 51 #include "exec/exec-all.h" 52 #include "exec/plugin-gen.h" 53 #include "exec/translator.h" 54 55 /* 56 * plugin_cb_start TCG op args[]: 57 * 0: enum plugin_gen_from 58 * 1: enum plugin_gen_cb 59 * 2: set to 1 for mem callback that is a write, 0 otherwise. 60 */ 61 62 enum plugin_gen_from { 63 PLUGIN_GEN_FROM_TB, 64 PLUGIN_GEN_FROM_INSN, 65 PLUGIN_GEN_AFTER_INSN, 66 PLUGIN_GEN_AFTER_TB, 67 PLUGIN_GEN_N_FROMS, 68 }; 69 70 enum plugin_gen_cb { 71 PLUGIN_GEN_CB_UDATA, 72 PLUGIN_GEN_CB_UDATA_R, 73 PLUGIN_GEN_CB_INLINE, 74 PLUGIN_GEN_CB_MEM, 75 PLUGIN_GEN_ENABLE_MEM_HELPER, 76 PLUGIN_GEN_DISABLE_MEM_HELPER, 77 PLUGIN_GEN_N_CBS, 78 }; 79 80 static void plugin_gen_empty_callback(enum plugin_gen_from from) 81 { 82 switch (from) { 83 case PLUGIN_GEN_AFTER_INSN: 84 case PLUGIN_GEN_FROM_TB: 85 case PLUGIN_GEN_FROM_INSN: 86 tcg_gen_plugin_cb(from); 87 break; 88 default: 89 g_assert_not_reached(); 90 } 91 } 92 93 /* called before finishing a TB with exit_tb, goto_tb or goto_ptr */ 94 void plugin_gen_disable_mem_helpers(void) 95 { 96 if (tcg_ctx->plugin_insn) { 97 tcg_gen_plugin_cb(PLUGIN_GEN_AFTER_TB); 98 } 99 } 100 101 static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, 102 struct qemu_plugin_insn *insn) 103 { 104 GArray *cbs[2]; 105 GArray *arr; 106 size_t n_cbs; 107 108 /* 109 * Tracking memory accesses performed from helpers requires extra work. 110 * If an instruction is emulated with helpers, we do two things: 111 * (1) copy the CB descriptors, and keep track of it so that they can be 112 * freed later on, and (2) point CPUState.plugin_mem_cbs to the 113 * descriptors, so that we can read them at run-time 114 * (i.e. when the helper executes). 115 * This run-time access is performed from qemu_plugin_vcpu_mem_cb. 116 * 117 * Note that plugin_gen_disable_mem_helpers undoes (2). Since it 118 * is possible that the code we generate after the instruction is 119 * dead, we also add checks before generating tb_exit etc. 120 */ 121 if (!insn->calls_helpers) { 122 return; 123 } 124 125 cbs[0] = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; 126 cbs[1] = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; 127 n_cbs = cbs[0]->len + cbs[1]->len; 128 129 if (n_cbs == 0) { 130 insn->mem_helper = false; 131 return; 132 } 133 insn->mem_helper = true; 134 ptb->mem_helper = true; 135 136 arr = g_array_sized_new(false, false, 137 sizeof(struct qemu_plugin_dyn_cb), n_cbs); 138 g_array_append_vals(arr, cbs[0]->data, cbs[0]->len); 139 g_array_append_vals(arr, cbs[1]->data, cbs[1]->len); 140 141 qemu_plugin_add_dyn_cb_arr(arr); 142 143 tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env, 144 offsetof(CPUState, plugin_mem_cbs) - 145 offsetof(ArchCPU, env)); 146 } 147 148 static void gen_disable_mem_helper(void) 149 { 150 tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env, 151 offsetof(CPUState, plugin_mem_cbs) - 152 offsetof(ArchCPU, env)); 153 } 154 155 static void gen_udata_cb(struct qemu_plugin_dyn_cb *cb) 156 { 157 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); 158 159 tcg_gen_ld_i32(cpu_index, tcg_env, 160 -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); 161 tcg_gen_call2(cb->regular.f.vcpu_udata, cb->regular.info, NULL, 162 tcgv_i32_temp(cpu_index), 163 tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); 164 tcg_temp_free_i32(cpu_index); 165 } 166 167 static void gen_inline_cb(struct qemu_plugin_dyn_cb *cb) 168 { 169 GArray *arr = cb->inline_insn.entry.score->data; 170 size_t offset = cb->inline_insn.entry.offset; 171 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); 172 TCGv_i64 val = tcg_temp_ebb_new_i64(); 173 TCGv_ptr ptr = tcg_temp_ebb_new_ptr(); 174 175 tcg_gen_ld_i32(cpu_index, tcg_env, 176 -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); 177 tcg_gen_muli_i32(cpu_index, cpu_index, g_array_get_element_size(arr)); 178 tcg_gen_ext_i32_ptr(ptr, cpu_index); 179 tcg_temp_free_i32(cpu_index); 180 181 tcg_gen_addi_ptr(ptr, ptr, (intptr_t)arr->data); 182 tcg_gen_ld_i64(val, ptr, offset); 183 tcg_gen_addi_i64(val, val, cb->inline_insn.imm); 184 tcg_gen_st_i64(val, ptr, offset); 185 186 tcg_temp_free_i64(val); 187 tcg_temp_free_ptr(ptr); 188 } 189 190 static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb, 191 qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) 192 { 193 TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); 194 195 tcg_gen_ld_i32(cpu_index, tcg_env, 196 -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); 197 tcg_gen_call4(cb->regular.f.vcpu_mem, cb->regular.info, NULL, 198 tcgv_i32_temp(cpu_index), 199 tcgv_i32_temp(tcg_constant_i32(meminfo)), 200 tcgv_i64_temp(addr), 201 tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); 202 tcg_temp_free_i32(cpu_index); 203 } 204 205 /* #define DEBUG_PLUGIN_GEN_OPS */ 206 static void pr_ops(void) 207 { 208 #ifdef DEBUG_PLUGIN_GEN_OPS 209 TCGOp *op; 210 int i = 0; 211 212 QTAILQ_FOREACH(op, &tcg_ctx->ops, link) { 213 const char *name = ""; 214 const char *type = ""; 215 216 if (op->opc == INDEX_op_plugin_cb_start) { 217 switch (op->args[0]) { 218 case PLUGIN_GEN_FROM_TB: 219 name = "tb"; 220 break; 221 case PLUGIN_GEN_FROM_INSN: 222 name = "insn"; 223 break; 224 case PLUGIN_GEN_FROM_MEM: 225 name = "mem"; 226 break; 227 case PLUGIN_GEN_AFTER_INSN: 228 name = "after insn"; 229 break; 230 default: 231 break; 232 } 233 switch (op->args[1]) { 234 case PLUGIN_GEN_CB_UDATA: 235 type = "udata"; 236 break; 237 case PLUGIN_GEN_CB_INLINE: 238 type = "inline"; 239 break; 240 case PLUGIN_GEN_CB_MEM: 241 type = "mem"; 242 break; 243 case PLUGIN_GEN_ENABLE_MEM_HELPER: 244 type = "enable mem helper"; 245 break; 246 case PLUGIN_GEN_DISABLE_MEM_HELPER: 247 type = "disable mem helper"; 248 break; 249 default: 250 break; 251 } 252 } 253 printf("op[%2i]: %s %s %s\n", i, tcg_op_defs[op->opc].name, name, type); 254 i++; 255 } 256 #endif 257 } 258 259 static void plugin_gen_inject(struct qemu_plugin_tb *plugin_tb) 260 { 261 TCGOp *op, *next; 262 int insn_idx = -1; 263 264 pr_ops(); 265 266 /* 267 * While injecting code, we cannot afford to reuse any ebb temps 268 * that might be live within the existing opcode stream. 269 * The simplest solution is to release them all and create new. 270 */ 271 memset(tcg_ctx->free_temps, 0, sizeof(tcg_ctx->free_temps)); 272 273 QTAILQ_FOREACH_SAFE(op, &tcg_ctx->ops, link, next) { 274 switch (op->opc) { 275 case INDEX_op_insn_start: 276 insn_idx++; 277 break; 278 279 case INDEX_op_plugin_cb: 280 { 281 enum plugin_gen_from from = op->args[0]; 282 struct qemu_plugin_insn *insn = NULL; 283 const GArray *cbs; 284 int i, n; 285 286 if (insn_idx >= 0) { 287 insn = g_ptr_array_index(plugin_tb->insns, insn_idx); 288 } 289 290 tcg_ctx->emit_before_op = op; 291 292 switch (from) { 293 case PLUGIN_GEN_AFTER_TB: 294 if (plugin_tb->mem_helper) { 295 gen_disable_mem_helper(); 296 } 297 break; 298 299 case PLUGIN_GEN_AFTER_INSN: 300 assert(insn != NULL); 301 if (insn->mem_helper) { 302 gen_disable_mem_helper(); 303 } 304 break; 305 306 case PLUGIN_GEN_FROM_TB: 307 assert(insn == NULL); 308 309 cbs = plugin_tb->cbs[PLUGIN_CB_REGULAR]; 310 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { 311 struct qemu_plugin_dyn_cb *cb = 312 &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); 313 gen_udata_cb(cb); 314 } 315 316 cbs = plugin_tb->cbs[PLUGIN_CB_INLINE]; 317 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { 318 struct qemu_plugin_dyn_cb *cb = 319 &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); 320 gen_inline_cb(cb); 321 } 322 break; 323 324 case PLUGIN_GEN_FROM_INSN: 325 assert(insn != NULL); 326 327 gen_enable_mem_helper(plugin_tb, insn); 328 329 cbs = insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_REGULAR]; 330 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { 331 struct qemu_plugin_dyn_cb *cb = 332 &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); 333 gen_udata_cb(cb); 334 } 335 336 cbs = insn->cbs[PLUGIN_CB_INSN][PLUGIN_CB_INLINE]; 337 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { 338 struct qemu_plugin_dyn_cb *cb = 339 &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); 340 gen_inline_cb(cb); 341 } 342 break; 343 344 default: 345 g_assert_not_reached(); 346 } 347 348 tcg_ctx->emit_before_op = NULL; 349 tcg_op_remove(tcg_ctx, op); 350 break; 351 } 352 353 case INDEX_op_plugin_mem_cb: 354 { 355 TCGv_i64 addr = temp_tcgv_i64(arg_temp(op->args[0])); 356 qemu_plugin_meminfo_t meminfo = op->args[1]; 357 struct qemu_plugin_insn *insn; 358 const GArray *cbs; 359 int i, n, rw; 360 361 assert(insn_idx >= 0); 362 insn = g_ptr_array_index(plugin_tb->insns, insn_idx); 363 rw = qemu_plugin_mem_is_store(meminfo) ? 2 : 1; 364 365 tcg_ctx->emit_before_op = op; 366 367 cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_REGULAR]; 368 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { 369 struct qemu_plugin_dyn_cb *cb = 370 &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); 371 if (cb->rw & rw) { 372 gen_mem_cb(cb, meminfo, addr); 373 } 374 } 375 376 cbs = insn->cbs[PLUGIN_CB_MEM][PLUGIN_CB_INLINE]; 377 for (i = 0, n = (cbs ? cbs->len : 0); i < n; i++) { 378 struct qemu_plugin_dyn_cb *cb = 379 &g_array_index(cbs, struct qemu_plugin_dyn_cb, i); 380 if (cb->rw & rw) { 381 gen_inline_cb(cb); 382 } 383 } 384 385 tcg_ctx->emit_before_op = NULL; 386 tcg_op_remove(tcg_ctx, op); 387 break; 388 } 389 390 default: 391 /* plugins don't care about any other ops */ 392 break; 393 } 394 } 395 pr_ops(); 396 } 397 398 bool plugin_gen_tb_start(CPUState *cpu, const DisasContextBase *db, 399 bool mem_only) 400 { 401 bool ret = false; 402 403 if (test_bit(QEMU_PLUGIN_EV_VCPU_TB_TRANS, cpu->plugin_state->event_mask)) { 404 struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; 405 int i; 406 407 /* reset callbacks */ 408 for (i = 0; i < PLUGIN_N_CB_SUBTYPES; i++) { 409 if (ptb->cbs[i]) { 410 g_array_set_size(ptb->cbs[i], 0); 411 } 412 } 413 ptb->n = 0; 414 415 ret = true; 416 417 ptb->vaddr = db->pc_first; 418 ptb->vaddr2 = -1; 419 ptb->haddr1 = db->host_addr[0]; 420 ptb->haddr2 = NULL; 421 ptb->mem_only = mem_only; 422 ptb->mem_helper = false; 423 424 plugin_gen_empty_callback(PLUGIN_GEN_FROM_TB); 425 } 426 427 tcg_ctx->plugin_insn = NULL; 428 429 return ret; 430 } 431 432 void plugin_gen_insn_start(CPUState *cpu, const DisasContextBase *db) 433 { 434 struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; 435 struct qemu_plugin_insn *pinsn; 436 437 pinsn = qemu_plugin_tb_insn_get(ptb, db->pc_next); 438 tcg_ctx->plugin_insn = pinsn; 439 plugin_gen_empty_callback(PLUGIN_GEN_FROM_INSN); 440 441 /* 442 * Detect page crossing to get the new host address. 443 * Note that we skip this when haddr1 == NULL, e.g. when we're 444 * fetching instructions from a region not backed by RAM. 445 */ 446 if (ptb->haddr1 == NULL) { 447 pinsn->haddr = NULL; 448 } else if (is_same_page(db, db->pc_next)) { 449 pinsn->haddr = ptb->haddr1 + pinsn->vaddr - ptb->vaddr; 450 } else { 451 if (ptb->vaddr2 == -1) { 452 ptb->vaddr2 = TARGET_PAGE_ALIGN(db->pc_first); 453 get_page_addr_code_hostp(cpu_env(cpu), ptb->vaddr2, &ptb->haddr2); 454 } 455 pinsn->haddr = ptb->haddr2 + pinsn->vaddr - ptb->vaddr2; 456 } 457 } 458 459 void plugin_gen_insn_end(void) 460 { 461 plugin_gen_empty_callback(PLUGIN_GEN_AFTER_INSN); 462 } 463 464 /* 465 * There are cases where we never get to finalise a translation - for 466 * example a page fault during translation. As a result we shouldn't 467 * do any clean-up here and make sure things are reset in 468 * plugin_gen_tb_start. 469 */ 470 void plugin_gen_tb_end(CPUState *cpu, size_t num_insns) 471 { 472 struct qemu_plugin_tb *ptb = tcg_ctx->plugin_tb; 473 474 /* translator may have removed instructions, update final count */ 475 g_assert(num_insns <= ptb->n); 476 ptb->n = num_insns; 477 478 /* collect instrumentation requests */ 479 qemu_plugin_tb_trans_cb(cpu, ptb); 480 481 /* inject the instrumentation at the appropriate places */ 482 plugin_gen_inject(ptb); 483 } 484