From patchwork Thu Oct 25 17:20:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Emilio Cota X-Patchwork-Id: 10656261 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id F057E13BF for ; Thu, 25 Oct 2018 17:37:00 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DC39A283D1 for ; Thu, 25 Oct 2018 17:37:00 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D042E2C22A; Thu, 25 Oct 2018 17:37:00 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-7.7 required=2.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 12D9E283D1 for ; Thu, 25 Oct 2018 17:37:00 +0000 (UTC) Received: from localhost ([::1]:56094 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gFjZ5-0004Vc-BY for patchwork-qemu-devel@patchwork.kernel.org; Thu, 25 Oct 2018 13:36:59 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40419) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1gFjKl-0006hz-6y for qemu-devel@nongnu.org; Thu, 25 Oct 2018 13:22:15 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1gFjKH-00007P-Ar for qemu-devel@nongnu.org; Thu, 25 Oct 2018 13:21:44 -0400 Received: from out3-smtp.messagingengine.com ([66.111.4.27]:58817) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1gFjKF-0008Qf-Rg for qemu-devel@nongnu.org; Thu, 25 Oct 2018 13:21:41 -0400 Received: from compute4.internal (compute4.nyi.internal [10.202.2.44]) by mailout.nyi.internal (Postfix) with ESMTP id 20826221CC; Thu, 25 Oct 2018 13:21:11 -0400 (EDT) Received: from mailfrontend1 ([10.202.2.162]) by compute4.internal (MEProxy); Thu, 25 Oct 2018 13:21:11 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=braap.org; h= from:to:cc:subject:date:message-id:in-reply-to:references; s= mesmtp; bh=nHNZGKhfVZ4jUtv2dSbuFnbN8wa/CiobFD05+W8f8Zg=; b=R3V2e mqVpQ1uCYOfK8MxxdzWva8O60nYZ2B3BoqWJXqPJW5vFi/3gsNSz870kysouzojq 5UJvDH51d2BdSuOyBhfYAri7z5Y40UHyx7kKtC/q8t1+ef9NY7osWyWRnMOORzvo NcknC1QNaXh0qYBJ5WbqXtA33i+FrixXJ038nE= DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d= messagingengine.com; h=cc:date:from:in-reply-to:message-id :references:subject:to:x-me-proxy:x-me-proxy:x-me-sender :x-me-sender:x-sasl-enc; s=fm1; bh=nHNZGKhfVZ4jUtv2dSbuFnbN8wa/C iobFD05+W8f8Zg=; b=lyQZoHWCksVtHvfMXQrcqv93o17SYaruSlYA1+8bWLF64 82WgT71F+Gch3x7q8Tl8F1ZDFg/9wkWgx4kX4l1HXuz1eUCpaRnC/J6vb/dz/MzZ SM29/lK4QPX410eTfoCk7ou4ctNDIXkMPuzk5UJAPlFX1lu8kRc0MZNU/pCNz6yz oJg82KCccFY1VMpl083Mx09DFJCf7SX6QIOCgIf8rcLJ9jsTZJyTw0/Y4fjfbFgU sYnLevnFemYRvesqigICQ/Tx9xa5TjrZBECphjn/+lHG1Kexjg4zWxEfLfiFbCCE /5Rl3OEKhI9FNTIoJ/xMa2aXEPIlZn4bL6wJZ1GNw== X-ME-Sender: X-ME-Proxy: Received: from localhost (flamenco.cs.columbia.edu [128.59.20.216]) by mail.messagingengine.com (Postfix) with ESMTPA id 959BBE47CB; Thu, 25 Oct 2018 13:21:10 -0400 (EDT) From: "Emilio G. Cota" To: qemu-devel@nongnu.org Date: Thu, 25 Oct 2018 13:20:26 -0400 Message-Id: <20181025172057.20414-18-cota@braap.org> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20181025172057.20414-1-cota@braap.org> References: <20181025172057.20414-1-cota@braap.org> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 66.111.4.27 Subject: [Qemu-devel] [RFC 17/48] plugin-gen: add TCG code generation helpers X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Peter Maydell , =?utf-8?q?Alex_Benn=C3=A9e?= , =?utf-8?q?Llu=C3=ADs?= =?utf-8?q?_Vilanova?= , Pavel Dovgalyuk , Stefan Hajnoczi Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Here we implement several features: - Inlining TCG code for simple operations. Crucially, we do not export TCG ops to plugins. Instead, we give them a C API to insert inlined ops. So far we only support adding an immediate to a u64, e.g. to count events. - "Direct" callbacks. These are callbacks that do not go via a helper. Instead, the helper is defined at run-time, so that the plugin code is directly called from TCG. This makes direct callbacks as efficient as possible; they are therefore used for very frequent events, e.g. memory callbacks. - Passing the host address to memory callbacks. Most of this is implemented in a later patch though. - Instrumentation of memory accesses performed from helpers. See comment. Signed-off-by: Emilio G. Cota --- include/exec/plugin-gen.h | 51 +++++++++ accel/tcg/plugin-gen.c | 230 ++++++++++++++++++++++++++++++++++++++ accel/tcg/Makefile.objs | 1 + 3 files changed, 282 insertions(+) create mode 100644 include/exec/plugin-gen.h create mode 100644 accel/tcg/plugin-gen.c diff --git a/include/exec/plugin-gen.h b/include/exec/plugin-gen.h new file mode 100644 index 0000000000..46a167527e --- /dev/null +++ b/include/exec/plugin-gen.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * plugin-gen.h - TCG-dependent definitions for generating plugin code + * + * This header should be included only from plugin.c and C files that emit + * TCG code. + */ +#ifndef QEMU_PLUGIN_GEN_H +#define QEMU_PLUGIN_GEN_H + +#include "qemu/plugin.h" +#include "tcg/tcg.h" + +#ifdef CONFIG_PLUGINS + +void qemu_plugin_gen_vcpu_mem_callbacks(struct qemu_plugin_dyn_cb_arr *arr, + TCGv vaddr, uint8_t info); + +void qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr); + +void qemu_plugin_gen_disable_mem_helpers(void); + +void +qemu_plugin_gen_enable_mem_helpers(const struct qemu_plugin_dyn_cb_arr *orig); + +#else /* !CONFIG_PLUGINS */ + +static inline void +qemu_plugin_gen_vcpu_mem_callbacks(struct qemu_plugin_dyn_cb_arr *arr, + TCGv vaddr, uint8_t info) +{ } + +static inline void +qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr) +{ } + +static inline void qemu_plugin_gen_disable_mem_helpers(void) +{ } + +static inline void +qemu_plugin_gen_enable_mem_helpers(const struct qemu_plugin_dyn_cb_arr *orig) +{ } + +#endif /* CONFIG_PLUGINS */ + +#endif /* QEMU_PLUGIN_GEN_H */ + diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c new file mode 100644 index 0000000000..75f182be37 --- /dev/null +++ b/accel/tcg/plugin-gen.c @@ -0,0 +1,230 @@ +#include "qemu/osdep.h" +#include "cpu.h" +#include "tcg/tcg.h" +#include "tcg/tcg-op.h" +#include "exec/exec-all.h" +#include "exec/plugin-gen.h" + +static void gen_inline_op(struct qemu_plugin_dyn_cb *cb) +{ + TCGv_i64 val = tcg_temp_new_i64(); + TCGv_ptr ptr = tcg_const_ptr(cb->userp); + + tcg_gen_ld_i64(val, ptr, 0); + switch (cb->inline_insn.op) { + case QEMU_PLUGIN_INLINE_ADD_U64: + tcg_gen_addi_i64(val, val, cb->inline_insn.imm); + break; + default: + g_assert_not_reached(); + } + tcg_gen_st_i64(val, ptr, 0); + + tcg_temp_free_ptr(ptr); + tcg_temp_free_i64(val); +} + +static void gen_helper_mem_cb(const char *name, unsigned flags, + qemu_plugin_vcpu_mem_cb_t cb, TCGv_i32 cpu_index, + TCGv_i32 meminfo, TCGv_i64 vaddr, TCGv_ptr udata) +{ + TCGHelperInfo info = { + .func = cb, + .name = name, + .flags = flags, + .sizemask = dh_sizemask(void, 0) | + dh_sizemask(i32, 1) | + dh_sizemask(i32, 2) | + dh_sizemask(i64, 3) | + dh_sizemask(ptr, 4), + + }; + TCGTemp *args[] = { + tcgv_i32_temp(cpu_index), + tcgv_i32_temp(meminfo), + tcgv_i64_temp(vaddr), + tcgv_ptr_temp(udata), + }; + + tcg_gen_runtime_helper(&info, NULL, ARRAY_SIZE(args), args); +} + +static void gen_helper_mem_haddr_cb(const char *name, unsigned flags, + qemu_plugin_vcpu_mem_haddr_cb_t cb, + TCGv_i32 cpu_index, TCGv_i32 meminfo, + TCGv_i64 vaddr, TCGv_ptr haddr, + TCGv_ptr udata) +{ + TCGHelperInfo info = { + .func = cb, + .name = name, + .flags = flags, + .sizemask = dh_sizemask(void, 0) | + dh_sizemask(i32, 1) | + dh_sizemask(i32, 2) | + dh_sizemask(i64, 3) | + dh_sizemask(ptr, 4) | + dh_sizemask(ptr, 5), + + }; + TCGTemp *args[] = { + tcgv_i32_temp(cpu_index), + tcgv_i32_temp(meminfo), + tcgv_i64_temp(vaddr), + tcgv_ptr_temp(haddr), + tcgv_ptr_temp(udata), + }; + + tcg_gen_runtime_helper(&info, NULL, ARRAY_SIZE(args), args); +} + +static void gen_mem_cb(struct qemu_plugin_dyn_cb *cb, TCGv vaddr, uint8_t info) +{ + TCGv_i32 cpu_index = tcg_temp_new_i32(); + TCGv_i32 meminfo = tcg_const_i32(info); + TCGv_i64 vaddr64 = tcg_temp_new_i64(); + TCGv_ptr udata = tcg_const_ptr(cb->userp); + TCGv_ptr haddr; + + tcg_gen_ld_i32(cpu_index, cpu_env, + -ENV_OFFSET + offsetof(CPUState, cpu_index)); + tcg_gen_extu_tl_i64(vaddr64, vaddr); + + if (cb->mem.haddr) { +#ifdef CONFIG_SOFTMMU + haddr = tcg_temp_new_ptr(); + tcg_gen_ld_ptr(haddr, cpu_env, offsetof(CPUArchState, hostaddr)); +#else + haddr = tcg_const_ptr(NULL); +#endif + gen_helper_mem_haddr_cb("helper_plugin_vcpu_mem_haddr_cb", + cb->tcg_flags, cb->f.vcpu_mem_haddr, + cpu_index, meminfo, vaddr64, haddr, udata); + tcg_temp_free_ptr(haddr); + } else { + gen_helper_mem_cb("helper_plugin_vcpu_mem_cb", cb->tcg_flags, + cb->f.vcpu_mem, cpu_index, meminfo, vaddr64, + udata); + } + + tcg_temp_free_ptr(udata); + tcg_temp_free_i64(vaddr64); + tcg_temp_free_i32(meminfo); + tcg_temp_free_i32(cpu_index); +} + +void qemu_plugin_gen_vcpu_mem_callbacks(struct qemu_plugin_dyn_cb_arr *arr, + TCGv vaddr, uint8_t info) +{ + size_t i; + + for (i = 0; i < arr->n; i++) { + struct qemu_plugin_dyn_cb *cb = &arr->data[i]; + + switch (cb->type) { + case QEMU_PLUGIN_DYN_CB_TYPE_REGULAR: + gen_mem_cb(cb, vaddr, info); + break; + case QEMU_PLUGIN_DYN_CB_TYPE_INLINE: + gen_inline_op(cb); + break; + default: + g_assert_not_reached(); + } + } +} + +static void gen_helper_vcpu_udata_cb(const char *name, unsigned flags, + qemu_plugin_vcpu_udata_cb_t cb, + TCGv_i32 cpu_index, TCGv_ptr udata) +{ + TCGHelperInfo info = { + .func = cb, + .name = name, + .flags = flags, + .sizemask = dh_sizemask(void, 0) | + dh_sizemask(i32, 1) | + dh_sizemask(ptr, 2), + }; + TCGTemp *args[] = { + tcgv_i32_temp(cpu_index), + tcgv_ptr_temp(udata), + }; + + tcg_gen_runtime_helper(&info, NULL, ARRAY_SIZE(args), args); +} + +static void gen_vcpu_udata_cb(struct qemu_plugin_dyn_cb *cb) +{ + TCGv_i32 cpu_index = tcg_temp_new_i32(); + TCGv_ptr udata = tcg_const_ptr(cb->userp); + + tcg_gen_ld_i32(cpu_index, cpu_env, + -ENV_OFFSET + offsetof(CPUState, cpu_index)); + + gen_helper_vcpu_udata_cb("helper_plugin_vcpu_udata_cb", cb->tcg_flags, + cb->f.vcpu_udata, cpu_index, udata); + + tcg_temp_free_ptr(udata); + tcg_temp_free_i32(cpu_index); +} + +void qemu_plugin_gen_vcpu_udata_callbacks(struct qemu_plugin_dyn_cb_arr *arr) +{ + size_t i; + + for (i = 0; i < arr->n; i++) { + struct qemu_plugin_dyn_cb *cb = &arr->data[i]; + + switch (cb->type) { + case QEMU_PLUGIN_DYN_CB_TYPE_REGULAR: + gen_vcpu_udata_cb(cb); + break; + case QEMU_PLUGIN_DYN_CB_TYPE_INLINE: + gen_inline_op(cb); + break; + default: + g_assert_not_reached(); + } + } +} + +/* + * Tracking memory accesses performed from helpers requires extra work. + * If an instruction is emulated with helpers, struct qemu_plugin_insn's + * .calls_helpers is set. If so, this function is called. Here we do two + * things: (1) copy the CB descriptor, and keep track of it so that it can be + * freed later on, and (2) point CPUState.plugin_mem_cbs to the descriptor, so + * that we can read it at run-time (i.e. when the helper executes). + * This run-time access is performed from qemu_plugin_vcpu_mem_cb. + * + * Note that qemu_plugin_gen_disable_mem_helpers undoes (2). + */ +void +qemu_plugin_gen_enable_mem_helpers(const struct qemu_plugin_dyn_cb_arr *orig) +{ + struct qemu_plugin_dyn_cb_arr *arr; + TCGv_ptr ptr; + + arr = g_new(struct qemu_plugin_dyn_cb_arr, 1); + arr->capacity = orig->n; + arr->n = orig->n; + arr->data = g_new(struct qemu_plugin_dyn_cb, arr->n); + memcpy(arr->data, orig->data, sizeof(*arr->data) * arr->n); + qemu_plugin_add_dyn_cb_arr(arr); + + ptr = tcg_const_ptr(arr); + tcg_gen_st_ptr(ptr, cpu_env, -ENV_OFFSET + offsetof(CPUState, + plugin_mem_cbs)); + tcg_temp_free_ptr(ptr); +} + +/* Called once we're done instrumenting an instruction that calls helpers */ +void qemu_plugin_gen_disable_mem_helpers(void) +{ + TCGv_ptr ptr = tcg_const_ptr(NULL); + + tcg_gen_st_ptr(ptr, cpu_env, -ENV_OFFSET + offsetof(CPUState, + plugin_mem_cbs)); + tcg_temp_free_ptr(ptr); +} diff --git a/accel/tcg/Makefile.objs b/accel/tcg/Makefile.objs index d381a02f34..4f617e4fe5 100644 --- a/accel/tcg/Makefile.objs +++ b/accel/tcg/Makefile.objs @@ -6,3 +6,4 @@ obj-y += translator.o obj-$(CONFIG_USER_ONLY) += user-exec.o obj-$(call lnot,$(CONFIG_SOFTMMU)) += user-exec-stub.o +obj-$(CONFIG_PLUGINS) += plugin-gen.o