From patchwork Wed Nov 25 09:34:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930925 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BD68AC5519F for ; Wed, 25 Nov 2020 09:43:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 70BFE2083E for ; Wed, 25 Nov 2020 09:43:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727794AbgKYJl4 (ORCPT ); Wed, 25 Nov 2020 04:41:56 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57142 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727403AbgKYJlz (ORCPT ); Wed, 25 Nov 2020 04:41:55 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 5F8FC30462CD; Wed, 25 Nov 2020 11:35:43 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 43FF93072785; Wed, 25 Nov 2020 11:35:43 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 01/81] KVM: UAPI: add error codes used by the VM introspection code Date: Wed, 25 Nov 2020 11:34:40 +0200 Message-Id: <20201125093600.2766-2-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org These new error codes help the introspection tool to identify the cause of the introspection command failure and to recover from some error cases or to give more information to the user. Signed-off-by: Adalbert Lazăr --- include/uapi/linux/kvm_para.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index 8b86609849b9..3ce388249682 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h @@ -17,6 +17,10 @@ #define KVM_E2BIG E2BIG #define KVM_EPERM EPERM #define KVM_EOPNOTSUPP 95 +#define KVM_EAGAIN 11 +#define KVM_ENOENT ENOENT +#define KVM_ENOMEM ENOMEM +#define KVM_EBUSY EBUSY #define KVM_HC_VAPIC_POLL_IRQ 1 #define KVM_HC_MMU_OP 2 From patchwork Wed Nov 25 09:34:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930823 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4C744C64E8A for ; Wed, 25 Nov 2020 09:42:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 19FA32083E for ; Wed, 25 Nov 2020 09:42:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728587AbgKYJmG (ORCPT ); Wed, 25 Nov 2020 04:42:06 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57188 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728360AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 7E82E305D4F3; Wed, 25 Nov 2020 11:35:43 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 63FA53072784; Wed, 25 Nov 2020 11:35:43 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 02/81] KVM: add kvm_vcpu_kick_and_wait() Date: Wed, 25 Nov 2020 11:34:41 +0200 Message-Id: <20201125093600.2766-3-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This function is needed for the KVMI_VM_PAUSE_VCPU command, which sets the introspection request flag, kicks the vCPU out of guest and returns a success error code (0). The vCPU will send the KVMI_VCPU_EVENT_PAUSE event as soon as possible. Once the introspection tool receives the event, it knows that the vCPU doesn't run guest code and can handle introspection commands (until the reply for the pause event is sent). To implement the "pause VM" command, the introspection tool will send a KVMI_VM_PAUSE_VCPU command for every vCPU. To know when the VM is paused, userspace has to receive and "parse" all events. For example, with a 4 vCPU VM, if "pause VM" was sent by userspace while handling an event from vCPU0 and at the same time a new vCPU was hot-plugged (which could send another event for vCPU4), the "pause VM" command has to receive and check all events until it gets the pause events for vCPU1, vCPU2 and vCPU3 before returning to the upper layer. In order to make it easier for userspace to implement the "pause VM" command, KVMI_VM_PAUSE_VCPU has an optional 'wait' parameter. If this is set, kvm_vcpu_kick_and_wait() will be used instead of kvm_vcpu_kick(). Once a sequence of KVMI_VM_PAUSE_VCPU commands with the 'wait' flag set is handled, the introspection tool can consider the VM paused, without the need to wait and check events. Signed-off-by: Adalbert Lazăr --- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index f3b1013fb22c..1bbb07b87d1a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -841,6 +841,7 @@ void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu); void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu); bool kvm_vcpu_wake_up(struct kvm_vcpu *vcpu); void kvm_vcpu_kick(struct kvm_vcpu *vcpu); +void kvm_vcpu_kick_and_wait(struct kvm_vcpu *vcpu); int kvm_vcpu_yield_to(struct kvm_vcpu *target); void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu, bool usermode_vcpu_not_eligible); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 3abcb2ce5b7d..069668b8afc2 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2887,6 +2887,16 @@ void kvm_vcpu_kick(struct kvm_vcpu *vcpu) EXPORT_SYMBOL_GPL(kvm_vcpu_kick); #endif /* !CONFIG_S390 */ +void kvm_vcpu_kick_and_wait(struct kvm_vcpu *vcpu) +{ + if (kvm_vcpu_wake_up(vcpu)) + return; + + if (kvm_request_needs_ipi(vcpu, KVM_REQUEST_WAIT)) + smp_call_function_single(vcpu->cpu, ack_flush, NULL, 1); +} +EXPORT_SYMBOL_GPL(kvm_vcpu_kick_and_wait); + int kvm_vcpu_yield_to(struct kvm_vcpu *target) { struct pid *pid; From patchwork Wed Nov 25 09:34:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930885 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7DEACC83019 for ; Wed, 25 Nov 2020 09:42:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 483C220B1F for ; Wed, 25 Nov 2020 09:42:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728733AbgKYJmn (ORCPT ); Wed, 25 Nov 2020 04:42:43 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57312 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728461AbgKYJmD (ORCPT ); Wed, 25 Nov 2020 04:42:03 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id A1324305D4F4; Wed, 25 Nov 2020 11:35:43 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 81DD83072785; Wed, 25 Nov 2020 11:35:43 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?=C8=98tefan_=C8=98icleru?= , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 03/81] KVM: add kvm_get_max_gfn() Date: Wed, 25 Nov 2020 11:34:42 +0200 Message-Id: <20201125093600.2766-4-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Ștefan Șicleru This function is needed for the KVMI_VM_GET_MAX_GFN command. Signed-off-by: Ștefan Șicleru Signed-off-by: Adalbert Lazăr --- include/linux/kvm_host.h | 1 + virt/kvm/kvm_main.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 1bbb07b87d1a..cd6ac3a43c9a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -807,6 +807,7 @@ bool kvm_vcpu_is_visible_gfn(struct kvm_vcpu *vcpu, gfn_t gfn); unsigned long kvm_host_page_size(struct kvm_vcpu *vcpu, gfn_t gfn); void mark_page_dirty_in_slot(struct kvm *kvm, struct kvm_memory_slot *memslot, gfn_t gfn); void mark_page_dirty(struct kvm *kvm, gfn_t gfn); +gfn_t kvm_get_max_gfn(struct kvm *kvm); struct kvm_memslots *kvm_vcpu_memslots(struct kvm_vcpu *vcpu); struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfn); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 069668b8afc2..e19dd6f92709 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1410,6 +1410,31 @@ static int kvm_vm_ioctl_set_memory_region(struct kvm *kvm, return kvm_set_memory_region(kvm, mem); } +gfn_t kvm_get_max_gfn(struct kvm *kvm) +{ + u32 skip_mask = KVM_MEM_READONLY | KVM_MEMSLOT_INVALID; + struct kvm_memory_slot *memslot; + struct kvm_memslots *slots; + gfn_t max_gfn = 0; + int idx; + + idx = srcu_read_lock(&kvm->srcu); + spin_lock(&kvm->mmu_lock); + + slots = kvm_memslots(kvm); + kvm_for_each_memslot(memslot, slots) + if (memslot->id < KVM_USER_MEM_SLOTS && + (memslot->flags & skip_mask) == 0 && + memslot->npages) + max_gfn = max(max_gfn, memslot->base_gfn + + memslot->npages); + + spin_unlock(&kvm->mmu_lock); + srcu_read_unlock(&kvm->srcu, idx); + + return max_gfn; +} + #ifndef CONFIG_KVM_GENERIC_DIRTYLOG_READ_PROTECT /** * kvm_get_dirty_log - get a snapshot of dirty pages From patchwork Wed Nov 25 09:34:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930869 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 044C2C71155 for ; Wed, 25 Nov 2020 09:42:29 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B16C92083E for ; Wed, 25 Nov 2020 09:42:28 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728729AbgKYJm1 (ORCPT ); Wed, 25 Nov 2020 04:42:27 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57142 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728552AbgKYJmG (ORCPT ); Wed, 25 Nov 2020 04:42:06 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id BFD94305D4F5; Wed, 25 Nov 2020 11:35:43 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id A52413072784; Wed, 25 Nov 2020 11:35:43 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 04/81] KVM: doc: fix the hypercalls numbering Date: Wed, 25 Nov 2020 11:34:43 +0200 Message-Id: <20201125093600.2766-5-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org The next hypercalls will be correctly numbered. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/hypercalls.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/hypercalls.rst b/Documentation/virt/kvm/hypercalls.rst index ed4fddd364ea..70e77c66b64c 100644 --- a/Documentation/virt/kvm/hypercalls.rst +++ b/Documentation/virt/kvm/hypercalls.rst @@ -137,7 +137,7 @@ compute the CLOCK_REALTIME for its clock, at the same instant. Returns KVM_EOPNOTSUPP if the host does not use TSC clocksource, or if clock type is different than KVM_CLOCK_PAIRING_WALLCLOCK. -6. KVM_HC_SEND_IPI +7. KVM_HC_SEND_IPI ------------------ :Architecture: x86 @@ -158,7 +158,7 @@ corresponds to the APIC ID a2+1, and so on. Returns the number of CPUs to which the IPIs were delivered successfully. -7. KVM_HC_SCHED_YIELD +8. KVM_HC_SCHED_YIELD --------------------- :Architecture: x86 From patchwork Wed Nov 25 09:34:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930795 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C6F2EC63777 for ; Wed, 25 Nov 2020 09:41:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 67AA7206F9 for ; Wed, 25 Nov 2020 09:41:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727288AbgKYJlx (ORCPT ); Wed, 25 Nov 2020 04:41:53 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57010 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726039AbgKYJlw (ORCPT ); Wed, 25 Nov 2020 04:41:52 -0500 X-Greylist: delayed 366 seconds by postgrey-1.27 at vger.kernel.org; Wed, 25 Nov 2020 04:41:50 EST Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E3FDE305D4F6; Wed, 25 Nov 2020 11:35:43 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C30163072785; Wed, 25 Nov 2020 11:35:43 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 05/81] KVM: x86: add kvm_arch_vcpu_get_regs() and kvm_arch_vcpu_get_sregs() Date: Wed, 25 Nov 2020 11:34:44 +0200 Message-Id: <20201125093600.2766-6-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu These functions are used by the VM introspection code (for the KVMI_VCPU_GET_REGISTERS command and all events sending the vCPU registers to the introspection tool). Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/x86.c | 10 ++++++++++ include/linux/kvm_host.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index a3fdc16cfd6f..540e42341435 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9375,6 +9375,11 @@ int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) return 0; } +void kvm_arch_vcpu_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + __get_regs(vcpu, regs); +} + static void __set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) { vcpu->arch.emulate_regs_need_sync_from_vcpu = true; @@ -9470,6 +9475,11 @@ int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, return 0; } +void kvm_arch_vcpu_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ + __get_sregs(vcpu, sregs); +} + int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, struct kvm_mp_state *mp_state) { diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index cd6ac3a43c9a..13c6b806477b 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -902,9 +902,12 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, struct kvm_translation *tr); int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); +void kvm_arch_vcpu_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); +void kvm_arch_vcpu_get_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs); int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, From patchwork Wed Nov 25 09:34:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930827 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 98DCDC64E90 for ; Wed, 25 Nov 2020 09:42:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 523672083E for ; Wed, 25 Nov 2020 09:42:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728614AbgKYJmI (ORCPT ); Wed, 25 Nov 2020 04:42:08 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57134 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728539AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 15C9C305D4F7; Wed, 25 Nov 2020 11:35:44 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id E88B63072784; Wed, 25 Nov 2020 11:35:43 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 06/81] KVM: x86: add kvm_arch_vcpu_set_regs() Date: Wed, 25 Nov 2020 11:34:45 +0200 Message-Id: <20201125093600.2766-7-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This is needed for the KVMI_VCPU_SET_REGISTERS command, which allows an introspection tool to override the kvm_regs structure for a specific vCPU without clearing the pending exception. In most cases this is used to increment the program counter. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/x86.c | 21 ++++++++++++++------- include/linux/kvm_host.h | 2 ++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 540e42341435..5951458408fb 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9406,16 +9406,23 @@ static void __set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) kvm_rip_write(vcpu, regs->rip); kvm_set_rflags(vcpu, regs->rflags | X86_EFLAGS_FIXED); - - vcpu->arch.exception.pending = false; - - kvm_make_request(KVM_REQ_EVENT, vcpu); } -int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +void kvm_arch_vcpu_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs, + bool clear_exception) { - vcpu_load(vcpu); __set_regs(vcpu, regs); + + if (clear_exception) + vcpu->arch.exception.pending = false; + + kvm_make_request(KVM_REQ_EVENT, vcpu); +} + +int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + vcpu_load(vcpu); + kvm_arch_vcpu_set_regs(vcpu, regs, true); vcpu_put(vcpu); return 0; } @@ -9816,7 +9823,7 @@ static int sync_regs(struct kvm_vcpu *vcpu) return -EINVAL; if (vcpu->run->kvm_dirty_regs & KVM_SYNC_X86_REGS) { - __set_regs(vcpu, &vcpu->run->s.regs.regs); + kvm_arch_vcpu_set_regs(vcpu, &vcpu->run->s.regs.regs, true); vcpu->run->kvm_dirty_regs &= ~KVM_SYNC_X86_REGS; } if (vcpu->run->kvm_dirty_regs & KVM_SYNC_X86_SREGS) { diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 13c6b806477b..6d622d8bd339 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -904,6 +904,8 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, int kvm_arch_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); void kvm_arch_vcpu_get_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); int kvm_arch_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs); +void kvm_arch_vcpu_set_regs(struct kvm_vcpu *vcpu, struct kvm_regs *regs, + bool clear_exception); int kvm_arch_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); void kvm_arch_vcpu_get_sregs(struct kvm_vcpu *vcpu, From patchwork Wed Nov 25 09:34:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930915 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4B9F2C8300F for ; Wed, 25 Nov 2020 09:43:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E82732083E for ; Wed, 25 Nov 2020 09:43:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728832AbgKYJnE (ORCPT ); Wed, 25 Nov 2020 04:43:04 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57100 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726620AbgKYJl5 (ORCPT ); Wed, 25 Nov 2020 04:41:57 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 3720A305D4F8; Wed, 25 Nov 2020 11:35:44 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 197EA3072785; Wed, 25 Nov 2020 11:35:44 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 07/81] KVM: x86: avoid injecting #PF when emulate the VMCALL instruction Date: Wed, 25 Nov 2020 11:34:46 +0200 Message-Id: <20201125093600.2766-8-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu It can happened to end up emulating the VMCALL instruction as a result of the handling of an EPT write fault. In this situation, the emulator will try to unconditionally patch the correct hypercall opcode bytes using emulator_write_emulated(). However, this last call uses the fault GPA (if available) or walks the guest page tables at RIP, otherwise. The trouble begins when using VM introspection, when we forbid the use of the fault GPA and fallback to the guest pt walk: in Windows (8.1 and newer) the page that we try to write into is marked read-execute and as such emulator_write_emulated() fails and we inject a write #PF, leading to a guest crash. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/x86.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 5951458408fb..816801d6c95d 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -8144,11 +8144,15 @@ static int emulator_fix_hypercall(struct x86_emulate_ctxt *ctxt) struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); char instruction[3]; unsigned long rip = kvm_rip_read(vcpu); + int err; kvm_x86_ops.patch_hypercall(vcpu, instruction); - return emulator_write_emulated(ctxt, rip, instruction, 3, + err = emulator_write_emulated(ctxt, rip, instruction, 3, &ctxt->exception); + if (err == X86EMUL_PROPAGATE_FAULT) + err = X86EMUL_CONTINUE; + return err; } static int dm_request_for_irq_injection(struct kvm_vcpu *vcpu) From patchwork Wed Nov 25 09:34:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930801 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 20A05C5519F for ; Wed, 25 Nov 2020 09:41:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C92EA2083E for ; Wed, 25 Nov 2020 09:41:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728003AbgKYJl4 (ORCPT ); Wed, 25 Nov 2020 04:41:56 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57144 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727455AbgKYJl4 (ORCPT ); Wed, 25 Nov 2020 04:41:56 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 5DBC5305D4F9; Wed, 25 Nov 2020 11:35:44 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 3A6183072784; Wed, 25 Nov 2020 11:35:44 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 08/81] KVM: x86: add kvm_x86_ops.bp_intercepted() Date: Wed, 25 Nov 2020 11:34:47 +0200 Message-Id: <20201125093600.2766-9-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu Both, the introspection tool and the device manager can request #BP interception. This function will be used to check if this interception is already enabled by either side. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/svm.c | 8 ++++++++ arch/x86/kvm/svm/svm.h | 7 +++++++ arch/x86/kvm/vmx/vmx.c | 6 ++++++ 4 files changed, 22 insertions(+) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index f002cdb13a0b..e46fee59d4ed 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1104,6 +1104,7 @@ struct kvm_x86_ops { void (*vcpu_load)(struct kvm_vcpu *vcpu, int cpu); void (*vcpu_put)(struct kvm_vcpu *vcpu); + bool (*bp_intercepted)(struct kvm_vcpu *vcpu); void (*update_exception_bitmap)(struct kvm_vcpu *vcpu); int (*get_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr); int (*set_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 6dc337b9c231..95c7072cde8e 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1738,6 +1738,13 @@ static void svm_set_segment(struct kvm_vcpu *vcpu, vmcb_mark_dirty(svm->vmcb, VMCB_SEG); } +static bool svm_bp_intercepted(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *svm = to_svm(vcpu); + + return get_exception_intercept(svm, BP_VECTOR); +} + static void update_exception_bitmap(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -4213,6 +4220,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .vcpu_blocking = svm_vcpu_blocking, .vcpu_unblocking = svm_vcpu_unblocking, + .bp_intercepted = svm_bp_intercepted, .update_exception_bitmap = update_exception_bitmap, .get_msr_feature = svm_get_msr_feature, .get_msr = svm_get_msr, diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index fdff76eb6ceb..dca2dfe2e30d 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -294,6 +294,13 @@ static inline void clr_exception_intercept(struct vcpu_svm *svm, u32 bit) recalc_intercepts(svm); } +static inline bool get_exception_intercept(struct vcpu_svm *svm, int bit) +{ + struct vmcb *vmcb = get_host_vmcb(svm); + + return vmcb_is_intercept(&vmcb->control, INTERCEPT_EXCEPTION_OFFSET + bit); +} + static inline void svm_set_intercept(struct vcpu_svm *svm, int bit) { struct vmcb *vmcb = get_host_vmcb(svm); diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index c3441e7e5a87..93a97aa3d847 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -807,6 +807,11 @@ static u32 vmx_read_guest_seg_ar(struct vcpu_vmx *vmx, unsigned seg) return *p; } +static bool vmx_bp_intercepted(struct kvm_vcpu *vcpu) +{ + return (vmcs_read32(EXCEPTION_BITMAP) & (1u << BP_VECTOR)); +} + void update_exception_bitmap(struct kvm_vcpu *vcpu) { u32 eb; @@ -7611,6 +7616,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .vcpu_load = vmx_vcpu_load, .vcpu_put = vmx_vcpu_put, + .bp_intercepted = vmx_bp_intercepted, .update_exception_bitmap = update_exception_bitmap, .get_msr_feature = vmx_get_msr_feature, .get_msr = vmx_get_msr, From patchwork Wed Nov 25 09:34:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930877 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EB0A5C8301C for ; Wed, 25 Nov 2020 09:42:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B7C762083E for ; Wed, 25 Nov 2020 09:42:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728546AbgKYJmE (ORCPT ); Wed, 25 Nov 2020 04:42:04 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57136 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727455AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 7DDE3305D4FA; Wed, 25 Nov 2020 11:35:44 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 5F7893072785; Wed, 25 Nov 2020 11:35:44 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= , =?utf-8?b?Tmlj?= =?utf-8?b?dciZb3IgQ8OuyJt1?= Subject: [PATCH v10 09/81] KVM: x86: add kvm_x86_ops.control_cr3_intercept() Date: Wed, 25 Nov 2020 11:34:48 +0200 Message-Id: <20201125093600.2766-10-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This function is needed for the KVMI_VCPU_CONTROL_CR command, when the introspection tool has to intercept the read/write access to CR3. Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 6 ++++++ arch/x86/kvm/svm/svm.c | 14 ++++++++++++++ arch/x86/kvm/vmx/vmx.c | 26 ++++++++++++++++++++------ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index e46fee59d4ed..0eeb1d829a1d 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -137,6 +137,10 @@ static inline gfn_t gfn_to_index(gfn_t gfn, gfn_t base_gfn, int level) #define KVM_NR_FIXED_MTRR_REGION 88 #define KVM_NR_VAR_MTRR 8 +#define CR_TYPE_R 1 +#define CR_TYPE_W 2 +#define CR_TYPE_RW 3 + #define ASYNC_PF_PER_VCPU 64 enum kvm_reg { @@ -1118,6 +1122,8 @@ struct kvm_x86_ops { void (*set_cr0)(struct kvm_vcpu *vcpu, unsigned long cr0); bool (*is_valid_cr4)(struct kvm_vcpu *vcpu, unsigned long cr0); void (*set_cr4)(struct kvm_vcpu *vcpu, unsigned long cr4); + void (*control_cr3_intercept)(struct kvm_vcpu *vcpu, int type, + bool enable); int (*set_efer)(struct kvm_vcpu *vcpu, u64 efer); void (*get_idt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); void (*set_idt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 95c7072cde8e..4f28fa035048 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1707,6 +1707,19 @@ void svm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) kvm_update_cpuid_runtime(vcpu); } +static void svm_control_cr3_intercept(struct kvm_vcpu *vcpu, int type, + bool enable) +{ + struct vcpu_svm *svm = to_svm(vcpu); + + if (type & CR_TYPE_R) + enable ? svm_set_intercept(svm, INTERCEPT_CR3_READ) : + svm_clr_intercept(svm, INTERCEPT_CR3_READ); + if (type & CR_TYPE_W) + enable ? svm_set_intercept(svm, INTERCEPT_CR3_WRITE) : + svm_clr_intercept(svm, INTERCEPT_CR3_WRITE); +} + static void svm_set_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg) { @@ -4233,6 +4246,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .set_cr0 = svm_set_cr0, .is_valid_cr4 = svm_is_valid_cr4, .set_cr4 = svm_set_cr4, + .control_cr3_intercept = svm_control_cr3_intercept, .set_efer = svm_set_efer, .get_idt = svm_get_idt, .set_idt = svm_set_idt, diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 93a97aa3d847..c5a53642d1c0 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -2978,24 +2978,37 @@ void ept_save_pdptrs(struct kvm_vcpu *vcpu) kvm_register_mark_dirty(vcpu, VCPU_EXREG_PDPTR); } +static void vmx_control_cr3_intercept(struct kvm_vcpu *vcpu, int type, + bool enable) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + u32 cr3_exec_control = 0; + + if (type & CR_TYPE_R) + cr3_exec_control |= CPU_BASED_CR3_STORE_EXITING; + if (type & CR_TYPE_W) + cr3_exec_control |= CPU_BASED_CR3_LOAD_EXITING; + + if (enable) + exec_controls_setbit(vmx, cr3_exec_control); + else + exec_controls_clearbit(vmx, cr3_exec_control); +} + static void ept_update_paging_mode_cr0(unsigned long *hw_cr0, unsigned long cr0, struct kvm_vcpu *vcpu) { - struct vcpu_vmx *vmx = to_vmx(vcpu); - if (!kvm_register_is_available(vcpu, VCPU_EXREG_CR3)) vmx_cache_reg(vcpu, VCPU_EXREG_CR3); if (!(cr0 & X86_CR0_PG)) { /* From paging/starting to nonpaging */ - exec_controls_setbit(vmx, CPU_BASED_CR3_LOAD_EXITING | - CPU_BASED_CR3_STORE_EXITING); + vmx_control_cr3_intercept(vcpu, CR_TYPE_RW, true); vcpu->arch.cr0 = cr0; vmx_set_cr4(vcpu, kvm_read_cr4(vcpu)); } else if (!is_paging(vcpu)) { /* From nonpaging to paging */ - exec_controls_clearbit(vmx, CPU_BASED_CR3_LOAD_EXITING | - CPU_BASED_CR3_STORE_EXITING); + vmx_control_cr3_intercept(vcpu, CR_TYPE_RW, false); vcpu->arch.cr0 = cr0; vmx_set_cr4(vcpu, kvm_read_cr4(vcpu)); } @@ -7629,6 +7642,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .set_cr0 = vmx_set_cr0, .is_valid_cr4 = vmx_is_valid_cr4, .set_cr4 = vmx_set_cr4, + .control_cr3_intercept = vmx_control_cr3_intercept, .set_efer = vmx_set_efer, .get_idt = vmx_get_idt, .set_idt = vmx_set_idt, From patchwork Wed Nov 25 09:34:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930875 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 17C17C8301B for ; Wed, 25 Nov 2020 09:42:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BF5092145D for ; Wed, 25 Nov 2020 09:42:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728739AbgKYJmj (ORCPT ); Wed, 25 Nov 2020 04:42:39 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57194 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728486AbgKYJmE (ORCPT ); Wed, 25 Nov 2020 04:42:04 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id A055C305D4FB; Wed, 25 Nov 2020 11:35:44 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 8076C3072784; Wed, 25 Nov 2020 11:35:44 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 10/81] KVM: x86: add kvm_x86_ops.cr3_write_intercepted() Date: Wed, 25 Nov 2020 11:34:49 +0200 Message-Id: <20201125093600.2766-11-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This function will be used to allow the introspection tool to disable the CR3-write interception when it is no longer interested in these events, but only if nothing else depends on these VM-exits. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/svm.c | 8 ++++++++ arch/x86/kvm/vmx/vmx.c | 8 ++++++++ 3 files changed, 17 insertions(+) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 0eeb1d829a1d..a402384a9326 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1124,6 +1124,7 @@ struct kvm_x86_ops { void (*set_cr4)(struct kvm_vcpu *vcpu, unsigned long cr4); void (*control_cr3_intercept)(struct kvm_vcpu *vcpu, int type, bool enable); + bool (*cr3_write_intercepted)(struct kvm_vcpu *vcpu); int (*set_efer)(struct kvm_vcpu *vcpu, u64 efer); void (*get_idt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); void (*set_idt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 4f28fa035048..5000ee25545b 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1720,6 +1720,13 @@ static void svm_control_cr3_intercept(struct kvm_vcpu *vcpu, int type, svm_clr_intercept(svm, INTERCEPT_CR3_WRITE); } +static bool svm_cr3_write_intercepted(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *svm = to_svm(vcpu); + + return svm_is_intercept(svm, INTERCEPT_CR3_WRITE); +} + static void svm_set_segment(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg) { @@ -4247,6 +4254,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .is_valid_cr4 = svm_is_valid_cr4, .set_cr4 = svm_set_cr4, .control_cr3_intercept = svm_control_cr3_intercept, + .cr3_write_intercepted = svm_cr3_write_intercepted, .set_efer = svm_set_efer, .get_idt = svm_get_idt, .set_idt = svm_set_idt, diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index c5a53642d1c0..7b2a60cd7a76 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -2995,6 +2995,13 @@ static void vmx_control_cr3_intercept(struct kvm_vcpu *vcpu, int type, exec_controls_clearbit(vmx, cr3_exec_control); } +static bool vmx_cr3_write_intercepted(struct kvm_vcpu *vcpu) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + + return !!(exec_controls_get(vmx) & CPU_BASED_CR3_LOAD_EXITING); +} + static void ept_update_paging_mode_cr0(unsigned long *hw_cr0, unsigned long cr0, struct kvm_vcpu *vcpu) @@ -7643,6 +7650,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .is_valid_cr4 = vmx_is_valid_cr4, .set_cr4 = vmx_set_cr4, .control_cr3_intercept = vmx_control_cr3_intercept, + .cr3_write_intercepted = vmx_cr3_write_intercepted, .set_efer = vmx_set_efer, .get_idt = vmx_get_idt, .set_idt = vmx_set_idt, From patchwork Wed Nov 25 09:34:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930859 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E4FA6C8301A for ; Wed, 25 Nov 2020 09:42:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A538120BED for ; Wed, 25 Nov 2020 09:42:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728677AbgKYJmV (ORCPT ); Wed, 25 Nov 2020 04:42:21 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57312 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728235AbgKYJmH (ORCPT ); Wed, 25 Nov 2020 04:42:07 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id BDCA0305D4FC; Wed, 25 Nov 2020 11:35:44 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id A39703072785; Wed, 25 Nov 2020 11:35:44 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 11/81] KVM: x86: add kvm_x86_ops.desc_ctrl_supported() Date: Wed, 25 Nov 2020 11:34:50 +0200 Message-Id: <20201125093600.2766-12-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org When the introspection tool tries to enable the KVMI_VCPU_EVENT_DESCRIPTOR event, this function is used to check if the control of VM-exits caused by descriptor-table registers access is supported. Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/svm.c | 6 ++++++ arch/x86/kvm/vmx/capabilities.h | 7 ++++++- arch/x86/kvm/vmx/vmx.c | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index a402384a9326..1e9cb521324e 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1130,6 +1130,7 @@ struct kvm_x86_ops { void (*set_idt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); void (*get_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); void (*set_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); + bool (*desc_ctrl_supported)(void); void (*sync_dirty_debug_regs)(struct kvm_vcpu *vcpu); void (*set_dr7)(struct kvm_vcpu *vcpu, unsigned long value); void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 5000ee25545b..f3ee6bad0db5 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1630,6 +1630,11 @@ static void svm_set_gdt(struct kvm_vcpu *vcpu, struct desc_ptr *dt) vmcb_mark_dirty(svm->vmcb, VMCB_DT); } +static bool svm_desc_ctrl_supported(void) +{ + return true; +} + static void update_cr0_intercept(struct vcpu_svm *svm) { ulong gcr0 = svm->vcpu.arch.cr0; @@ -4260,6 +4265,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .set_idt = svm_set_idt, .get_gdt = svm_get_gdt, .set_gdt = svm_set_gdt, + .desc_ctrl_supported = svm_desc_ctrl_supported, .set_dr7 = svm_set_dr7, .sync_dirty_debug_regs = svm_sync_dirty_debug_regs, .cache_reg = svm_cache_reg, diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index 3a1861403d73..6695b061bae4 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -142,12 +142,17 @@ static inline bool cpu_has_vmx_ept(void) SECONDARY_EXEC_ENABLE_EPT; } -static inline bool vmx_umip_emulated(void) +static inline bool vmx_desc_ctrl_supported(void) { return vmcs_config.cpu_based_2nd_exec_ctrl & SECONDARY_EXEC_DESC; } +static inline bool vmx_umip_emulated(void) +{ + return vmx_desc_ctrl_supported(); +} + static inline bool cpu_has_vmx_rdtscp(void) { return vmcs_config.cpu_based_2nd_exec_ctrl & diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 7b2a60cd7a76..a5e1f61d2622 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -7656,6 +7656,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .set_idt = vmx_set_idt, .get_gdt = vmx_get_gdt, .set_gdt = vmx_set_gdt, + .desc_ctrl_supported = vmx_desc_ctrl_supported, .set_dr7 = vmx_set_dr7, .sync_dirty_debug_regs = vmx_sync_dirty_debug_regs, .cache_reg = vmx_cache_reg, From patchwork Wed Nov 25 09:34:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930791 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6D01AC5519F for ; Wed, 25 Nov 2020 09:41:53 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 0C6392083E for ; Wed, 25 Nov 2020 09:41:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727123AbgKYJlw (ORCPT ); Wed, 25 Nov 2020 04:41:52 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57012 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726620AbgKYJlv (ORCPT ); Wed, 25 Nov 2020 04:41:51 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E19A5305D4FD; Wed, 25 Nov 2020 11:35:44 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C141D3072784; Wed, 25 Nov 2020 11:35:44 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 12/81] KVM: svm: add support for descriptor-table VM-exits Date: Wed, 25 Nov 2020 11:34:51 +0200 Message-Id: <20201125093600.2766-13-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This function is needed for the KVMI_VCPU_EVENT_DESCRIPTOR event. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/svm/svm.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index f3ee6bad0db5..00bda794609c 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2335,6 +2335,13 @@ static int rsm_interception(struct vcpu_svm *svm) return kvm_emulate_instruction_from_buffer(&svm->vcpu, rsm_ins_bytes, 2); } +static int descriptor_access_interception(struct vcpu_svm *svm) +{ + struct kvm_vcpu *vcpu = &svm->vcpu; + + return kvm_emulate_instruction(vcpu, 0); +} + static int rdpmc_interception(struct vcpu_svm *svm) { int err; @@ -2959,6 +2966,14 @@ static int (*const svm_exit_handlers[])(struct vcpu_svm *svm) = { [SVM_EXIT_RSM] = rsm_interception, [SVM_EXIT_AVIC_INCOMPLETE_IPI] = avic_incomplete_ipi_interception, [SVM_EXIT_AVIC_UNACCELERATED_ACCESS] = avic_unaccelerated_access_interception, + [SVM_EXIT_IDTR_READ] = descriptor_access_interception, + [SVM_EXIT_GDTR_READ] = descriptor_access_interception, + [SVM_EXIT_LDTR_READ] = descriptor_access_interception, + [SVM_EXIT_TR_READ] = descriptor_access_interception, + [SVM_EXIT_IDTR_WRITE] = descriptor_access_interception, + [SVM_EXIT_GDTR_WRITE] = descriptor_access_interception, + [SVM_EXIT_LDTR_WRITE] = descriptor_access_interception, + [SVM_EXIT_TR_WRITE] = descriptor_access_interception, }; static void dump_vmcb(struct kvm_vcpu *vcpu) From patchwork Wed Nov 25 09:34:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930941 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 057CCC6379D for ; Wed, 25 Nov 2020 09:43:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B9F8720B1F for ; Wed, 25 Nov 2020 09:43:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728873AbgKYJnM (ORCPT ); Wed, 25 Nov 2020 04:43:12 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57138 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727368AbgKYJlz (ORCPT ); Wed, 25 Nov 2020 04:41:55 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 0B11B305D4FE; Wed, 25 Nov 2020 11:35:45 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id E6B523072785; Wed, 25 Nov 2020 11:35:44 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 13/81] KVM: x86: add kvm_x86_ops.control_desc_intercept() Date: Wed, 25 Nov 2020 11:34:52 +0200 Message-Id: <20201125093600.2766-14-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This function is needed to intercept descriptor-table registers access. Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/svm.c | 26 ++++++++++++++++++++++++++ arch/x86/kvm/vmx/vmx.c | 15 +++++++++++++-- 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 1e9cb521324e..730429cd2e3d 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1131,6 +1131,7 @@ struct kvm_x86_ops { void (*get_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); void (*set_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); bool (*desc_ctrl_supported)(void); + void (*control_desc_intercept)(struct kvm_vcpu *vcpu, bool enable); void (*sync_dirty_debug_regs)(struct kvm_vcpu *vcpu); void (*set_dr7)(struct kvm_vcpu *vcpu, unsigned long value); void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 00bda794609c..c8e56ad9cbb1 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1635,6 +1635,31 @@ static bool svm_desc_ctrl_supported(void) return true; } +static void svm_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) +{ + struct vcpu_svm *svm = to_svm(vcpu); + + if (enable) { + svm_set_intercept(svm, INTERCEPT_STORE_IDTR); + svm_set_intercept(svm, INTERCEPT_STORE_GDTR); + svm_set_intercept(svm, INTERCEPT_STORE_LDTR); + svm_set_intercept(svm, INTERCEPT_STORE_TR); + svm_set_intercept(svm, INTERCEPT_LOAD_IDTR); + svm_set_intercept(svm, INTERCEPT_LOAD_GDTR); + svm_set_intercept(svm, INTERCEPT_LOAD_LDTR); + svm_set_intercept(svm, INTERCEPT_LOAD_TR); + } else { + svm_clr_intercept(svm, INTERCEPT_STORE_IDTR); + svm_clr_intercept(svm, INTERCEPT_STORE_GDTR); + svm_clr_intercept(svm, INTERCEPT_STORE_LDTR); + svm_clr_intercept(svm, INTERCEPT_STORE_TR); + svm_clr_intercept(svm, INTERCEPT_LOAD_IDTR); + svm_clr_intercept(svm, INTERCEPT_LOAD_GDTR); + svm_clr_intercept(svm, INTERCEPT_LOAD_LDTR); + svm_clr_intercept(svm, INTERCEPT_LOAD_TR); + } +} + static void update_cr0_intercept(struct vcpu_svm *svm) { ulong gcr0 = svm->vcpu.arch.cr0; @@ -4281,6 +4306,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .get_gdt = svm_get_gdt, .set_gdt = svm_set_gdt, .desc_ctrl_supported = svm_desc_ctrl_supported, + .control_desc_intercept = svm_control_desc_intercept, .set_dr7 = svm_set_dr7, .sync_dirty_debug_regs = svm_sync_dirty_debug_regs, .cache_reg = svm_cache_reg, diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index a5e1f61d2622..20351e027898 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -3120,6 +3120,16 @@ static void vmx_load_mmu_pgd(struct kvm_vcpu *vcpu, unsigned long pgd, vmcs_writel(GUEST_CR3, guest_cr3); } +static void vmx_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + + if (enable) + secondary_exec_controls_setbit(vmx, SECONDARY_EXEC_DESC); + else + secondary_exec_controls_clearbit(vmx, SECONDARY_EXEC_DESC); +} + static bool vmx_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) { /* @@ -3157,11 +3167,11 @@ void vmx_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) if (!boot_cpu_has(X86_FEATURE_UMIP) && vmx_umip_emulated()) { if (cr4 & X86_CR4_UMIP) { - secondary_exec_controls_setbit(vmx, SECONDARY_EXEC_DESC); + vmx_control_desc_intercept(vcpu, true); hw_cr4 &= ~X86_CR4_UMIP; } else if (!is_guest_mode(vcpu) || !nested_cpu_has2(get_vmcs12(vcpu), SECONDARY_EXEC_DESC)) { - secondary_exec_controls_clearbit(vmx, SECONDARY_EXEC_DESC); + vmx_control_desc_intercept(vcpu, false); } } @@ -7657,6 +7667,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .get_gdt = vmx_get_gdt, .set_gdt = vmx_set_gdt, .desc_ctrl_supported = vmx_desc_ctrl_supported, + .control_desc_intercept = vmx_control_desc_intercept, .set_dr7 = vmx_set_dr7, .sync_dirty_debug_regs = vmx_sync_dirty_debug_regs, .cache_reg = vmx_cache_reg, From patchwork Wed Nov 25 09:34:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930945 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 23A71C64E90 for ; Wed, 25 Nov 2020 09:42:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D37682145D for ; Wed, 25 Nov 2020 09:42:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728761AbgKYJmt (ORCPT ); Wed, 25 Nov 2020 04:42:49 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57144 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728291AbgKYJmB (ORCPT ); Wed, 25 Nov 2020 04:42:01 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 2D094305D500; Wed, 25 Nov 2020 11:35:45 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 0E61D3072784; Wed, 25 Nov 2020 11:35:45 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 14/81] KVM: x86: add kvm_x86_ops.desc_intercepted() Date: Wed, 25 Nov 2020 11:34:53 +0200 Message-Id: <20201125093600.2766-15-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This function will be used to test if the descriptor-table registers access is already tracked by userspace. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/svm.c | 15 +++++++++++++++ arch/x86/kvm/vmx/vmx.c | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 730429cd2e3d..0e9144e23ce6 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1132,6 +1132,7 @@ struct kvm_x86_ops { void (*set_gdt)(struct kvm_vcpu *vcpu, struct desc_ptr *dt); bool (*desc_ctrl_supported)(void); void (*control_desc_intercept)(struct kvm_vcpu *vcpu, bool enable); + bool (*desc_intercepted)(struct kvm_vcpu *vcpu); void (*sync_dirty_debug_regs)(struct kvm_vcpu *vcpu); void (*set_dr7)(struct kvm_vcpu *vcpu, unsigned long value); void (*cache_reg)(struct kvm_vcpu *vcpu, enum kvm_reg reg); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index c8e56ad9cbb1..86f0dcf9fecd 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1660,6 +1660,20 @@ static void svm_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) } } +static inline bool svm_desc_intercepted(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *svm = to_svm(vcpu); + + return (svm_is_intercept(svm, INTERCEPT_STORE_IDTR) || + svm_is_intercept(svm, INTERCEPT_STORE_GDTR) || + svm_is_intercept(svm, INTERCEPT_STORE_LDTR) || + svm_is_intercept(svm, INTERCEPT_STORE_TR) || + svm_is_intercept(svm, INTERCEPT_LOAD_IDTR) || + svm_is_intercept(svm, INTERCEPT_LOAD_GDTR) || + svm_is_intercept(svm, INTERCEPT_LOAD_LDTR) || + svm_is_intercept(svm, INTERCEPT_LOAD_TR)); +} + static void update_cr0_intercept(struct vcpu_svm *svm) { ulong gcr0 = svm->vcpu.arch.cr0; @@ -4307,6 +4321,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .set_gdt = svm_set_gdt, .desc_ctrl_supported = svm_desc_ctrl_supported, .control_desc_intercept = svm_control_desc_intercept, + .desc_intercepted = svm_desc_intercepted, .set_dr7 = svm_set_dr7, .sync_dirty_debug_regs = svm_sync_dirty_debug_regs, .cache_reg = svm_cache_reg, diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 20351e027898..5bd6a4add27e 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -3361,6 +3361,13 @@ static void vmx_set_gdt(struct kvm_vcpu *vcpu, struct desc_ptr *dt) vmcs_writel(GUEST_GDTR_BASE, dt->address); } +static bool vmx_desc_intercepted(struct kvm_vcpu *vcpu) +{ + struct vcpu_vmx *vmx = to_vmx(vcpu); + + return !!(secondary_exec_controls_get(vmx) & SECONDARY_EXEC_DESC); +} + static bool rmode_segment_valid(struct kvm_vcpu *vcpu, int seg) { struct kvm_segment var; @@ -7668,6 +7675,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .set_gdt = vmx_set_gdt, .desc_ctrl_supported = vmx_desc_ctrl_supported, .control_desc_intercept = vmx_control_desc_intercept, + .desc_intercepted = vmx_desc_intercepted, .set_dr7 = vmx_set_dr7, .sync_dirty_debug_regs = vmx_sync_dirty_debug_regs, .cache_reg = vmx_cache_reg, From patchwork Wed Nov 25 09:34:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930881 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D33B4C83017 for ; Wed, 25 Nov 2020 09:42:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A0DF120B1F for ; Wed, 25 Nov 2020 09:42:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728638AbgKYJmM (ORCPT ); Wed, 25 Nov 2020 04:42:12 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57224 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728588AbgKYJmI (ORCPT ); Wed, 25 Nov 2020 04:42:08 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 4EA3A305D501; Wed, 25 Nov 2020 11:35:45 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 2F2AB3072785; Wed, 25 Nov 2020 11:35:45 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 15/81] KVM: x86: add kvm_x86_ops.msr_write_intercepted() Date: Wed, 25 Nov 2020 11:34:54 +0200 Message-Id: <20201125093600.2766-16-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This function will be used to check if the write access for a specific MSR is already intercepted. The information will be used to restore the interception status when the introspection tool is no longer interested in that MSR. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/svm/svm.c | 1 + arch/x86/kvm/vmx/vmx.c | 1 + 3 files changed, 3 insertions(+) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 0e9144e23ce6..5236008d231f 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1112,6 +1112,7 @@ struct kvm_x86_ops { void (*update_exception_bitmap)(struct kvm_vcpu *vcpu); int (*get_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr); int (*set_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr); + bool (*msr_write_intercepted)(struct kvm_vcpu *vcpu, u32 msr); u64 (*get_segment_base)(struct kvm_vcpu *vcpu, int seg); void (*get_segment)(struct kvm_vcpu *vcpu, struct kvm_segment *var, int seg); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 86f0dcf9fecd..4478942f10a5 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -4304,6 +4304,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .get_msr_feature = svm_get_msr_feature, .get_msr = svm_get_msr, .set_msr = svm_set_msr, + .msr_write_intercepted = msr_write_intercepted, .get_segment_base = svm_get_segment_base, .get_segment = svm_get_segment, .set_segment = svm_set_segment, diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 5bd6a4add27e..d4833d3bf966 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -7658,6 +7658,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .get_msr_feature = vmx_get_msr_feature, .get_msr = vmx_get_msr, .set_msr = vmx_set_msr, + .msr_write_intercepted = msr_write_intercepted, .get_segment_base = vmx_get_segment_base, .get_segment = vmx_get_segment, .set_segment = vmx_set_segment, From patchwork Wed Nov 25 09:34:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930871 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id CFBBFC83018 for ; Wed, 25 Nov 2020 09:42:43 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 90F4D2083E for ; Wed, 25 Nov 2020 09:42:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728676AbgKYJmn (ORCPT ); Wed, 25 Nov 2020 04:42:43 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57190 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728404AbgKYJmD (ORCPT ); Wed, 25 Nov 2020 04:42:03 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 753CE305D502; Wed, 25 Nov 2020 11:35:45 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 5230C3072784; Wed, 25 Nov 2020 11:35:45 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 16/81] KVM: x86: svm: use the vmx convention to control the MSR interception Date: Wed, 25 Nov 2020 11:34:55 +0200 Message-Id: <20201125093600.2766-17-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This is a preparatory patch in order to use a common interface to enable/disable the MSR interception. Also, it will allow to independently control the read and write interceptions. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 4 ++ arch/x86/kvm/svm/svm.c | 88 +++++++++++++++++++++------------ arch/x86/kvm/vmx/vmx.h | 4 -- 3 files changed, 60 insertions(+), 36 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 5236008d231f..8586c9f4feba 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -141,6 +141,10 @@ static inline gfn_t gfn_to_index(gfn_t gfn, gfn_t base_gfn, int level) #define CR_TYPE_W 2 #define CR_TYPE_RW 3 +#define MSR_TYPE_R 1 +#define MSR_TYPE_W 2 +#define MSR_TYPE_RW 3 + #define ASYNC_PF_PER_VCPU 64 enum kvm_reg { diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 4478942f10a5..8d662ccf5b62 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -584,8 +584,8 @@ static int direct_access_msr_slot(u32 msr) return -ENOENT; } -static void set_shadow_msr_intercept(struct kvm_vcpu *vcpu, u32 msr, int read, - int write) +static void set_shadow_msr_intercept(struct kvm_vcpu *vcpu, u32 msr, + int type, bool value) { struct vcpu_svm *svm = to_svm(vcpu); int slot = direct_access_msr_slot(msr); @@ -594,15 +594,19 @@ static void set_shadow_msr_intercept(struct kvm_vcpu *vcpu, u32 msr, int read, return; /* Set the shadow bitmaps to the desired intercept states */ - if (read) - set_bit(slot, svm->shadow_msr_intercept.read); - else - clear_bit(slot, svm->shadow_msr_intercept.read); + if (type & MSR_TYPE_R) { + if (value) + set_bit(slot, svm->shadow_msr_intercept.read); + else + clear_bit(slot, svm->shadow_msr_intercept.read); + } - if (write) - set_bit(slot, svm->shadow_msr_intercept.write); - else - clear_bit(slot, svm->shadow_msr_intercept.write); + if (type & MSR_TYPE_W) { + if (value) + set_bit(slot, svm->shadow_msr_intercept.write); + else + clear_bit(slot, svm->shadow_msr_intercept.write); + } } static bool valid_msr_intercept(u32 index) @@ -630,7 +634,7 @@ static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr) } static void set_msr_interception_bitmap(struct kvm_vcpu *vcpu, u32 *msrpm, - u32 msr, int read, int write) + u32 msr, int type, bool value) { u8 bit_read, bit_write; unsigned long tmp; @@ -643,11 +647,13 @@ static void set_msr_interception_bitmap(struct kvm_vcpu *vcpu, u32 *msrpm, WARN_ON(!valid_msr_intercept(msr)); /* Enforce non allowed MSRs to trap */ - if (read && !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_READ)) - read = 0; + if (value && (type & MSR_TYPE_R) && + !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_READ)) + type &= ~MSR_TYPE_R; - if (write && !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_WRITE)) - write = 0; + if (value && (type & MSR_TYPE_W) && + !kvm_msr_allowed(vcpu, msr, KVM_MSR_FILTER_WRITE)) + type &= ~MSR_TYPE_W; offset = svm_msrpm_offset(msr); bit_read = 2 * (msr & 0x0f); @@ -656,17 +662,19 @@ static void set_msr_interception_bitmap(struct kvm_vcpu *vcpu, u32 *msrpm, BUG_ON(offset == MSR_INVALID); - read ? clear_bit(bit_read, &tmp) : set_bit(bit_read, &tmp); - write ? clear_bit(bit_write, &tmp) : set_bit(bit_write, &tmp); + if (type & MSR_TYPE_R) + value ? clear_bit(bit_read, &tmp) : set_bit(bit_read, &tmp); + if (type & MSR_TYPE_W) + value ? clear_bit(bit_write, &tmp) : set_bit(bit_write, &tmp); msrpm[offset] = tmp; } static void set_msr_interception(struct kvm_vcpu *vcpu, u32 *msrpm, u32 msr, - int read, int write) + int type, bool value) { - set_shadow_msr_intercept(vcpu, msr, read, write); - set_msr_interception_bitmap(vcpu, msrpm, msr, read, write); + set_shadow_msr_intercept(vcpu, msr, type, value); + set_msr_interception_bitmap(vcpu, msrpm, msr, type, value); } u32 *svm_vcpu_alloc_msrpm(void) @@ -690,7 +698,8 @@ void svm_vcpu_init_msrpm(struct kvm_vcpu *vcpu, u32 *msrpm) for (i = 0; direct_access_msrs[i].index != MSR_INVALID; i++) { if (!direct_access_msrs[i].always) continue; - set_msr_interception(vcpu, msrpm, direct_access_msrs[i].index, 1, 1); + set_msr_interception(vcpu, msrpm, direct_access_msrs[i].index, + MSR_TYPE_RW, 1); } } @@ -715,7 +724,10 @@ static void svm_msr_filter_changed(struct kvm_vcpu *vcpu) u32 read = test_bit(i, svm->shadow_msr_intercept.read); u32 write = test_bit(i, svm->shadow_msr_intercept.write); - set_msr_interception_bitmap(vcpu, svm->msrpm, msr, read, write); + set_msr_interception_bitmap(vcpu, svm->msrpm, msr, + MSR_TYPE_R, read); + set_msr_interception_bitmap(vcpu, svm->msrpm, msr, + MSR_TYPE_W, write); } } @@ -767,10 +779,14 @@ static void svm_enable_lbrv(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); svm->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK; - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 1, 1); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 1, 1); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, + MSR_TYPE_RW, 1); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, + MSR_TYPE_RW, 1); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, + MSR_TYPE_RW, 1); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, + MSR_TYPE_RW, 1); } static void svm_disable_lbrv(struct kvm_vcpu *vcpu) @@ -778,10 +794,14 @@ static void svm_disable_lbrv(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); svm->vmcb->control.virt_ext &= ~LBR_CTL_ENABLE_MASK; - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, 0, 0); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, 0, 0); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, 0, 0); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, 0, 0); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHFROMIP, + MSR_TYPE_RW, 0); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTBRANCHTOIP, + MSR_TYPE_RW, 0); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTFROMIP, + MSR_TYPE_RW, 0); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_LASTINTTOIP, + MSR_TYPE_RW, 0); } void disable_nmi_singlestep(struct vcpu_svm *svm) @@ -2734,7 +2754,8 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) * We update the L1 MSR bit as well since it will end up * touching the MSR anyway now. */ - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SPEC_CTRL, 1, 1); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_SPEC_CTRL, + MSR_TYPE_RW, 1); break; case MSR_IA32_PRED_CMD: if (!msr->host_initiated && @@ -2749,7 +2770,10 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) break; wrmsrl(MSR_IA32_PRED_CMD, PRED_CMD_IBPB); - set_msr_interception(vcpu, svm->msrpm, MSR_IA32_PRED_CMD, 0, 1); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_PRED_CMD, + MSR_TYPE_R, 0); + set_msr_interception(vcpu, svm->msrpm, MSR_IA32_PRED_CMD, + MSR_TYPE_W, 1); break; case MSR_AMD64_VIRT_SPEC_CTRL: if (!msr->host_initiated && diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 9d3a557949ac..892e9ca643c4 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -16,10 +16,6 @@ extern const u32 vmx_msr_index[]; -#define MSR_TYPE_R 1 -#define MSR_TYPE_W 2 -#define MSR_TYPE_RW 3 - #define X2APIC_MSR(r) (APIC_BASE_MSR + ((r) >> 4)) #ifdef CONFIG_X86_64 From patchwork Wed Nov 25 09:34:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930903 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 96AEFC64E7B for ; Wed, 25 Nov 2020 09:42:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4A57221D91 for ; Wed, 25 Nov 2020 09:42:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728324AbgKYJmA (ORCPT ); Wed, 25 Nov 2020 04:42:00 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57134 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728077AbgKYJl7 (ORCPT ); Wed, 25 Nov 2020 04:41:59 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 9B3EB305D503; Wed, 25 Nov 2020 11:35:45 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 78DE63072785; Wed, 25 Nov 2020 11:35:45 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?b?TmljdciZ?= =?utf-8?b?b3IgQ8OuyJt1?= , =?utf-8?q?Adalbert_Laz?= =?utf-8?q?=C4=83r?= Subject: [PATCH v10 17/81] KVM: x86: add kvm_x86_ops.control_msr_intercept() Date: Wed, 25 Nov 2020 11:34:56 +0200 Message-Id: <20201125093600.2766-18-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This is needed for the KVMI_VCPU_EVENT_MSR event, which is used notify the introspection tool about any change made to a MSR of interest. Signed-off-by: Mihai Donțu Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/svm/svm.c | 11 +++++++++++ arch/x86/kvm/vmx/vmx.c | 7 +++++++ 3 files changed, 20 insertions(+) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 8586c9f4feba..01853453a659 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1116,6 +1116,8 @@ struct kvm_x86_ops { void (*update_exception_bitmap)(struct kvm_vcpu *vcpu); int (*get_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr); int (*set_msr)(struct kvm_vcpu *vcpu, struct msr_data *msr); + void (*control_msr_intercept)(struct kvm_vcpu *vcpu, unsigned int msr, + int type, bool enable); bool (*msr_write_intercepted)(struct kvm_vcpu *vcpu, u32 msr); u64 (*get_segment_base)(struct kvm_vcpu *vcpu, int seg); void (*get_segment)(struct kvm_vcpu *vcpu, diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 8d662ccf5b62..2bfefcfbddd7 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -677,6 +677,16 @@ static void set_msr_interception(struct kvm_vcpu *vcpu, u32 *msrpm, u32 msr, set_msr_interception_bitmap(vcpu, msrpm, msr, type, value); } +static void svm_control_msr_intercept(struct kvm_vcpu *vcpu, unsigned int msr, + int type, bool enable) +{ + const struct vcpu_svm *svm = to_svm(vcpu); + u32 *msrpm = is_guest_mode(vcpu) ? svm->nested.msrpm : + svm->msrpm; + + set_msr_interception(vcpu, msrpm, msr, type, enable); +} + u32 *svm_vcpu_alloc_msrpm(void) { struct page *pages = alloc_pages(GFP_KERNEL_ACCOUNT, MSRPM_ALLOC_ORDER); @@ -4328,6 +4338,7 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .get_msr_feature = svm_get_msr_feature, .get_msr = svm_get_msr, .set_msr = svm_set_msr, + .control_msr_intercept = svm_control_msr_intercept, .msr_write_intercepted = msr_write_intercepted, .get_segment_base = svm_get_segment_base, .get_segment = svm_get_segment, diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index d4833d3bf966..c1497b8e506c 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -3826,6 +3826,12 @@ static __always_inline void vmx_set_intercept_for_msr(struct kvm_vcpu *vcpu, vmx_disable_intercept_for_msr(vcpu, msr, type); } +static void vmx_control_msr_intercept(struct kvm_vcpu *vcpu, unsigned int msr, + int type, bool enable) +{ + vmx_set_intercept_for_msr(vcpu, msr, type, enable); +} + static u8 vmx_msr_bitmap_mode(struct kvm_vcpu *vcpu) { u8 mode = 0; @@ -7658,6 +7664,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .get_msr_feature = vmx_get_msr_feature, .get_msr = vmx_get_msr, .set_msr = vmx_set_msr, + .control_msr_intercept = vmx_control_msr_intercept, .msr_write_intercepted = msr_write_intercepted, .get_segment_base = vmx_get_segment_base, .get_segment = vmx_get_segment, From patchwork Wed Nov 25 09:34:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930865 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4D32DC64E7D for ; Wed, 25 Nov 2020 09:42:23 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 04CA020BED for ; Wed, 25 Nov 2020 09:42:22 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728687AbgKYJmW (ORCPT ); Wed, 25 Nov 2020 04:42:22 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57100 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728193AbgKYJmG (ORCPT ); Wed, 25 Nov 2020 04:42:06 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id BC901305D504; Wed, 25 Nov 2020 11:35:45 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 9DE753072784; Wed, 25 Nov 2020 11:35:45 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 18/81] KVM: x86: vmx: use a symbolic constant when checking the exit qualifications Date: Wed, 25 Nov 2020 11:34:57 +0200 Message-Id: <20201125093600.2766-19-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This should make the code more readable. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/vmx/vmx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index c1497b8e506c..a7d2bab38233 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5386,8 +5386,8 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu) EPT_VIOLATION_EXECUTABLE)) ? PFERR_PRESENT_MASK : 0; - error_code |= (exit_qualification & 0x100) != 0 ? - PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK; + error_code |= (exit_qualification & EPT_VIOLATION_GVA_TRANSLATED) + ? PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK; vcpu->arch.exit_qualification = exit_qualification; From patchwork Wed Nov 25 09:34:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930793 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2C8B4C56201 for ; Wed, 25 Nov 2020 09:41:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CE971206F9 for ; Wed, 25 Nov 2020 09:41:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727329AbgKYJlx (ORCPT ); Wed, 25 Nov 2020 04:41:53 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57024 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726903AbgKYJlw (ORCPT ); Wed, 25 Nov 2020 04:41:52 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E21B2305D505; Wed, 25 Nov 2020 11:35:45 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id BFB963072785; Wed, 25 Nov 2020 11:35:45 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 19/81] KVM: x86: save the error code during EPT/NPF exits handling Date: Wed, 25 Nov 2020 11:34:58 +0200 Message-Id: <20201125093600.2766-20-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This is needed for kvm_page_track_emulation_failure(). When the introspection tool {read,write,exec}-protect a guest memory page, it is notified from the read/write/fetch callbacks used by the KVM emulator. If the emulation fails it is possible that the read/write callbacks were not used. In such cases, the emulator will call kvm_page_track_emulation_failure() to ensure that the introspection tool is notified of the read/write #PF (based on this saved error code), which in turn can emulate the instruction or unprotect the memory page (and let the guest execute the instruction). Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 3 +++ arch/x86/kvm/svm/svm.c | 2 ++ arch/x86/kvm/vmx/vmx.c | 1 + 3 files changed, 6 insertions(+) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 01853453a659..86048037da23 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -813,6 +813,9 @@ struct kvm_vcpu_arch { */ bool enforce; } pv_cpuid; + + /* #PF translated error code from EPT/NPT exit reason */ + u64 error_code; }; struct kvm_lpage_info { diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 2bfefcfbddd7..43a2e4ec6178 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1916,6 +1916,8 @@ static int npf_interception(struct vcpu_svm *svm) u64 fault_address = __sme_clr(svm->vmcb->control.exit_info_2); u64 error_code = svm->vmcb->control.exit_info_1; + svm->vcpu.arch.error_code = error_code; + trace_kvm_page_fault(fault_address, error_code); return kvm_mmu_page_fault(&svm->vcpu, fault_address, error_code, static_cpu_has(X86_FEATURE_DECODEASSISTS) ? diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index a7d2bab38233..d5d4203378d3 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5390,6 +5390,7 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu) ? PFERR_GUEST_FINAL_MASK : PFERR_GUEST_PAGE_MASK; vcpu->arch.exit_qualification = exit_qualification; + vcpu->arch.error_code = error_code; /* * Check that the GPA doesn't exceed physical memory limits, as that is From patchwork Wed Nov 25 09:34:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930849 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 11E5CC83013 for ; Wed, 25 Nov 2020 09:42:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8A6282083E for ; Wed, 25 Nov 2020 09:42:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728707AbgKYJm0 (ORCPT ); Wed, 25 Nov 2020 04:42:26 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57340 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728540AbgKYJmG (ORCPT ); Wed, 25 Nov 2020 04:42:06 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 1586E305D4FF; Wed, 25 Nov 2020 11:35:46 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id E53133072784; Wed, 25 Nov 2020 11:35:45 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?b?TmljdciZ?= =?utf-8?b?b3IgQ8OuyJt1?= , =?utf-8?q?Adalbert_Laz?= =?utf-8?q?=C4=83r?= Subject: [PATCH v10 20/81] KVM: x86: add kvm_x86_ops.fault_gla() Date: Wed, 25 Nov 2020 11:34:59 +0200 Message-Id: <20201125093600.2766-21-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This function is needed for kvmi_update_ad_flags() and kvm_page_track_emulation_failure(). kvmi_update_ad_flags() uses the existing guest page table walk code to update the A/D bits and return to guest (when the introspection tool write-protects the guest page tables). kvm_page_track_emulation_failure() calls the page tracking code, that can trigger an event for the introspection tool (which might need the GVA in addition to the GPA). Signed-off-by: Mihai Donțu Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/include/asm/vmx.h | 2 ++ arch/x86/kvm/svm/svm.c | 9 +++++++++ arch/x86/kvm/vmx/vmx.c | 9 +++++++++ 4 files changed, 22 insertions(+) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 86048037da23..45c72af05fa2 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1303,6 +1303,8 @@ struct kvm_x86_ops { void (*migrate_timers)(struct kvm_vcpu *vcpu); void (*msr_filter_changed)(struct kvm_vcpu *vcpu); + + u64 (*fault_gla)(struct kvm_vcpu *vcpu); }; struct kvm_x86_nested_ops { diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index 38ca445a8429..5543332292b5 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -544,6 +544,7 @@ enum vm_entry_failure_code { #define EPT_VIOLATION_READABLE_BIT 3 #define EPT_VIOLATION_WRITABLE_BIT 4 #define EPT_VIOLATION_EXECUTABLE_BIT 5 +#define EPT_VIOLATION_GLA_VALID_BIT 7 #define EPT_VIOLATION_GVA_TRANSLATED_BIT 8 #define EPT_VIOLATION_ACC_READ (1 << EPT_VIOLATION_ACC_READ_BIT) #define EPT_VIOLATION_ACC_WRITE (1 << EPT_VIOLATION_ACC_WRITE_BIT) @@ -551,6 +552,7 @@ enum vm_entry_failure_code { #define EPT_VIOLATION_READABLE (1 << EPT_VIOLATION_READABLE_BIT) #define EPT_VIOLATION_WRITABLE (1 << EPT_VIOLATION_WRITABLE_BIT) #define EPT_VIOLATION_EXECUTABLE (1 << EPT_VIOLATION_EXECUTABLE_BIT) +#define EPT_VIOLATION_GLA_VALID (1 << EPT_VIOLATION_GLA_VALID_BIT) #define EPT_VIOLATION_GVA_TRANSLATED (1 << EPT_VIOLATION_GVA_TRANSLATED_BIT) /* diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 43a2e4ec6178..c6730ec39c58 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -4314,6 +4314,13 @@ static int svm_vm_init(struct kvm *kvm) return 0; } +static u64 svm_fault_gla(struct kvm_vcpu *vcpu) +{ + const struct vcpu_svm *svm = to_svm(vcpu); + + return svm->vcpu.arch.cr2 ? svm->vcpu.arch.cr2 : ~0ull; +} + static struct kvm_x86_ops svm_x86_ops __initdata = { .hardware_unsetup = svm_hardware_teardown, .hardware_enable = svm_hardware_enable, @@ -4442,6 +4449,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .apic_init_signal_blocked = svm_apic_init_signal_blocked, .msr_filter_changed = svm_msr_filter_changed, + + .fault_gla = svm_fault_gla, }; static struct kvm_x86_init_ops svm_init_ops __initdata = { diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index d5d4203378d3..41ea1ee9d419 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -7641,6 +7641,13 @@ static int vmx_cpu_dirty_log_size(void) return enable_pml ? PML_ENTITY_NUM : 0; } +static u64 vmx_fault_gla(struct kvm_vcpu *vcpu) +{ + if (vcpu->arch.exit_qualification & EPT_VIOLATION_GLA_VALID) + return vmcs_readl(GUEST_LINEAR_ADDRESS); + return ~0ull; +} + static struct kvm_x86_ops vmx_x86_ops __initdata = { .hardware_unsetup = hardware_unsetup, @@ -7779,6 +7786,8 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .msr_filter_changed = vmx_msr_filter_changed, .cpu_dirty_log_size = vmx_cpu_dirty_log_size, + + .fault_gla = vmx_fault_gla, }; static __init int hardware_setup(void) From patchwork Wed Nov 25 09:35:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930803 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BF560C64E75 for ; Wed, 25 Nov 2020 09:41:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 71063206F9 for ; Wed, 25 Nov 2020 09:41:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728065AbgKYJl5 (ORCPT ); Wed, 25 Nov 2020 04:41:57 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57134 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727682AbgKYJl5 (ORCPT ); Wed, 25 Nov 2020 04:41:57 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 36FBC305D506; Wed, 25 Nov 2020 11:35:46 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 172643072785; Wed, 25 Nov 2020 11:35:46 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 21/81] KVM: x86: add kvm_x86_ops.control_singlestep() Date: Wed, 25 Nov 2020 11:35:00 +0200 Message-Id: <20201125093600.2766-22-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This function is needed for KVMI_VCPU_CONTROL_SINGLESTEP. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/vmx/vmx.c | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 45c72af05fa2..c2da5c24e825 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1305,6 +1305,7 @@ struct kvm_x86_ops { void (*msr_filter_changed)(struct kvm_vcpu *vcpu); u64 (*fault_gla)(struct kvm_vcpu *vcpu); + void (*control_singlestep)(struct kvm_vcpu *vcpu, bool enable); }; struct kvm_x86_nested_ops { diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 41ea1ee9d419..1c8fbd6209ce 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -7648,6 +7648,16 @@ static u64 vmx_fault_gla(struct kvm_vcpu *vcpu) return ~0ull; } +static void vmx_control_singlestep(struct kvm_vcpu *vcpu, bool enable) +{ + if (enable) + exec_controls_setbit(to_vmx(vcpu), + CPU_BASED_MONITOR_TRAP_FLAG); + else + exec_controls_clearbit(to_vmx(vcpu), + CPU_BASED_MONITOR_TRAP_FLAG); +} + static struct kvm_x86_ops vmx_x86_ops __initdata = { .hardware_unsetup = hardware_unsetup, @@ -7788,6 +7798,7 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .cpu_dirty_log_size = vmx_cpu_dirty_log_size, .fault_gla = vmx_fault_gla, + .control_singlestep = vmx_control_singlestep, }; static __init int hardware_setup(void) From patchwork Wed Nov 25 09:35:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930805 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3C559C64E7B for ; Wed, 25 Nov 2020 09:41:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E7A41206F9 for ; Wed, 25 Nov 2020 09:41:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728168AbgKYJl6 (ORCPT ); Wed, 25 Nov 2020 04:41:58 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57190 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727611AbgKYJl5 (ORCPT ); Wed, 25 Nov 2020 04:41:57 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 581E7305D508; Wed, 25 Nov 2020 11:35:46 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 3AC2C3072784; Wed, 25 Nov 2020 11:35:46 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 22/81] KVM: x86: export kvm_arch_vcpu_set_guest_debug() Date: Wed, 25 Nov 2020 11:35:01 +0200 Message-Id: <20201125093600.2766-23-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This function is needed in order to notify the introspection tool through KVMI_VCPU_EVENT_BP events on guest breakpoints. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/x86.c | 18 +++++++++++++----- include/linux/kvm_host.h | 2 ++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 816801d6c95d..00ab76366868 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9684,14 +9684,12 @@ int kvm_arch_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, return ret; } -int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, - struct kvm_guest_debug *dbg) +int kvm_arch_vcpu_set_guest_debug(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg) { unsigned long rflags; int i, r; - vcpu_load(vcpu); - if (dbg->control & (KVM_GUESTDBG_INJECT_DB | KVM_GUESTDBG_INJECT_BP)) { r = -EBUSY; if (vcpu->arch.exception.pending) @@ -9737,10 +9735,20 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, r = 0; out: - vcpu_put(vcpu); return r; } +int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg) +{ + int ret; + + vcpu_load(vcpu); + ret = kvm_arch_vcpu_set_guest_debug(vcpu, dbg); + vcpu_put(vcpu); + return ret; +} + /* * Translate a guest virtual address to a guest physical address. */ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 6d622d8bd339..2c640ea9d7ba 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -919,6 +919,8 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_guest_debug *dbg); int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu); +int kvm_arch_vcpu_set_guest_debug(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *dbg); int kvm_arch_init(void *opaque); void kvm_arch_exit(void); From patchwork Wed Nov 25 09:35:02 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930817 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 30A99C64E7B for ; Wed, 25 Nov 2020 09:42:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D7B222083E for ; Wed, 25 Nov 2020 09:42:05 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728557AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57330 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728364AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 79672305D509; Wed, 25 Nov 2020 11:35:46 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 5B6183072785; Wed, 25 Nov 2020 11:35:46 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 23/81] KVM: x86: extend kvm_mmu_gva_to_gpa_system() with the 'access' parameter Date: Wed, 25 Nov 2020 11:35:02 +0200 Message-Id: <20201125093600.2766-24-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This is needed for kvmi_update_ad_flags() to emulate a guest page table walk on SPT violations due to A/D bit updates. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/kvm/x86.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index c2da5c24e825..3a06a7799571 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1568,7 +1568,7 @@ gpa_t kvm_mmu_gva_to_gpa_fetch(struct kvm_vcpu *vcpu, gva_t gva, gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva, struct x86_exception *exception); gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva, - struct x86_exception *exception); + u32 access, struct x86_exception *exception); bool kvm_apicv_activated(struct kvm *kvm); void kvm_apicv_init(struct kvm *kvm, bool enable); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 00ab76366868..8eda5c3bd244 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -5890,9 +5890,9 @@ gpa_t kvm_mmu_gva_to_gpa_write(struct kvm_vcpu *vcpu, gva_t gva, /* uses this to access any guest's mapped memory without checking CPL */ gpa_t kvm_mmu_gva_to_gpa_system(struct kvm_vcpu *vcpu, gva_t gva, - struct x86_exception *exception) + u32 access, struct x86_exception *exception) { - return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, 0, exception); + return vcpu->arch.walk_mmu->gva_to_gpa(vcpu, gva, access, exception); } static int kvm_read_guest_virt_helper(gva_t addr, void *val, unsigned int bytes, @@ -9762,7 +9762,7 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, vcpu_load(vcpu); idx = srcu_read_lock(&vcpu->kvm->srcu); - gpa = kvm_mmu_gva_to_gpa_system(vcpu, vaddr, NULL); + gpa = kvm_mmu_gva_to_gpa_system(vcpu, vaddr, 0, NULL); srcu_read_unlock(&vcpu->kvm->srcu, idx); tr->physical_address = gpa; tr->valid = gpa != UNMAPPED_GVA; From patchwork Wed Nov 25 09:35:03 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930939 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7E839C71155 for ; Wed, 25 Nov 2020 09:42:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 2D2DC2145D for ; Wed, 25 Nov 2020 09:42:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728788AbgKYJmz (ORCPT ); Wed, 25 Nov 2020 04:42:55 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57330 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727611AbgKYJl7 (ORCPT ); Wed, 25 Nov 2020 04:41:59 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 9CEF0305D50A; Wed, 25 Nov 2020 11:35:46 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 7D9813072784; Wed, 25 Nov 2020 11:35:46 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 24/81] KVM: x86: export kvm_inject_pending_exception() Date: Wed, 25 Nov 2020 11:35:03 +0200 Message-Id: <20201125093600.2766-25-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This function is needed for the KVMI_VCPU_INJECT_EXCEPTION command. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 1 + arch/x86/kvm/x86.c | 52 +++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 3a06a7799571..7dc1ebac8d91 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1514,6 +1514,7 @@ unsigned long kvm_get_rflags(struct kvm_vcpu *vcpu); void kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags); bool kvm_rdpmc(struct kvm_vcpu *vcpu); +bool kvm_inject_pending_exception(struct kvm_vcpu *vcpu); void kvm_queue_exception(struct kvm_vcpu *vcpu, unsigned nr); void kvm_queue_exception_e(struct kvm_vcpu *vcpu, unsigned nr, u32 error_code); void kvm_queue_exception_p(struct kvm_vcpu *vcpu, unsigned nr, unsigned long payload); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 8eda5c3bd244..741505f405b1 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -8200,6 +8200,35 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu) kvm_x86_ops.update_cr8_intercept(vcpu, tpr, max_irr); } +bool kvm_inject_pending_exception(struct kvm_vcpu *vcpu) +{ + if (vcpu->arch.exception.pending) { + trace_kvm_inj_exception(vcpu->arch.exception.nr, + vcpu->arch.exception.has_error_code, + vcpu->arch.exception.error_code); + + vcpu->arch.exception.pending = false; + vcpu->arch.exception.injected = true; + + if (exception_type(vcpu->arch.exception.nr) == EXCPT_FAULT) + __kvm_set_rflags(vcpu, kvm_get_rflags(vcpu) | + X86_EFLAGS_RF); + + if (vcpu->arch.exception.nr == DB_VECTOR) { + kvm_deliver_exception_payload(vcpu); + if (vcpu->arch.dr7 & DR7_GD) { + vcpu->arch.dr7 &= ~DR7_GD; + kvm_update_dr7(vcpu); + } + } + + kvm_x86_ops.queue_exception(vcpu); + return true; + } + + return false; +} + static void inject_pending_event(struct kvm_vcpu *vcpu, bool *req_immediate_exit) { int r; @@ -8251,29 +8280,8 @@ static void inject_pending_event(struct kvm_vcpu *vcpu, bool *req_immediate_exit } /* try to inject new event if pending */ - if (vcpu->arch.exception.pending) { - trace_kvm_inj_exception(vcpu->arch.exception.nr, - vcpu->arch.exception.has_error_code, - vcpu->arch.exception.error_code); - - vcpu->arch.exception.pending = false; - vcpu->arch.exception.injected = true; - - if (exception_type(vcpu->arch.exception.nr) == EXCPT_FAULT) - __kvm_set_rflags(vcpu, kvm_get_rflags(vcpu) | - X86_EFLAGS_RF); - - if (vcpu->arch.exception.nr == DB_VECTOR) { - kvm_deliver_exception_payload(vcpu); - if (vcpu->arch.dr7 & DR7_GD) { - vcpu->arch.dr7 &= ~DR7_GD; - kvm_update_dr7(vcpu); - } - } - - kvm_x86_ops.queue_exception(vcpu); + if (kvm_inject_pending_exception(vcpu)) can_inject = false; - } /* * Finally, inject interrupt events. If an event cannot be injected From patchwork Wed Nov 25 09:35:04 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930933 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4973DC64E8A for ; Wed, 25 Nov 2020 09:42:28 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 004CC206F9 for ; Wed, 25 Nov 2020 09:42:27 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728720AbgKYJm1 (ORCPT ); Wed, 25 Nov 2020 04:42:27 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57212 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728364AbgKYJmG (ORCPT ); Wed, 25 Nov 2020 04:42:06 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id BE4DD305D50B; Wed, 25 Nov 2020 11:35:46 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 9F7743072785; Wed, 25 Nov 2020 11:35:46 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 25/81] KVM: x86: export kvm_vcpu_ioctl_x86_get_xsave() Date: Wed, 25 Nov 2020 11:35:04 +0200 Message-Id: <20201125093600.2766-26-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This function is needed for the KVMI_VCPU_GET_XSAVE command. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr Reported-by: kernel test robot Reported-by: kernel test robot --- arch/x86/kvm/x86.c | 4 ++-- include/linux/kvm_host.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 741505f405b1..4fadd1ab20ae 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4497,8 +4497,8 @@ static void load_xsave(struct kvm_vcpu *vcpu, u8 *src) } } -static void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu, - struct kvm_xsave *guest_xsave) +void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu, + struct kvm_xsave *guest_xsave) { if (boot_cpu_has(X86_FEATURE_XSAVE)) { memset(guest_xsave, 0, sizeof(struct kvm_xsave)); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 2c640ea9d7ba..6eec75f77d7e 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -921,6 +921,8 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu); int kvm_arch_vcpu_set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_guest_debug *dbg); +void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu, + struct kvm_xsave *guest_xsave); int kvm_arch_init(void *opaque); void kvm_arch_exit(void); From patchwork Wed Nov 25 09:35:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930861 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B9F8FC5519F for ; Wed, 25 Nov 2020 09:42:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 711A7206F9 for ; Wed, 25 Nov 2020 09:42:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728602AbgKYJmH (ORCPT ); Wed, 25 Nov 2020 04:42:07 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57136 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728542AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id DBC36305D50C; Wed, 25 Nov 2020 11:35:46 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C102D3072784; Wed, 25 Nov 2020 11:35:46 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 26/81] KVM: x86: export kvm_vcpu_ioctl_x86_set_xsave() Date: Wed, 25 Nov 2020 11:35:05 +0200 Message-Id: <20201125093600.2766-27-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This function is needed for the KVMI_VCPU_SET_XSAVE command. Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/x86.c | 4 ++-- include/linux/kvm_host.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4fadd1ab20ae..f48603c8e44d 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -4514,8 +4514,8 @@ void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu, #define XSAVE_MXCSR_OFFSET 24 -static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu, - struct kvm_xsave *guest_xsave) +int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu, + struct kvm_xsave *guest_xsave) { u64 xstate_bv = *(u64 *)&guest_xsave->region[XSAVE_HDR_OFFSET / sizeof(u32)]; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 6eec75f77d7e..db04dab23013 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -923,6 +923,8 @@ int kvm_arch_vcpu_set_guest_debug(struct kvm_vcpu *vcpu, struct kvm_guest_debug *dbg); void kvm_vcpu_ioctl_x86_get_xsave(struct kvm_vcpu *vcpu, struct kvm_xsave *guest_xsave); +int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu, + struct kvm_xsave *guest_xsave); int kvm_arch_init(void *opaque); void kvm_arch_exit(void); From patchwork Wed Nov 25 09:35:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930815 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2C809C71155 for ; Wed, 25 Nov 2020 09:42:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D821D206F9 for ; Wed, 25 Nov 2020 09:42:08 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728610AbgKYJmI (ORCPT ); Wed, 25 Nov 2020 04:42:08 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57138 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728143AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 12720305D50D; Wed, 25 Nov 2020 11:35:47 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id DF9403072785; Wed, 25 Nov 2020 11:35:46 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 27/81] KVM: x86: page track: provide all callbacks with the guest virtual address Date: Wed, 25 Nov 2020 11:35:06 +0200 Message-Id: <20201125093600.2766-28-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This is needed because the emulator calls the page tracking code irrespective of the current VM-exit reason or available information. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 2 +- arch/x86/include/asm/kvm_page_track.h | 10 ++++++---- arch/x86/kvm/mmu/mmu.c | 2 +- arch/x86/kvm/mmu/page_track.c | 6 +++--- arch/x86/kvm/x86.c | 16 ++++++++-------- drivers/gpu/drm/i915/gvt/kvmgt.c | 2 +- 6 files changed, 20 insertions(+), 18 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 7dc1ebac8d91..0342835a79d2 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1396,7 +1396,7 @@ void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned long kvm_nr_mmu_pages); int load_pdptrs(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, unsigned long cr3); bool pdptrs_changed(struct kvm_vcpu *vcpu); -int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, +int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, const void *val, int bytes); struct kvm_irq_mask_notifier { diff --git a/arch/x86/include/asm/kvm_page_track.h b/arch/x86/include/asm/kvm_page_track.h index 87bd6025d91d..9a261e463eb3 100644 --- a/arch/x86/include/asm/kvm_page_track.h +++ b/arch/x86/include/asm/kvm_page_track.h @@ -28,12 +28,14 @@ struct kvm_page_track_notifier_node { * * @vcpu: the vcpu where the write access happened. * @gpa: the physical address written by guest. + * @gva: the virtual address written by guest. * @new: the data was written to the address. * @bytes: the written length. * @node: this node */ - void (*track_write)(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, - int bytes, struct kvm_page_track_notifier_node *node); + void (*track_write)(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + const u8 *new, int bytes, + struct kvm_page_track_notifier_node *node); /* * It is called when memory slot is being moved or removed * users can drop write-protection for the pages in that memory slot @@ -68,7 +70,7 @@ kvm_page_track_register_notifier(struct kvm *kvm, void kvm_page_track_unregister_notifier(struct kvm *kvm, struct kvm_page_track_notifier_node *n); -void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, - int bytes); +void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + const u8 *new, int bytes); void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot); #endif diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 5dfe0ede0e81..1631e2367085 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -4963,7 +4963,7 @@ static const union kvm_mmu_page_role role_ign = { .invalid = 0x1, }; -static void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, +static void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, const u8 *new, int bytes, struct kvm_page_track_notifier_node *node) { diff --git a/arch/x86/kvm/mmu/page_track.c b/arch/x86/kvm/mmu/page_track.c index 8443a675715b..d7a591a85af8 100644 --- a/arch/x86/kvm/mmu/page_track.c +++ b/arch/x86/kvm/mmu/page_track.c @@ -216,8 +216,8 @@ EXPORT_SYMBOL_GPL(kvm_page_track_unregister_notifier); * The node should figure out if the written page is the one that node is * interested in by itself. */ -void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, - int bytes) +void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + const u8 *new, int bytes) { struct kvm_page_track_notifier_head *head; struct kvm_page_track_notifier_node *n; @@ -232,7 +232,7 @@ void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, const u8 *new, hlist_for_each_entry_srcu(n, &head->track_notifier_list, node, srcu_read_lock_held(&head->track_srcu)) if (n->track_write) - n->track_write(vcpu, gpa, new, bytes, n); + n->track_write(vcpu, gpa, gva, new, bytes, n); srcu_read_unlock(&head->track_srcu, idx); } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index f48603c8e44d..c2f13a275448 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6115,7 +6115,7 @@ static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva, return vcpu_is_mmio_gpa(vcpu, gva, *gpa, write); } -int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, +int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, const void *val, int bytes) { int ret; @@ -6123,14 +6123,14 @@ int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, ret = kvm_vcpu_write_guest(vcpu, gpa, val, bytes); if (ret < 0) return 0; - kvm_page_track_write(vcpu, gpa, val, bytes); + kvm_page_track_write(vcpu, gpa, gva, val, bytes); return 1; } struct read_write_emulator_ops { int (*read_write_prepare)(struct kvm_vcpu *vcpu, void *val, int bytes); - int (*read_write_emulate)(struct kvm_vcpu *vcpu, gpa_t gpa, + int (*read_write_emulate)(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, void *val, int bytes); int (*read_write_mmio)(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes, void *val); @@ -6151,16 +6151,16 @@ static int read_prepare(struct kvm_vcpu *vcpu, void *val, int bytes) return 0; } -static int read_emulate(struct kvm_vcpu *vcpu, gpa_t gpa, +static int read_emulate(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, void *val, int bytes) { return !kvm_vcpu_read_guest(vcpu, gpa, val, bytes); } -static int write_emulate(struct kvm_vcpu *vcpu, gpa_t gpa, +static int write_emulate(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, void *val, int bytes) { - return emulator_write_phys(vcpu, gpa, val, bytes); + return emulator_write_phys(vcpu, gpa, gva, val, bytes); } static int write_mmio(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes, void *val) @@ -6228,7 +6228,7 @@ static int emulator_read_write_onepage(unsigned long addr, void *val, return X86EMUL_PROPAGATE_FAULT; } - if (!ret && ops->read_write_emulate(vcpu, gpa, val, bytes)) + if (!ret && ops->read_write_emulate(vcpu, gpa, addr, val, bytes)) return X86EMUL_CONTINUE; /* @@ -6397,7 +6397,7 @@ static int emulator_cmpxchg_emulated(struct x86_emulate_ctxt *ctxt, if (!exchanged) return X86EMUL_CMPXCHG_FAILED; - kvm_page_track_write(vcpu, gpa, new, bytes); + kvm_page_track_write(vcpu, gpa, addr, new, bytes); return X86EMUL_CONTINUE; diff --git a/drivers/gpu/drm/i915/gvt/kvmgt.c b/drivers/gpu/drm/i915/gvt/kvmgt.c index ad8a9df49f29..4e370b216365 100644 --- a/drivers/gpu/drm/i915/gvt/kvmgt.c +++ b/drivers/gpu/drm/i915/gvt/kvmgt.c @@ -1749,7 +1749,7 @@ static int kvmgt_page_track_remove(unsigned long handle, u64 gfn) return 0; } -static void kvmgt_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, +static void kvmgt_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, const u8 *val, int len, struct kvm_page_track_notifier_node *node) { From patchwork Wed Nov 25 09:35:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930937 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A4FC5C8300E for ; Wed, 25 Nov 2020 09:42:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5A5602083E for ; Wed, 25 Nov 2020 09:42:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728456AbgKYJmB (ORCPT ); Wed, 25 Nov 2020 04:42:01 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57212 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728006AbgKYJl7 (ORCPT ); Wed, 25 Nov 2020 04:41:59 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 358D6305D50E; Wed, 25 Nov 2020 11:35:47 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 15F8C3072784; Wed, 25 Nov 2020 11:35:47 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 28/81] KVM: x86: page track: add track_create_slot() callback Date: Wed, 25 Nov 2020 11:35:07 +0200 Message-Id: <20201125093600.2766-29-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This is used to add page access notifications as soon as a slot appears or when a slot is moved. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_page_track.h | 13 ++++++++++++- arch/x86/kvm/mmu/page_track.c | 17 ++++++++++++++++- arch/x86/kvm/x86.c | 7 ++++--- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/arch/x86/include/asm/kvm_page_track.h b/arch/x86/include/asm/kvm_page_track.h index 9a261e463eb3..00a66c4d4d3c 100644 --- a/arch/x86/include/asm/kvm_page_track.h +++ b/arch/x86/include/asm/kvm_page_track.h @@ -36,6 +36,17 @@ struct kvm_page_track_notifier_node { void (*track_write)(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, const u8 *new, int bytes, struct kvm_page_track_notifier_node *node); + /* + * It is called when memory slot is being created + * + * @kvm: the kvm where memory slot being moved or removed + * @slot: the memory slot being moved or removed + * @npages: the number of pages + * @node: this node + */ + void (*track_create_slot)(struct kvm *kvm, struct kvm_memory_slot *slot, + unsigned long npages, + struct kvm_page_track_notifier_node *node); /* * It is called when memory slot is being moved or removed * users can drop write-protection for the pages in that memory slot @@ -52,7 +63,7 @@ void kvm_page_track_init(struct kvm *kvm); void kvm_page_track_cleanup(struct kvm *kvm); void kvm_page_track_free_memslot(struct kvm_memory_slot *slot); -int kvm_page_track_create_memslot(struct kvm_memory_slot *slot, +int kvm_page_track_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, unsigned long npages); void kvm_slot_page_track_add_page(struct kvm *kvm, diff --git a/arch/x86/kvm/mmu/page_track.c b/arch/x86/kvm/mmu/page_track.c index d7a591a85af8..27a8d1a02e84 100644 --- a/arch/x86/kvm/mmu/page_track.c +++ b/arch/x86/kvm/mmu/page_track.c @@ -28,9 +28,12 @@ void kvm_page_track_free_memslot(struct kvm_memory_slot *slot) } } -int kvm_page_track_create_memslot(struct kvm_memory_slot *slot, +int kvm_page_track_create_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, unsigned long npages) { + struct kvm_page_track_notifier_head *head; + struct kvm_page_track_notifier_node *n; + int idx; int i; for (i = 0; i < KVM_PAGE_TRACK_MAX; i++) { @@ -41,6 +44,18 @@ int kvm_page_track_create_memslot(struct kvm_memory_slot *slot, goto track_free; } + head = &kvm->arch.track_notifier_head; + + if (hlist_empty(&head->track_notifier_list)) + return 0; + + idx = srcu_read_lock(&head->track_srcu); + hlist_for_each_entry_srcu(n, &head->track_notifier_list, node, + srcu_read_lock_held(&head->track_srcu)) + if (n->track_create_slot) + n->track_create_slot(kvm, slot, npages, n); + srcu_read_unlock(&head->track_srcu, idx); + return 0; track_free: diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index c2f13a275448..4d19da016c12 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -10524,7 +10524,8 @@ void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) kvm_page_track_free_memslot(slot); } -static int kvm_alloc_memslot_metadata(struct kvm_memory_slot *slot, +static int kvm_alloc_memslot_metadata(struct kvm *kvm, + struct kvm_memory_slot *slot, unsigned long npages) { int i; @@ -10576,7 +10577,7 @@ static int kvm_alloc_memslot_metadata(struct kvm_memory_slot *slot, } } - if (kvm_page_track_create_memslot(slot, npages)) + if (kvm_page_track_create_memslot(kvm, slot, npages)) goto out_free; return 0; @@ -10616,7 +10617,7 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, enum kvm_mr_change change) { if (change == KVM_MR_CREATE || change == KVM_MR_MOVE) - return kvm_alloc_memslot_metadata(memslot, + return kvm_alloc_memslot_metadata(kvm, memslot, mem->memory_size >> PAGE_SHIFT); return 0; } From patchwork Wed Nov 25 09:35:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930913 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C45BFC83018 for ; Wed, 25 Nov 2020 09:42:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 7434821D7F for ; Wed, 25 Nov 2020 09:42:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728791AbgKYJmz (ORCPT ); Wed, 25 Nov 2020 04:42:55 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57136 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727957AbgKYJl7 (ORCPT ); Wed, 25 Nov 2020 04:41:59 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 67FDE305D50F; Wed, 25 Nov 2020 11:35:47 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 40A813072785; Wed, 25 Nov 2020 11:35:47 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 29/81] KVM: x86: page_track: add support for preread, prewrite and preexec Date: Wed, 25 Nov 2020 11:35:08 +0200 Message-Id: <20201125093600.2766-30-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu The access to a tracked memory page leads to two types of actions from the introspection tool: either the access is allowed (maybe with different data for the source operand) or the vCPU should re-enter in guest (the page is not tracked anymore, the instruction was skipped/emulated by the introspection tool, etc.). These new callbacks must return 'true' for the first case and 'false' for the second. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_page_track.h | 48 +++++++++- arch/x86/kvm/mmu/mmu.c | 81 +++++++++++++++++ arch/x86/kvm/mmu/mmu_internal.h | 4 + arch/x86/kvm/mmu/page_track.c | 123 ++++++++++++++++++++++++-- 4 files changed, 246 insertions(+), 10 deletions(-) diff --git a/arch/x86/include/asm/kvm_page_track.h b/arch/x86/include/asm/kvm_page_track.h index 00a66c4d4d3c..c10f0f65c77a 100644 --- a/arch/x86/include/asm/kvm_page_track.h +++ b/arch/x86/include/asm/kvm_page_track.h @@ -3,7 +3,10 @@ #define _ASM_X86_KVM_PAGE_TRACK_H enum kvm_page_track_mode { + KVM_PAGE_TRACK_PREREAD, + KVM_PAGE_TRACK_PREWRITE, KVM_PAGE_TRACK_WRITE, + KVM_PAGE_TRACK_PREEXEC, KVM_PAGE_TRACK_MAX, }; @@ -22,6 +25,33 @@ struct kvm_page_track_notifier_head { struct kvm_page_track_notifier_node { struct hlist_node node; + /* + * It is called when guest is reading the read-tracked page + * and the read emulation is about to happen. + * + * @vcpu: the vcpu where the read access happened. + * @gpa: the physical address read by guest. + * @gva: the virtual address read by guest. + * @bytes: the read length. + * @node: this node. + */ + bool (*track_preread)(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + int bytes, + struct kvm_page_track_notifier_node *node); + /* + * It is called when guest is writing the write-tracked page + * and the write emulation didn't happened yet. + * + * @vcpu: the vcpu where the write access happened. + * @gpa: the physical address written by guest. + * @gva: the virtual address written by guest. + * @new: the data was written to the address. + * @bytes: the written length. + * @node: this node + */ + bool (*track_prewrite)(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + const u8 *new, int bytes, + struct kvm_page_track_notifier_node *node); /* * It is called when guest is writing the write-tracked page * and write emulation is finished at that time. @@ -36,6 +66,17 @@ struct kvm_page_track_notifier_node { void (*track_write)(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, const u8 *new, int bytes, struct kvm_page_track_notifier_node *node); + /* + * It is called when guest is fetching from a exec-tracked page + * and the fetch emulation is about to happen. + * + * @vcpu: the vcpu where the fetch access happened. + * @gpa: the physical address fetched by guest. + * @gva: the virtual address fetched by guest. + * @node: this node. + */ + bool (*track_preexec)(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + struct kvm_page_track_notifier_node *node); /* * It is called when memory slot is being created * @@ -49,7 +90,7 @@ struct kvm_page_track_notifier_node { struct kvm_page_track_notifier_node *node); /* * It is called when memory slot is being moved or removed - * users can drop write-protection for the pages in that memory slot + * users can drop active protection for the pages in that memory slot * * @kvm: the kvm where memory slot being moved or removed * @slot: the memory slot being moved or removed @@ -81,7 +122,12 @@ kvm_page_track_register_notifier(struct kvm *kvm, void kvm_page_track_unregister_notifier(struct kvm *kvm, struct kvm_page_track_notifier_node *n); +bool kvm_page_track_preread(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + int bytes); +bool kvm_page_track_prewrite(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + const u8 *new, int bytes); void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, const u8 *new, int bytes); +bool kvm_page_track_preexec(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva); void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot); #endif diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 1631e2367085..36add6fb712f 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -1124,6 +1124,31 @@ static bool spte_write_protect(u64 *sptep, bool pt_protect) return mmu_spte_update(sptep, spte); } +static bool spte_read_protect(u64 *sptep) +{ + u64 spte = *sptep; + bool exec_only_supported = (shadow_present_mask == 0ull); + + rmap_printk("rmap_read_protect: spte %p %llx\n", sptep, *sptep); + + WARN_ON_ONCE(!exec_only_supported); + + spte = spte & ~(PT_WRITABLE_MASK | PT_PRESENT_MASK); + + return mmu_spte_update(sptep, spte); +} + +static bool spte_exec_protect(u64 *sptep) +{ + u64 spte = *sptep; + + rmap_printk("rmap_exec_protect: spte %p %llx\n", sptep, *sptep); + + spte = spte & ~PT_USER_MASK; + + return mmu_spte_update(sptep, spte); +} + static bool __rmap_write_protect(struct kvm *kvm, struct kvm_rmap_head *rmap_head, bool pt_protect) @@ -1138,6 +1163,32 @@ static bool __rmap_write_protect(struct kvm *kvm, return flush; } +static bool __rmap_read_protect(struct kvm *kvm, + struct kvm_rmap_head *rmap_head) +{ + struct rmap_iterator iter; + bool flush = false; + u64 *sptep; + + for_each_rmap_spte(rmap_head, &iter, sptep) + flush |= spte_read_protect(sptep); + + return flush; +} + +static bool __rmap_exec_protect(struct kvm *kvm, + struct kvm_rmap_head *rmap_head) +{ + struct rmap_iterator iter; + bool flush = false; + u64 *sptep; + + for_each_rmap_spte(rmap_head, &iter, sptep) + flush |= spte_exec_protect(sptep); + + return flush; +} + static bool spte_clear_dirty(u64 *sptep) { u64 spte = *sptep; @@ -1316,6 +1367,36 @@ bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm, return write_protected; } +bool kvm_mmu_slot_gfn_read_protect(struct kvm *kvm, + struct kvm_memory_slot *slot, u64 gfn) +{ + struct kvm_rmap_head *rmap_head; + bool read_protected = false; + int i; + + for (i = PG_LEVEL_4K; i <= KVM_MAX_HUGEPAGE_LEVEL; ++i) { + rmap_head = __gfn_to_rmap(gfn, i, slot); + read_protected |= __rmap_read_protect(kvm, rmap_head); + } + + return read_protected; +} + +bool kvm_mmu_slot_gfn_exec_protect(struct kvm *kvm, + struct kvm_memory_slot *slot, u64 gfn) +{ + struct kvm_rmap_head *rmap_head; + bool exec_protected = false; + int i; + + for (i = PG_LEVEL_4K; i <= KVM_MAX_HUGEPAGE_LEVEL; ++i) { + rmap_head = __gfn_to_rmap(gfn, i, slot); + exec_protected |= __rmap_exec_protect(kvm, rmap_head); + } + + return exec_protected; +} + static bool rmap_write_protect(struct kvm_vcpu *vcpu, u64 gfn) { struct kvm_memory_slot *slot; diff --git a/arch/x86/kvm/mmu/mmu_internal.h b/arch/x86/kvm/mmu/mmu_internal.h index bfc6389edc28..6fa15728f6ca 100644 --- a/arch/x86/kvm/mmu/mmu_internal.h +++ b/arch/x86/kvm/mmu/mmu_internal.h @@ -92,6 +92,10 @@ void kvm_mmu_gfn_disallow_lpage(struct kvm_memory_slot *slot, gfn_t gfn); void kvm_mmu_gfn_allow_lpage(struct kvm_memory_slot *slot, gfn_t gfn); bool kvm_mmu_slot_gfn_write_protect(struct kvm *kvm, struct kvm_memory_slot *slot, u64 gfn); +bool kvm_mmu_slot_gfn_read_protect(struct kvm *kvm, + struct kvm_memory_slot *slot, u64 gfn); +bool kvm_mmu_slot_gfn_exec_protect(struct kvm *kvm, + struct kvm_memory_slot *slot, u64 gfn); void kvm_flush_remote_tlbs_with_address(struct kvm *kvm, u64 start_gfn, u64 pages); diff --git a/arch/x86/kvm/mmu/page_track.c b/arch/x86/kvm/mmu/page_track.c index 27a8d1a02e84..ebbfd99a69da 100644 --- a/arch/x86/kvm/mmu/page_track.c +++ b/arch/x86/kvm/mmu/page_track.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Support KVM gust page tracking + * Support KVM guest page tracking * * This feature allows us to track page access in guest. Currently, only * write access is tracked. @@ -96,7 +96,7 @@ static void update_gfn_track(struct kvm_memory_slot *slot, gfn_t gfn, * @kvm: the guest instance we are interested in. * @slot: the @gfn belongs to. * @gfn: the guest page. - * @mode: tracking mode, currently only write track is supported. + * @mode: tracking mode. */ void kvm_slot_page_track_add_page(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn, @@ -114,9 +114,16 @@ void kvm_slot_page_track_add_page(struct kvm *kvm, */ kvm_mmu_gfn_disallow_lpage(slot, gfn); - if (mode == KVM_PAGE_TRACK_WRITE) + if (mode == KVM_PAGE_TRACK_PREWRITE || mode == KVM_PAGE_TRACK_WRITE) { if (kvm_mmu_slot_gfn_write_protect(kvm, slot, gfn)) kvm_flush_remote_tlbs(kvm); + } else if (mode == KVM_PAGE_TRACK_PREREAD) { + if (kvm_mmu_slot_gfn_read_protect(kvm, slot, gfn)) + kvm_flush_remote_tlbs(kvm); + } else if (mode == KVM_PAGE_TRACK_PREEXEC) { + if (kvm_mmu_slot_gfn_exec_protect(kvm, slot, gfn)) + kvm_flush_remote_tlbs(kvm); + } } EXPORT_SYMBOL_GPL(kvm_slot_page_track_add_page); @@ -131,7 +138,7 @@ EXPORT_SYMBOL_GPL(kvm_slot_page_track_add_page); * @kvm: the guest instance we are interested in. * @slot: the @gfn belongs to. * @gfn: the guest page. - * @mode: tracking mode, currently only write track is supported. + * @mode: tracking mode. */ void kvm_slot_page_track_remove_page(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn, @@ -224,12 +231,80 @@ kvm_page_track_unregister_notifier(struct kvm *kvm, } EXPORT_SYMBOL_GPL(kvm_page_track_unregister_notifier); +/* + * Notify the node that a read access is about to happen. Returning false + * doesn't stop the other nodes from being called, but it will stop + * the emulation. + * + * The node should figure out if the read page is the one that the node + * is interested in by itself. + * + * The nodes will always be in conflict if they track the same page: + * - accepting a read won't guarantee that the next node will not override + * the data (filling new/bytes and setting data_ready) + * - filling new/bytes with custom data won't guarantee that the next node + * will not override that + */ +bool kvm_page_track_preread(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + int bytes) +{ + struct kvm_page_track_notifier_head *head; + struct kvm_page_track_notifier_node *n; + int idx; + bool ret = true; + + head = &vcpu->kvm->arch.track_notifier_head; + + if (hlist_empty(&head->track_notifier_list)) + return ret; + + idx = srcu_read_lock(&head->track_srcu); + hlist_for_each_entry_srcu(n, &head->track_notifier_list, node, + srcu_read_lock_held(&head->track_srcu)) + if (n->track_preread) + if (!n->track_preread(vcpu, gpa, gva, bytes, n)) + ret = false; + srcu_read_unlock(&head->track_srcu, idx); + return ret; +} + +/* + * Notify the node that a write access is about to happen. Returning false + * doesn't stop the other nodes from being called, but it will stop + * the emulation. + * + * The node should figure out if the written page is the one that the node + * is interested in by itself. + */ +bool kvm_page_track_prewrite(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + const u8 *new, int bytes) +{ + struct kvm_page_track_notifier_head *head; + struct kvm_page_track_notifier_node *n; + int idx; + bool ret = true; + + head = &vcpu->kvm->arch.track_notifier_head; + + if (hlist_empty(&head->track_notifier_list)) + return ret; + + idx = srcu_read_lock(&head->track_srcu); + hlist_for_each_entry_srcu(n, &head->track_notifier_list, node, + srcu_read_lock_held(&head->track_srcu)) + if (n->track_prewrite) + if (!n->track_prewrite(vcpu, gpa, gva, new, bytes, n)) + ret = false; + srcu_read_unlock(&head->track_srcu, idx); + return ret; +} + /* * Notify the node that write access is intercepted and write emulation is * finished at this time. * - * The node should figure out if the written page is the one that node is - * interested in by itself. + * The node should figure out if the written page is the one that the node + * is interested in by itself. */ void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, const u8 *new, int bytes) @@ -251,12 +326,42 @@ void kvm_page_track_write(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, srcu_read_unlock(&head->track_srcu, idx); } +/* + * Notify the node that an instruction is about to be executed. + * Returning false doesn't stop the other nodes from being called, + * but it will stop the emulation with X86EMUL_RETRY_INSTR. + * + * The node should figure out if the page is the one that the node + * is interested in by itself. + */ +bool kvm_page_track_preexec(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva) +{ + struct kvm_page_track_notifier_head *head; + struct kvm_page_track_notifier_node *n; + int idx; + bool ret = true; + + head = &vcpu->kvm->arch.track_notifier_head; + + if (hlist_empty(&head->track_notifier_list)) + return ret; + + idx = srcu_read_lock(&head->track_srcu); + hlist_for_each_entry_srcu(n, &head->track_notifier_list, node, + srcu_read_lock_held(&head->track_srcu)) + if (n->track_preexec) + if (!n->track_preexec(vcpu, gpa, gva, n)) + ret = false; + srcu_read_unlock(&head->track_srcu, idx); + return ret; +} + /* * Notify the node that memory slot is being removed or moved so that it can - * drop write-protection for the pages in the memory slot. + * drop active protection for the pages in the memory slot. * - * The node should figure out it has any write-protected pages in this slot - * by itself. + * The node should figure out if the page is the one that the node + * is interested in by itself. */ void kvm_page_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot) { From patchwork Wed Nov 25 09:35:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930901 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 97EE8C83010 for ; Wed, 25 Nov 2020 09:42:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 58D1F2083E for ; Wed, 25 Nov 2020 09:42:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728751AbgKYJmq (ORCPT ); Wed, 25 Nov 2020 04:42:46 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57200 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727838AbgKYJmB (ORCPT ); Wed, 25 Nov 2020 04:42:01 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 9A9C9305D510; Wed, 25 Nov 2020 11:35:47 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 6A6283072784; Wed, 25 Nov 2020 11:35:47 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , Marian Rotariu , Stefan Sicleru , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 30/81] KVM: x86: wire in the preread/prewrite/preexec page trackers Date: Wed, 25 Nov 2020 11:35:09 +0200 Message-Id: <20201125093600.2766-31-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu These are needed in order to notify the introspection tool when read/write/execute access happens on one of the tracked memory pages. Also, this patch adds the case when the introspection tool requests that the vCPU re-enter in guest (and abort the emulation of the current instruction). Signed-off-by: Mihai Donțu Co-developed-by: Marian Rotariu Signed-off-by: Marian Rotariu Co-developed-by: Stefan Sicleru Signed-off-by: Stefan Sicleru Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/emulate.c | 4 ++++ arch/x86/kvm/kvm_emulate.h | 1 + arch/x86/kvm/mmu/mmu.c | 38 +++++++++++++++++++++----------- arch/x86/kvm/mmu/spte.c | 17 ++++++++++++++ arch/x86/kvm/x86.c | 45 ++++++++++++++++++++++++++++++-------- 5 files changed, 83 insertions(+), 22 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index 56cae1ff9e3f..ad32632d4dcb 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -5474,6 +5474,8 @@ int x86_decode_insn(struct x86_emulate_ctxt *ctxt, void *insn, int insn_len) ctxt->memopp->addr.mem.ea + ctxt->_eip); done: + if (rc == X86EMUL_RETRY_INSTR) + return EMULATION_RETRY_INSTR; if (rc == X86EMUL_PROPAGATE_FAULT) ctxt->have_exception = true; return (rc != X86EMUL_CONTINUE) ? EMULATION_FAILED : EMULATION_OK; @@ -5845,6 +5847,8 @@ int x86_emulate_insn(struct x86_emulate_ctxt *ctxt) if (rc == X86EMUL_INTERCEPTED) return EMULATION_INTERCEPTED; + if (rc == X86EMUL_RETRY_INSTR) + return EMULATION_RETRY_INSTR; if (rc == X86EMUL_CONTINUE) writeback_registers(ctxt); diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index 43c93ffa76ed..5bfab8d65cd1 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -496,6 +496,7 @@ bool x86_page_table_writing_insn(struct x86_emulate_ctxt *ctxt); #define EMULATION_OK 0 #define EMULATION_RESTART 1 #define EMULATION_INTERCEPTED 2 +#define EMULATION_RETRY_INSTR 3 void init_decode_cache(struct x86_emulate_ctxt *ctxt); int x86_emulate_insn(struct x86_emulate_ctxt *ctxt); int emulator_task_switch(struct x86_emulate_ctxt *ctxt, diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 36add6fb712f..23b72532cd18 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -769,9 +769,13 @@ static void account_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp) slot = __gfn_to_memslot(slots, gfn); /* the non-leaf shadow pages are keeping readonly. */ - if (sp->role.level > PG_LEVEL_4K) - return kvm_slot_page_track_add_page(kvm, slot, gfn, - KVM_PAGE_TRACK_WRITE); + if (sp->role.level > PG_LEVEL_4K) { + kvm_slot_page_track_add_page(kvm, slot, gfn, + KVM_PAGE_TRACK_PREWRITE); + kvm_slot_page_track_add_page(kvm, slot, gfn, + KVM_PAGE_TRACK_WRITE); + return; + } kvm_mmu_gfn_disallow_lpage(slot, gfn); } @@ -797,9 +801,13 @@ static void unaccount_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp) gfn = sp->gfn; slots = kvm_memslots_for_spte_role(kvm, sp->role); slot = __gfn_to_memslot(slots, gfn); - if (sp->role.level > PG_LEVEL_4K) - return kvm_slot_page_track_remove_page(kvm, slot, gfn, - KVM_PAGE_TRACK_WRITE); + if (sp->role.level > PG_LEVEL_4K) { + kvm_slot_page_track_remove_page(kvm, slot, gfn, + KVM_PAGE_TRACK_PREWRITE); + kvm_slot_page_track_remove_page(kvm, slot, gfn, + KVM_PAGE_TRACK_WRITE); + return; + } kvm_mmu_gfn_allow_lpage(slot, gfn); } @@ -2601,7 +2609,8 @@ bool mmu_need_write_protect(struct kvm_vcpu *vcpu, gfn_t gfn, { struct kvm_mmu_page *sp; - if (kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_WRITE)) + if (kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_PREWRITE) || + kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_WRITE)) return true; for_each_gfn_indirect_valid_sp(vcpu->kvm, sp, gfn) { @@ -3689,15 +3698,18 @@ static bool page_fault_handle_page_track(struct kvm_vcpu *vcpu, if (unlikely(error_code & PFERR_RSVD_MASK)) return false; - if (!(error_code & PFERR_PRESENT_MASK) || - !(error_code & PFERR_WRITE_MASK)) - return false; - /* - * guest is writing the page which is write tracked which can + * guest is reading/writing/fetching the page which is + * read/write/execute tracked which can * not be fixed by page fault handler. */ - if (kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_WRITE)) + if (((error_code & PFERR_USER_MASK) + && kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_PREREAD)) + || ((error_code & PFERR_WRITE_MASK) + && (kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_PREWRITE) + || kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_WRITE))) + || ((error_code & PFERR_FETCH_MASK) + && kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_PREEXEC))) return true; return false; diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index fcac2cac78fe..9225b80ab209 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -81,6 +81,21 @@ static bool kvm_is_mmio_pfn(kvm_pfn_t pfn) E820_TYPE_RAM); } +static unsigned int kvm_mmu_apply_introspection_access(struct kvm_vcpu *vcpu, + gfn_t gfn, + unsigned int acc) +{ + if (kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_PREREAD)) + acc &= ~ACC_USER_MASK; + if (kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_PREWRITE) || + kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_WRITE)) + acc &= ~ACC_WRITE_MASK; + if (kvm_page_track_is_active(vcpu, gfn, KVM_PAGE_TRACK_PREEXEC)) + acc &= ~ACC_EXEC_MASK; + + return acc; +} + int make_spte(struct kvm_vcpu *vcpu, unsigned int pte_access, int level, gfn_t gfn, kvm_pfn_t pfn, u64 old_spte, bool speculative, bool can_unsync, bool host_writable, bool ad_disabled, @@ -89,6 +104,8 @@ int make_spte(struct kvm_vcpu *vcpu, unsigned int pte_access, int level, u64 spte = 0; int ret = 0; + pte_access = kvm_mmu_apply_introspection_access(vcpu, gfn, pte_access); + if (ad_disabled) spte |= SPTE_AD_DISABLED_MASK; else if (kvm_vcpu_ad_need_write_protect(vcpu)) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4d19da016c12..a82db6b30aee 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -5911,6 +5911,8 @@ static int kvm_read_guest_virt_helper(gva_t addr, void *val, unsigned int bytes, if (gpa == UNMAPPED_GVA) return X86EMUL_PROPAGATE_FAULT; + if (!kvm_page_track_preread(vcpu, gpa, addr, toread)) + return X86EMUL_RETRY_INSTR; ret = kvm_vcpu_read_guest_page(vcpu, gpa >> PAGE_SHIFT, data, offset, toread); if (ret < 0) { @@ -5942,6 +5944,9 @@ static int kvm_fetch_guest_virt(struct x86_emulate_ctxt *ctxt, if (unlikely(gpa == UNMAPPED_GVA)) return X86EMUL_PROPAGATE_FAULT; + if (!kvm_page_track_preexec(vcpu, gpa, addr)) + return X86EMUL_RETRY_INSTR; + offset = addr & (PAGE_SIZE-1); if (WARN_ON(offset + bytes > PAGE_SIZE)) bytes = (unsigned)PAGE_SIZE - offset; @@ -6010,11 +6015,14 @@ static int kvm_write_guest_virt_helper(gva_t addr, void *val, unsigned int bytes if (gpa == UNMAPPED_GVA) return X86EMUL_PROPAGATE_FAULT; + if (!kvm_page_track_prewrite(vcpu, gpa, addr, data, towrite)) + return X86EMUL_RETRY_INSTR; ret = kvm_vcpu_write_guest(vcpu, gpa, data, towrite); if (ret < 0) { r = X86EMUL_IO_NEEDED; goto out; } + kvm_page_track_write(vcpu, gpa, addr, data, towrite); bytes -= towrite; data += towrite; @@ -6118,13 +6126,22 @@ static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva, int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, const void *val, int bytes) { - int ret; - - ret = kvm_vcpu_write_guest(vcpu, gpa, val, bytes); - if (ret < 0) - return 0; + if (!kvm_page_track_prewrite(vcpu, gpa, gva, val, bytes)) + return X86EMUL_RETRY_INSTR; + if (kvm_vcpu_write_guest(vcpu, gpa, val, bytes) < 0) + return X86EMUL_UNHANDLEABLE; kvm_page_track_write(vcpu, gpa, gva, val, bytes); - return 1; + return X86EMUL_CONTINUE; +} + +static int emulator_read_phys(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + void *val, int bytes) +{ + if (!kvm_page_track_preread(vcpu, gpa, gva, bytes)) + return X86EMUL_RETRY_INSTR; + if (kvm_vcpu_read_guest(vcpu, gpa, val, bytes) < 0) + return X86EMUL_UNHANDLEABLE; + return X86EMUL_CONTINUE; } struct read_write_emulator_ops { @@ -6154,7 +6171,7 @@ static int read_prepare(struct kvm_vcpu *vcpu, void *val, int bytes) static int read_emulate(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, void *val, int bytes) { - return !kvm_vcpu_read_guest(vcpu, gpa, val, bytes); + return emulator_read_phys(vcpu, gpa, gva, val, bytes); } static int write_emulate(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, @@ -6228,8 +6245,11 @@ static int emulator_read_write_onepage(unsigned long addr, void *val, return X86EMUL_PROPAGATE_FAULT; } - if (!ret && ops->read_write_emulate(vcpu, gpa, addr, val, bytes)) - return X86EMUL_CONTINUE; + if (!ret) { + ret = ops->read_write_emulate(vcpu, gpa, addr, val, bytes); + if (ret == X86EMUL_CONTINUE || ret == X86EMUL_RETRY_INSTR) + return ret; + } /* * Is this MMIO handled locally? @@ -6373,6 +6393,9 @@ static int emulator_cmpxchg_emulated(struct x86_emulate_ctxt *ctxt, if (kvm_vcpu_map(vcpu, gpa_to_gfn(gpa), &map)) goto emul_write; + if (!kvm_page_track_prewrite(vcpu, gpa, addr, new, bytes)) + return X86EMUL_RETRY_INSTR; + kaddr = map.hva + offset_in_page(gpa); switch (bytes) { @@ -7323,6 +7346,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, trace_kvm_emulate_insn_start(vcpu); ++vcpu->stat.insn_emulation; + if (r == EMULATION_RETRY_INSTR) + return 1; if (r != EMULATION_OK) { if ((emulation_type & EMULTYPE_TRAP_UD) || (emulation_type & EMULTYPE_TRAP_UD_FORCED)) { @@ -7392,6 +7417,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, r = x86_emulate_insn(ctxt); + if (r == EMULATION_RETRY_INSTR) + return 1; if (r == EMULATION_INTERCEPTED) return 1; From patchwork Wed Nov 25 09:35:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930851 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C2E59C64E7C for ; Wed, 25 Nov 2020 09:42:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 794D22145D for ; Wed, 25 Nov 2020 09:42:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728668AbgKYJmX (ORCPT ); Wed, 25 Nov 2020 04:42:23 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57190 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728560AbgKYJmG (ORCPT ); Wed, 25 Nov 2020 04:42:06 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id BCD52305D511; Wed, 25 Nov 2020 11:35:47 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 9BC703072785; Wed, 25 Nov 2020 11:35:47 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mircea_C=C3=AErjaliu?= , =?utf-8?q?Ada?= =?utf-8?q?lbert_Laz=C4=83r?= Subject: [PATCH v10 31/81] KVM: x86: disable gpa_available optimization for fetch and page-walk SPT violations Date: Wed, 25 Nov 2020 11:35:10 +0200 Message-Id: <20201125093600.2766-32-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mircea Cîrjaliu This change is needed because the introspection tool can write-protect guest page tables or exec-protect heap/stack pages. Signed-off-by: Mircea Cîrjaliu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 5 +++++ arch/x86/kvm/mmu/mmu.c | 7 +++++++ arch/x86/kvm/x86.c | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 0342835a79d2..46849b92f937 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1448,6 +1448,10 @@ extern u64 kvm_mce_cap_supported; * retry native execution under certain conditions, * Can only be set in conjunction with EMULTYPE_PF. * + * EMULTYPE_GPA_AVAILABLE_PF - Set when the emulator can avoid a page walk + * to get the GPA. + * Can only be set in conjunction with EMULTYPE_PF. + * * EMULTYPE_TRAP_UD_FORCED - Set when emulating an intercepted #UD that was * triggered by KVM's magic "force emulation" prefix, * which is opt in via module param (off by default). @@ -1470,6 +1474,7 @@ extern u64 kvm_mce_cap_supported; #define EMULTYPE_TRAP_UD_FORCED (1 << 4) #define EMULTYPE_VMWARE_GP (1 << 5) #define EMULTYPE_PF (1 << 6) +#define EMULTYPE_GPA_AVAILABLE_PF (1 << 7) int kvm_emulate_instruction(struct kvm_vcpu *vcpu, int emulation_type); int kvm_emulate_instruction_from_buffer(struct kvm_vcpu *vcpu, diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 23b72532cd18..f79cf58a27dc 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -5148,6 +5148,13 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code, if (WARN_ON(!VALID_PAGE(vcpu->arch.mmu->root_hpa))) return RET_PF_RETRY; + /* + * With shadow page tables, fault_address contains a GVA or nGPA. + * On a fetch fault, fault_address contains the instruction pointer. + */ + if (direct && likely(!(error_code & PFERR_FETCH_MASK)) && + (error_code & PFERR_GUEST_FINAL_MASK)) + emulation_type |= EMULTYPE_GPA_AVAILABLE_PF; r = RET_PF_INVALID; if (unlikely(error_code & PFERR_RSVD_MASK)) { diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index a82db6b30aee..d9b1034465c8 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -7406,7 +7406,7 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, ctxt->exception.address = cr2_or_gpa; /* With shadow page tables, cr2 contains a GVA or nGPA. */ - if (vcpu->arch.mmu->direct_map) { + if (emulation_type & EMULTYPE_GPA_AVAILABLE_PF) { ctxt->gpa_available = true; ctxt->gpa_val = cr2_or_gpa; } From patchwork Wed Nov 25 09:35:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930931 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A9A9DC64E7D for ; Wed, 25 Nov 2020 09:43:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 50DB02083E for ; Wed, 25 Nov 2020 09:43:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727569AbgKYJlz (ORCPT ); Wed, 25 Nov 2020 04:41:55 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57020 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726881AbgKYJly (ORCPT ); Wed, 25 Nov 2020 04:41:54 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id ECFCC305D512; Wed, 25 Nov 2020 11:35:47 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C272F3072784; Wed, 25 Nov 2020 11:35:47 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , Marian Rotariu , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 32/81] KVM: introduce VM introspection Date: Wed, 25 Nov 2020 11:35:11 +0200 Message-Id: <20201125093600.2766-33-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu The KVM introspection subsystem provides a facility for applications to control the execution of any running VMs (pause, resume, shutdown), query the state of the vCPUs (GPRs, MSRs etc.), alter the page access bits in the shadow page tables and receive notifications when events of interest have taken place (shadow page table level faults, key MSR writes, hypercalls etc.). Some notifications can be responded to with an action (like preventing an MSR from being written), others are mere informative (like breakpoint events which can be used for execution tracing). Signed-off-by: Mihai Donțu Co-developed-by: Marian Rotariu Signed-off-by: Marian Rotariu Signed-off-by: Adalbert Lazăr Reported-by: kernel test robot Reported-by: kernel test robot Reported-by: kernel test robot --- Documentation/virt/kvm/kvmi.rst | 139 ++++++++++++++++++++++++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/kvm/Kconfig | 9 ++ arch/x86/kvm/Makefile | 2 + arch/x86/kvm/x86.c | 3 + include/linux/kvmi_host.h | 21 +++++ virt/kvm/introspection/kvmi.c | 25 ++++++ virt/kvm/introspection/kvmi_int.h | 7 ++ virt/kvm/kvm_main.c | 12 +++ 9 files changed, 220 insertions(+) create mode 100644 Documentation/virt/kvm/kvmi.rst create mode 100644 include/linux/kvmi_host.h create mode 100644 virt/kvm/introspection/kvmi.c create mode 100644 virt/kvm/introspection/kvmi_int.h diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst new file mode 100644 index 000000000000..59cc33a39f9f --- /dev/null +++ b/Documentation/virt/kvm/kvmi.rst @@ -0,0 +1,139 @@ +.. SPDX-License-Identifier: GPL-2.0 + +========================================================= +KVMI - The kernel virtual machine introspection subsystem +========================================================= + +The KVM introspection subsystem provides a facility for applications running +on the host or in a separate VM, to control the execution of any running VMs +(pause, resume, shutdown), query the state of the vCPUs (GPRs, MSRs etc.), +alter the page access bits in the shadow page tables (only for the hardware +backed ones, eg. Intel's EPT) and receive notifications when events of +interest have taken place (shadow page table level faults, key MSR writes, +hypercalls etc.). Some notifications can be responded to with an action +(like preventing an MSR from being written), others are mere informative +(like breakpoint events which can be used for execution tracing). +With few exceptions, all events are optional. An application using this +subsystem will explicitly register for them. + +The use case that gave way for the creation of this subsystem is to monitor +the guest OS and as such the ABI/API is highly influenced by how the guest +software (kernel, applications) sees the world. For example, some events +provide information specific for the host CPU architecture +(eg. MSR_IA32_SYSENTER_EIP) merely because its leveraged by guest software +to implement a critical feature (fast system calls). + +At the moment, the target audience for KVMI are security software authors +that wish to perform forensics on newly discovered threats (exploits) or +to implement another layer of security like preventing a large set of +kernel rootkits simply by "locking" the kernel image in the shadow page +tables (ie. enforce .text r-x, .rodata rw- etc.). It's the latter case that +made KVMI a separate subsystem, even though many of these features are +available in the device manager (eg. QEMU). The ability to build a security +application that does not interfere (in terms of performance) with the +guest software asks for a specialized interface that is designed for minimum +overhead. + +API/ABI +======= + +This chapter describes the VMI interface used to monitor and control local +guests from a user application. + +Overview +-------- + +The interface is socket based, one connection for every VM. One end is in the +host kernel while the other is held by the user application (introspection +tool). + +The initial connection is established by an application running on the +host (eg. QEMU) that connects to the introspection tool and after a +handshake the file descriptor is passed to the host kernel making all +further communication take place between it and the introspection tool. + +The socket protocol allows for commands and events to be multiplexed over +the same connection. As such, it is possible for the introspection tool to +receive an event while waiting for the result of a command. Also, it can +send a command while the host kernel is waiting for a reply to an event. + +The kernel side of the socket communication is blocking and will wait +for an answer from its peer indefinitely or until the guest is powered +off (killed), restarted or the peer goes away, at which point it will +wake up and properly cleanup as if the introspection subsystem has never +been used on that guest (if requested). Obviously, whether the guest can +really continue normal execution depends on whether the introspection +tool has made any modifications that require an active KVMI channel. + +Handshake +--------- + +Although this falls out of the scope of the introspection subsystem, below +is a proposal of a handshake that can be used by implementors. + +Based on the system administration policies, the management tool +(eg. libvirt) starts device managers (eg. QEMU) with some extra arguments: +what introspection tool could monitor/control that specific guest (and +how to connect to) and what introspection commands/events are allowed. + +The device manager will connect to the introspection tool and wait for a +cryptographic hash of a cookie that should be known by both peers. If the +hash is correct (the destination has been "authenticated"), the device +manager will send another cryptographic hash and random salt. The peer +recomputes the hash of the cookie bytes including the salt and if they match, +the device manager has been "authenticated" too. This is a rather crude +system that makes it difficult for device manager exploits to trick the +introspection tool into believing its working OK. + +The cookie would normally be generated by a management tool (eg. libvirt) +and make it available to the device manager and to a properly authenticated +client. It is the job of a third party to retrieve the cookie from the +management application and pass it over a secure channel to the introspection +tool. + +Once the basic "authentication" has taken place, the introspection tool +can receive information on the guest (its UUID) and other flags (endianness +or features supported by the host kernel). + +In the end, the device manager will pass the file descriptor (plus +the allowed commands/events) to KVM. It will detect when the socket is +shutdown and it will reinitiate the handshake. + +Unhooking +--------- + +During a VMI session it is possible for the guest to be patched and for +some of these patches to "talk" with the introspection tool. It thus +becomes necessary to remove them before the guest is suspended, moved +(migrated) or a snapshot with memory is created. + +The actions are normally performed by the device manager. In the case +of QEMU, it will use another ioctl to notify the introspection tool and +wait for a limited amount of time (a few seconds) for a confirmation that +is OK to proceed (the introspection tool will close the connection). + +Live migrations +--------------- + +Before the live migration takes place, the introspection tool has to be +notified and have a chance to unhook (see **Unhooking**). + +The QEMU instance on the receiving end, if configured for KVMI, will need +to establish a connection to the introspection tool after the migration +has been completed. + +Obviously, this creates a window in which the guest is not introspected. +The user has to be aware of this detail. Future introspection technologies +can choose not to disconnect and instead transfer the necessary context +to the introspection tool at the migration destination via a separate +channel. + +Memory access safety +-------------------- + +The KVMI API gives access to the entire guest physical address space but +provides no information on which parts of it are system RAM and which are +device-specific memory (DMA, emulated MMIO, reserved by a passthrough +device etc.). It is up to the user to determine, using the guest operating +system data structures, the areas that are safe to access (code, stack, heap +etc.). diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 46849b92f937..7d1e865193a9 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1414,6 +1414,8 @@ void kvm_fire_mask_notifiers(struct kvm *kvm, unsigned irqchip, unsigned pin, extern bool tdp_enabled; +extern bool enable_introspection; + u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); /* control of guest tsc rate supported? */ diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index f92dfd8ef10d..dbffc829ec9a 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -109,4 +109,13 @@ config KVM_MMU_AUDIT This option adds a R/W kVM module parameter 'mmu_audit', which allows auditing of KVM MMU events at runtime. +config KVM_INTROSPECTION + bool "KVM Introspection" + depends on (KVM_INTEL || KVM_AMD) + default n + help + Provides the introspection interface, which allows the control + of any running VM. It must be explicitly enabled by setting + the module parameter 'kvm.introspection'. + endif # VIRTUALIZATION diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index 4bd14ab01323..e9784df055d4 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -8,11 +8,13 @@ OBJECT_FILES_NON_STANDARD_vmenter.o := y endif KVM := ../../../virt/kvm +KVMI := $(KVM)/introspection kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \ $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o \ $(KVM)/dirty_ring.o kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o +kvm-$(CONFIG_KVM_INTROSPECTION) += $(KVMI)/kvmi.o kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index d9b1034465c8..45221caeb84d 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -162,6 +162,9 @@ module_param(force_emulation_prefix, bool, S_IRUGO); int __read_mostly pi_inject_timer = -1; module_param(pi_inject_timer, bint, S_IRUGO | S_IWUSR); +bool __read_mostly enable_introspection; +module_param_named(introspection, enable_introspection, bool, 0444); + /* * Restoring the host value for MSRs that are only consumed when running in * usermode, e.g. SYSCALL MSRs and TSC_AUX, can be deferred until the CPU diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h new file mode 100644 index 000000000000..1e0a73c2a190 --- /dev/null +++ b/include/linux/kvmi_host.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __KVMI_HOST_H +#define __KVMI_HOST_H + +#ifdef CONFIG_KVM_INTROSPECTION + +int kvmi_init(void); +void kvmi_uninit(void); +void kvmi_create_vm(struct kvm *kvm); +void kvmi_destroy_vm(struct kvm *kvm); + +#else + +static inline int kvmi_init(void) { return 0; } +static inline void kvmi_uninit(void) { } +static inline void kvmi_create_vm(struct kvm *kvm) { } +static inline void kvmi_destroy_vm(struct kvm *kvm) { } + +#endif /* CONFIG_KVM_INTROSPECTION */ + +#endif diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c new file mode 100644 index 000000000000..af53bdcb7ec8 --- /dev/null +++ b/virt/kvm/introspection/kvmi.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KVM Introspection + * + * Copyright (C) 2017-2020 Bitdefender S.R.L. + * + */ +#include "kvmi_int.h" + +int kvmi_init(void) +{ + return 0; +} + +void kvmi_uninit(void) +{ +} + +void kvmi_create_vm(struct kvm *kvm) +{ +} + +void kvmi_destroy_vm(struct kvm *kvm) +{ +} diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h new file mode 100644 index 000000000000..bdb4228fda5b --- /dev/null +++ b/virt/kvm/introspection/kvmi_int.h @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef __KVMI_INT_H +#define __KVMI_INT_H + +#include + +#endif diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index e19dd6f92709..d995be2421eb 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -802,6 +803,9 @@ static struct kvm *kvm_create_vm(unsigned long type) if (r) goto out_err; + if (enable_introspection) + kvmi_create_vm(kvm); + mutex_lock(&kvm_lock); list_add(&kvm->vm_list, &vm_list); mutex_unlock(&kvm_lock); @@ -854,6 +858,8 @@ static void kvm_destroy_vm(struct kvm *kvm) int i; struct mm_struct *mm = kvm->mm; + if (enable_introspection) + kvmi_destroy_vm(kvm); kvm_uevent_notify_change(KVM_EVENT_DESTROY_VM, kvm); kvm_destroy_vm_debugfs(kvm); kvm_arch_sync_events(kvm); @@ -5003,6 +5009,11 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, r = kvm_vfio_ops_init(); WARN_ON(r); + if (enable_introspection) { + r = kvmi_init(); + WARN_ON(r); + } + return 0; out_unreg: @@ -5027,6 +5038,7 @@ EXPORT_SYMBOL_GPL(kvm_init); void kvm_exit(void) { + kvmi_uninit(); debugfs_remove_recursive(kvm_debugfs_dir); misc_deregister(&kvm_dev); kmem_cache_destroy(kvm_vcpu_cache); From patchwork Wed Nov 25 09:35:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930867 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A91A3C71156 for ; Wed, 25 Nov 2020 09:42:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5EE172083E for ; Wed, 25 Nov 2020 09:42:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728732AbgKYJmi (ORCPT ); Wed, 25 Nov 2020 04:42:38 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57094 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728295AbgKYJmE (ORCPT ); Wed, 25 Nov 2020 04:42:04 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 26ADF305D513; Wed, 25 Nov 2020 11:35:48 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id EE9EE3072785; Wed, 25 Nov 2020 11:35:47 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= , =?utf-8?q?Mircea_?= =?utf-8?q?C=C3=AErjaliu?= , =?utf-8?b?TmljdciZ?= =?utf-8?b?b3IgQ8OuyJt1?= Subject: [PATCH v10 33/81] KVM: introspection: add hook/unhook ioctls Date: Wed, 25 Nov 2020 11:35:12 +0200 Message-Id: <20201125093600.2766-34-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org On hook, a new thread is created to handle the messages coming from the introspection tool (commands or event replies). The VM related commands are handled by this thread, while the vCPU commands and events replies are dispatched to the vCPU threads. On unhook, the socket is shut down, which will signal: the receiving thread to quit (because it might be blocked in recvmsg()) and the introspection tool to clean up. The mutex is used to protect the 'kvm->kvmi' pointer when accessed through ioctls. The reference counter is incremented by the receiving thread (for its entire life time) and by the vCPU threads while sending events or handling commands. The completion objects is set when the reference counter reaches zero, allowing the unhook process to continue and free the introspection structures. Co-developed-by: Mircea Cîrjaliu Signed-off-by: Mircea Cîrjaliu Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/api.rst | 63 +++++++ arch/x86/include/asm/kvmi_host.h | 8 + arch/x86/kvm/Makefile | 2 +- arch/x86/kvm/x86.c | 5 + include/linux/kvm_host.h | 5 + include/linux/kvmi_host.h | 18 ++ include/uapi/linux/kvm.h | 10 ++ include/uapi/linux/kvmi.h | 13 ++ tools/testing/selftests/kvm/Makefile | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 87 ++++++++++ virt/kvm/introspection/kvmi.c | 159 ++++++++++++++++++ virt/kvm/introspection/kvmi_int.h | 10 ++ virt/kvm/introspection/kvmi_msg.c | 39 +++++ virt/kvm/kvm_main.c | 21 +++ 14 files changed, 440 insertions(+), 1 deletion(-) create mode 100644 arch/x86/include/asm/kvmi_host.h create mode 100644 include/uapi/linux/kvmi.h create mode 100644 tools/testing/selftests/kvm/x86_64/kvmi_test.c create mode 100644 virt/kvm/introspection/kvmi_msg.c diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 70254eaa5229..9b48be90ae7b 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -4825,6 +4825,58 @@ into user space. If a vCPU is in running state while this ioctl is invoked, the vCPU may experience inconsistent filtering behavior on MSR accesses. +4.127 KVM_INTROSPECTION_HOOK +---------------------------- + +:Capability: KVM_CAP_INTROSPECTION +:Architectures: x86 +:Type: vm ioctl +:Parameters: struct kvm_introspection (in) +:Returns: 0 on success, a negative value on error + +Errors: + + ====== ========================================================== + ENOMEM the memory allocation failed + EEXIST the VM is already introspected + EINVAL the file descriptor doesn't correspond to an active socket + EINVAL the padding is not zero + EPERM the introspection is disabled (kvm.introspection=0) + ====== ========================================================== + +This ioctl is used to enable the introspection of the current VM. + +:: + + struct kvm_introspection { + __s32 fd; + __u32 padding; + __u8 uuid[16]; + }; + +fd is the file descriptor of a socket connected to the introspection tool, + +padding must be zero (it might be used in the future), + +uuid is used for debug and error messages. + +4.128 KVM_INTROSPECTION_UNHOOK +------------------------------ + +:Capability: KVM_CAP_INTROSPECTION +:Architectures: x86 +:Type: vm ioctl +:Parameters: none +:Returns: 0 on success, a negative value on error + +Errors: + + ====== ========================================================== + EPERM the introspection is disabled (kvm.introspection=0) + ====== ========================================================== + +This ioctl is used to free all introspection structures +related to this VM. 5. The kvm_run structure ======================== @@ -6496,3 +6548,14 @@ KVM_GET_DIRTY_LOG and KVM_CLEAR_DIRTY_LOG. After enabling KVM_CAP_DIRTY_LOG_RING with an acceptable dirty ring size, the virtual machine will switch to ring-buffer dirty page tracking and further KVM_GET_DIRTY_LOG or KVM_CLEAR_DIRTY_LOG ioctls will fail. + +8.30 KVM_CAP_INTROSPECTION +-------------------------- + +:Architectures: x86 + +This capability indicates that KVM supports VM introspection +and it is enabled. + +The KVM_CHECK_EXTENSION ioctl returns the introspection API version +(a number larger than 0). diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h new file mode 100644 index 000000000000..38c398262913 --- /dev/null +++ b/arch/x86/include/asm/kvmi_host.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_KVMI_HOST_H +#define _ASM_X86_KVMI_HOST_H + +struct kvm_arch_introspection { +}; + +#endif /* _ASM_X86_KVMI_HOST_H */ diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index e9784df055d4..db4121b4112d 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -14,7 +14,7 @@ kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \ $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o \ $(KVM)/dirty_ring.o kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o -kvm-$(CONFIG_KVM_INTROSPECTION) += $(KVMI)/kvmi.o +kvm-$(CONFIG_KVM_INTROSPECTION) += $(KVMI)/kvmi.o $(KVMI)/kvmi_msg.o kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 45221caeb84d..fcf7e68cb6c8 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -57,6 +57,7 @@ #include #include #include +#include #include @@ -3828,6 +3829,10 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_STEAL_TIME: r = sched_info_on(); break; + case KVM_CAP_INTROSPECTION: + if (enable_introspection) + r = kvmi_version(); + break; default: break; } diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index db04dab23013..51e6a4d7e5c9 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -513,6 +513,11 @@ struct kvm { pid_t userspace_pid; unsigned int max_halt_poll_ns; u32 dirty_ring_size; + + struct mutex kvmi_lock; + refcount_t kvmi_ref; + struct completion kvmi_complete; + struct kvm_introspection *kvmi; }; #define kvm_err(fmt, ...) \ diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 1e0a73c2a190..8574b9688736 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -4,13 +4,31 @@ #ifdef CONFIG_KVM_INTROSPECTION +#include + +struct kvm_introspection { + struct kvm_arch_introspection arch; + struct kvm *kvm; + + uuid_t uuid; + + struct socket *sock; + struct task_struct *recv; +}; + +int kvmi_version(void); int kvmi_init(void); void kvmi_uninit(void); void kvmi_create_vm(struct kvm *kvm); void kvmi_destroy_vm(struct kvm *kvm); +int kvmi_ioctl_hook(struct kvm *kvm, + const struct kvm_introspection_hook *hook); +int kvmi_ioctl_unhook(struct kvm *kvm); + #else +static inline int kvmi_version(void) { return 0; } static inline int kvmi_init(void) { return 0; } static inline void kvmi_uninit(void) { } static inline void kvmi_create_vm(struct kvm *kvm) { } diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 886802b8ffba..a0be0ea5b13f 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1056,6 +1056,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_ENFORCE_PV_FEATURE_CPUID 190 #define KVM_CAP_SYS_HYPERV_CPUID 191 #define KVM_CAP_DIRTY_LOG_RING 192 +#define KVM_CAP_INTROSPECTION 193 #ifdef KVM_CAP_IRQ_ROUTING @@ -1643,6 +1644,15 @@ struct kvm_sev_dbg { __u32 len; }; +struct kvm_introspection_hook { + __s32 fd; + __u32 padding; + __u8 uuid[16]; +}; + +#define KVM_INTROSPECTION_HOOK _IOW(KVMIO, 0xc8, struct kvm_introspection_hook) +#define KVM_INTROSPECTION_UNHOOK _IO(KVMIO, 0xc9) + #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h new file mode 100644 index 000000000000..34dda91016db --- /dev/null +++ b/include/uapi/linux/kvmi.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI__LINUX_KVMI_H +#define _UAPI__LINUX_KVMI_H + +/* + * KVMI structures and definitions + */ + +enum { + KVMI_VERSION = 0x00000001 +}; + +#endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 4febf4d5ead9..f6e9a442a0a0 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -42,6 +42,7 @@ TEST_GEN_PROGS_x86_64 = x86_64/cr4_cpuid_sync_test TEST_GEN_PROGS_x86_64 += x86_64/evmcs_test TEST_GEN_PROGS_x86_64 += x86_64/hyperv_cpuid TEST_GEN_PROGS_x86_64 += x86_64/kvm_pv_test +TEST_GEN_PROGS_x86_64 += x86_64/kvmi_test TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test TEST_GEN_PROGS_x86_64 += x86_64/set_sregs_test diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c new file mode 100644 index 000000000000..13d3e0a3af9d --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KVM introspection tests + * + * Copyright (C) 2020, Bitdefender S.R.L. + */ + +#define _GNU_SOURCE /* for program_invocation_short_name */ +#include +#include + +#include "test_util.h" + +#include "kvm_util.h" +#include "processor.h" +#include "../lib/kvm_util_internal.h" + +#include "linux/kvmi.h" + +#define VCPU_ID 1 + +static int socket_pair[2]; +#define Kvm_socket socket_pair[0] +#define Userspace_socket socket_pair[1] + +void setup_socket(void) +{ + int r; + + r = socketpair(AF_UNIX, SOCK_STREAM, 0, socket_pair); + TEST_ASSERT(r == 0, + "socketpair() failed, errno %d (%s)\n", + errno, strerror(errno)); +} + +static void do_hook_ioctl(struct kvm_vm *vm, __s32 fd, int expected_err) +{ + struct kvm_introspection_hook hook = { .fd = fd, }; + int r; + + r = ioctl(vm->fd, KVM_INTROSPECTION_HOOK, &hook); + TEST_ASSERT(r == 0 || errno == expected_err, + "KVM_INTROSPECTION_HOOK failed, errno %d (%s), expected %d, fd %d\n", + errno, strerror(errno), expected_err, fd); +} + +static void hook_introspection(struct kvm_vm *vm) +{ + do_hook_ioctl(vm, -1, EINVAL); + do_hook_ioctl(vm, Kvm_socket, 0); + do_hook_ioctl(vm, Kvm_socket, EEXIST); +} + +static void unhook_introspection(struct kvm_vm *vm) +{ + int r; + + r = ioctl(vm->fd, KVM_INTROSPECTION_UNHOOK, NULL); + TEST_ASSERT(r == 0, + "KVM_INTROSPECTION_UNHOOK failed, errno %d (%s)\n", + errno, strerror(errno)); +} + +static void test_introspection(struct kvm_vm *vm) +{ + setup_socket(); + hook_introspection(vm); + unhook_introspection(vm); +} + +int main(int argc, char *argv[]) +{ + struct kvm_vm *vm; + + if (!kvm_check_cap(KVM_CAP_INTROSPECTION)) { + print_skip("KVM_CAP_INTROSPECTION not available"); + exit(KSFT_SKIP); + } + + vm = vm_create_default(VCPU_ID, 0, NULL); + vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); + + test_introspection(vm); + + kvm_vm_free(vm); + return 0; +} diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index af53bdcb7ec8..3367023219b7 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -5,6 +5,7 @@ * Copyright (C) 2017-2020 Bitdefender S.R.L. * */ +#include #include "kvmi_int.h" int kvmi_init(void) @@ -12,14 +13,172 @@ int kvmi_init(void) return 0; } +int kvmi_version(void) +{ + return KVMI_VERSION; +} + void kvmi_uninit(void) { } +static void kvmi_free(struct kvm *kvm) +{ + kfree(kvm->kvmi); + kvm->kvmi = NULL; +} + +static struct kvm_introspection * +kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook) +{ + struct kvm_introspection *kvmi; + + kvmi = kzalloc(sizeof(*kvmi), GFP_KERNEL); + if (!kvmi) + return NULL; + + BUILD_BUG_ON(sizeof(hook->uuid) != sizeof(kvmi->uuid)); + memcpy(&kvmi->uuid, &hook->uuid, sizeof(kvmi->uuid)); + + kvmi->kvm = kvm; + + return kvmi; +} + +static void kvmi_destroy(struct kvm_introspection *kvmi) +{ + struct kvm *kvm = kvmi->kvm; + + kvmi_free(kvm); +} + +static void kvmi_stop_recv_thread(struct kvm_introspection *kvmi) +{ + kvmi_sock_shutdown(kvmi); +} + +static void __kvmi_unhook(struct kvm *kvm) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + + wait_for_completion_killable(&kvm->kvmi_complete); + kvmi_sock_put(kvmi); +} + +static void kvmi_unhook(struct kvm *kvm) +{ + struct kvm_introspection *kvmi; + + mutex_lock(&kvm->kvmi_lock); + + kvmi = KVMI(kvm); + if (kvmi) { + kvmi_stop_recv_thread(kvmi); + __kvmi_unhook(kvm); + kvmi_destroy(kvmi); + } + + mutex_unlock(&kvm->kvmi_lock); +} + +int kvmi_ioctl_unhook(struct kvm *kvm) +{ + kvmi_unhook(kvm); + return 0; +} + +void kvmi_put(struct kvm *kvm) +{ + if (refcount_dec_and_test(&kvm->kvmi_ref)) + complete(&kvm->kvmi_complete); +} + +static int __kvmi_hook(struct kvm *kvm, + const struct kvm_introspection_hook *hook) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + + if (!kvmi_sock_get(kvmi, hook->fd)) + return -EINVAL; + + return 0; +} + +static int kvmi_recv_thread(void *arg) +{ + struct kvm_introspection *kvmi = arg; + + while (kvmi_msg_process(kvmi)) + ; + + /* Signal userspace and prevent the vCPUs from sending events. */ + kvmi_sock_shutdown(kvmi); + + kvmi_put(kvmi->kvm); + return 0; +} + +int kvmi_hook(struct kvm *kvm, const struct kvm_introspection_hook *hook) +{ + struct kvm_introspection *kvmi; + int err = 0; + + mutex_lock(&kvm->kvmi_lock); + + if (kvm->kvmi) { + err = -EEXIST; + goto out; + } + + kvmi = kvmi_alloc(kvm, hook); + if (!kvmi) { + err = -ENOMEM; + goto out; + } + + kvm->kvmi = kvmi; + + err = __kvmi_hook(kvm, hook); + if (err) + goto destroy; + + init_completion(&kvm->kvmi_complete); + + refcount_set(&kvm->kvmi_ref, 1); + + kvmi->recv = kthread_run(kvmi_recv_thread, kvmi, "kvmi-recv"); + if (IS_ERR(kvmi->recv)) { + err = -ENOMEM; + kvmi_put(kvm); + goto unhook; + } + + goto out; + +unhook: + __kvmi_unhook(kvm); +destroy: + kvmi_destroy(kvmi); +out: + mutex_unlock(&kvm->kvmi_lock); + return err; +} + +int kvmi_ioctl_hook(struct kvm *kvm, + const struct kvm_introspection_hook *hook) +{ + if (hook->padding) + return -EINVAL; + + return kvmi_hook(kvm, hook); +} + void kvmi_create_vm(struct kvm *kvm) { + mutex_init(&kvm->kvmi_lock); } void kvmi_destroy_vm(struct kvm *kvm) { + kvmi_unhook(kvm); } diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index bdb4228fda5b..c89875bd2bac 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -3,5 +3,15 @@ #define __KVMI_INT_H #include +#include +#include + +#define KVMI(kvm) ((kvm)->kvmi) + +/* kvmi_msg.c */ +bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd); +void kvmi_sock_shutdown(struct kvm_introspection *kvmi); +void kvmi_sock_put(struct kvm_introspection *kvmi); +bool kvmi_msg_process(struct kvm_introspection *kvmi); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c new file mode 100644 index 000000000000..3ae52c61f861 --- /dev/null +++ b/virt/kvm/introspection/kvmi_msg.c @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KVM introspection (message handling) + * + * Copyright (C) 2017-2020 Bitdefender S.R.L. + * + */ +#include +#include "kvmi_int.h" + +bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd) +{ + struct socket *sock; + int err; + + sock = sockfd_lookup(fd, &err); + if (!sock) + return false; + + kvmi->sock = sock; + + return true; +} + +void kvmi_sock_put(struct kvm_introspection *kvmi) +{ + if (kvmi->sock) + sockfd_put(kvmi->sock); +} + +void kvmi_sock_shutdown(struct kvm_introspection *kvmi) +{ + kernel_sock_shutdown(kvmi->sock, SHUT_RDWR); +} + +bool kvmi_msg_process(struct kvm_introspection *kvmi) +{ + return false; +} diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index d995be2421eb..66fa749a2c6e 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -4015,6 +4015,27 @@ static long kvm_vm_ioctl(struct file *filp, case KVM_RESET_DIRTY_RINGS: r = kvm_vm_ioctl_reset_dirty_pages(kvm); break; +#ifdef CONFIG_KVM_INTROSPECTION + case KVM_INTROSPECTION_HOOK: { + struct kvm_introspection_hook hook; + + r = -EPERM; + if (!enable_introspection) + goto out; + + r = -EFAULT; + if (copy_from_user(&hook, argp, sizeof(hook))) + goto out; + + r = kvmi_ioctl_hook(kvm, &hook); + break; + } + case KVM_INTROSPECTION_UNHOOK: + r = -EPERM; + if (enable_introspection) + r = kvmi_ioctl_unhook(kvm); + break; +#endif /* CONFIG_KVM_INTROSPECTION */ default: r = kvm_arch_vm_ioctl(filp, ioctl, arg); } From patchwork Wed Nov 25 09:35:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930813 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EB790C63777 for ; Wed, 25 Nov 2020 09:42:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 95CAF20B1F for ; Wed, 25 Nov 2020 09:42:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728494AbgKYJmB (ORCPT ); Wed, 25 Nov 2020 04:42:01 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57188 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726881AbgKYJl7 (ORCPT ); Wed, 25 Nov 2020 04:41:59 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 502BD305D514; Wed, 25 Nov 2020 11:35:48 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 294093072784; Wed, 25 Nov 2020 11:35:48 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 34/81] KVM: introspection: add permission access ioctls Date: Wed, 25 Nov 2020 11:35:13 +0200 Message-Id: <20201125093600.2766-35-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org KVM_INTROSPECTION_COMMAND and KVM_INTROSPECTION_EVENTS ioctls are used by the device manager to allow/disallow access to specific (or all) introspection commands and events. The introspection tool will get the KVM_EPERM error code on any attempt to use a disallowed command. By default, all events and almost all commands are disallowed. Some commands are always allowed (those querying the introspection capabilities). Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/api.rst | 68 ++++++++++ include/linux/kvmi_host.h | 7 + include/uapi/linux/kvm.h | 8 ++ include/uapi/linux/kvmi.h | 22 ++++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 49 +++++++ virt/kvm/introspection/kvmi.c | 122 ++++++++++++++++++ virt/kvm/kvm_main.c | 18 +++ 7 files changed, 294 insertions(+) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 9b48be90ae7b..f3698413ddab 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -4878,6 +4878,74 @@ Errors: This ioctl is used to free all introspection structures related to this VM. +4.129 KVM_INTROSPECTION_COMMAND +------------------------------- + +:Capability: KVM_CAP_INTROSPECTION +:Architectures: x86 +:Type: vm ioctl +:Parameters: struct kvm_introspection_feature (in) +:Returns: 0 on success, a negative value on error + +Errors: + + ====== =========================================================== + EFAULT the VM is not introspected yet (use KVM_INTROSPECTION_HOOK) + EINVAL the command is unknown + EPERM the command can't be disallowed (e.g. KVMI_GET_VERSION) + EPERM the introspection is disabled (kvm.introspection=0) + ====== =========================================================== + +This ioctl is used to allow or disallow introspection commands +for the current VM. By default, almost all commands are disallowed +except for those used to query the API features. + +:: + + struct kvm_introspection_feature { + __u32 allow; + __s32 id; + }; + +If allow is 1, the command specified by id is allowed. If allow is 0, +the command is disallowed. + +Unless set to -1 (meaning all commands), id must be a command ID +(e.g. KVMI_GET_VERSION) + +4.130 KVM_INTROSPECTION_EVENT +----------------------------- + +:Capability: KVM_CAP_INTROSPECTION +:Architectures: x86 +:Type: vm ioctl +:Parameters: struct kvm_introspection_feature (in) +:Returns: 0 on success, a negative value on error + +Errors: + + ====== =========================================================== + EFAULT the VM is not introspected yet (use KVM_INTROSPECTION_HOOK) + EINVAL the event is unknown + EPERM the introspection is disabled (kvm.introspection=0) + ====== =========================================================== + +This ioctl is used to allow or disallow introspection events +for the current VM. By default, all events are disallowed. + +:: + + struct kvm_introspection_feature { + __u32 allow; + __s32 id; + }; + +If allow is 1, the event specified by id is allowed. If allow is 0, +the event is disallowed. + +Unless set to -1 (meaning all events), id must be a event ID +(e.g. KVMI_VM_EVENT_UNHOOK, KVMI_VCPU_EVENT_CR, etc.) + 5. The kvm_run structure ======================== diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 8574b9688736..a5ede07686b9 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -14,6 +14,9 @@ struct kvm_introspection { struct socket *sock; struct task_struct *recv; + + unsigned long *cmd_allow_mask; + unsigned long *event_allow_mask; }; int kvmi_version(void); @@ -25,6 +28,10 @@ void kvmi_destroy_vm(struct kvm *kvm); int kvmi_ioctl_hook(struct kvm *kvm, const struct kvm_introspection_hook *hook); int kvmi_ioctl_unhook(struct kvm *kvm); +int kvmi_ioctl_command(struct kvm *kvm, + const struct kvm_introspection_feature *feat); +int kvmi_ioctl_event(struct kvm *kvm, + const struct kvm_introspection_feature *feat); #else diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index a0be0ea5b13f..c69140893f68 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1653,6 +1653,14 @@ struct kvm_introspection_hook { #define KVM_INTROSPECTION_HOOK _IOW(KVMIO, 0xc8, struct kvm_introspection_hook) #define KVM_INTROSPECTION_UNHOOK _IO(KVMIO, 0xc9) +struct kvm_introspection_feature { + __u32 allow; + __s32 id; +}; + +#define KVM_INTROSPECTION_COMMAND _IOW(KVMIO, 0xca, struct kvm_introspection_feature) +#define KVM_INTROSPECTION_EVENT _IOW(KVMIO, 0xcb, struct kvm_introspection_feature) + #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 34dda91016db..85f8622ddf95 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -10,4 +10,26 @@ enum { KVMI_VERSION = 0x00000001 }; +#define KVMI_VM_MESSAGE_ID(id) ((id) << 1) +#define KVMI_VCPU_MESSAGE_ID(id) (((id) << 1) | 1) + +enum { + KVMI_NEXT_VM_MESSAGE +}; + +enum { + KVMI_NEXT_VCPU_MESSAGE +}; + +#define KVMI_VM_EVENT_ID(id) ((id) << 1) +#define KVMI_VCPU_EVENT_ID(id) (((id) << 1) | 1) + +enum { + KVMI_NEXT_VM_EVENT +}; + +enum { + KVMI_NEXT_VCPU_EVENT +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 13d3e0a3af9d..ac8f658999f8 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -44,11 +44,60 @@ static void do_hook_ioctl(struct kvm_vm *vm, __s32 fd, int expected_err) errno, strerror(errno), expected_err, fd); } +static void set_perm(struct kvm_vm *vm, __s32 id, __u32 allow, + int expected_err, int ioctl_id, + const char *ioctl_str) +{ + struct kvm_introspection_feature feat = { + .allow = allow, + .id = id + }; + int r; + + r = ioctl(vm->fd, ioctl_id, &feat); + TEST_ASSERT(r == 0 || errno == expected_err, + "%s failed, id %d, errno %d (%s), expected %d\n", + ioctl_str, id, errno, strerror(errno), expected_err); +} + +static void set_event_perm(struct kvm_vm *vm, __s32 id, __u32 allow, + int expected_err) +{ + set_perm(vm, id, allow, expected_err, KVM_INTROSPECTION_EVENT, + "KVM_INTROSPECTION_EVENT"); +} + +static void allow_event(struct kvm_vm *vm, __s32 event_id) +{ + set_event_perm(vm, event_id, 1, 0); +} + +static void set_command_perm(struct kvm_vm *vm, __s32 id, __u32 allow, + int expected_err) +{ + set_perm(vm, id, allow, expected_err, KVM_INTROSPECTION_COMMAND, + "KVM_INTROSPECTION_COMMAND"); +} + static void hook_introspection(struct kvm_vm *vm) { + __u32 allow = 1, disallow = 0, allow_inval = 2; + __s32 all_IDs = -1; + + set_command_perm(vm, all_IDs, allow, EFAULT); + set_event_perm(vm, all_IDs, allow, EFAULT); + do_hook_ioctl(vm, -1, EINVAL); do_hook_ioctl(vm, Kvm_socket, 0); do_hook_ioctl(vm, Kvm_socket, EEXIST); + + set_command_perm(vm, all_IDs, allow_inval, EINVAL); + set_command_perm(vm, all_IDs, disallow, 0); + set_command_perm(vm, all_IDs, allow, 0); + + set_event_perm(vm, all_IDs, allow_inval, EINVAL); + set_event_perm(vm, all_IDs, disallow, 0); + allow_event(vm, all_IDs); } static void unhook_introspection(struct kvm_vm *vm) diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 3367023219b7..57487105aa45 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -8,6 +8,11 @@ #include #include "kvmi_int.h" +#define KVMI_NUM_COMMANDS __cmp((int)KVMI_NEXT_VM_MESSAGE, \ + (int)KVMI_NEXT_VCPU_MESSAGE, >) +#define KVMI_NUM_EVENTS __cmp((int)KVMI_NEXT_VM_EVENT, \ + (int)KVMI_NEXT_VCPU_EVENT, >) + int kvmi_init(void) { return 0; @@ -24,6 +29,9 @@ void kvmi_uninit(void) static void kvmi_free(struct kvm *kvm) { + bitmap_free(kvm->kvmi->cmd_allow_mask); + bitmap_free(kvm->kvmi->event_allow_mask); + kfree(kvm->kvmi); kvm->kvmi = NULL; } @@ -37,6 +45,15 @@ kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook) if (!kvmi) return NULL; + kvmi->cmd_allow_mask = bitmap_zalloc(KVMI_NUM_COMMANDS, GFP_KERNEL); + kvmi->event_allow_mask = bitmap_zalloc(KVMI_NUM_EVENTS, GFP_KERNEL); + if (!kvmi->cmd_allow_mask || !kvmi->event_allow_mask) { + bitmap_free(kvmi->cmd_allow_mask); + bitmap_free(kvmi->event_allow_mask); + kfree(kvmi); + return NULL; + } + BUILD_BUG_ON(sizeof(hook->uuid) != sizeof(kvmi->uuid)); memcpy(&kvmi->uuid, &hook->uuid, sizeof(kvmi->uuid)); @@ -182,3 +199,108 @@ void kvmi_destroy_vm(struct kvm *kvm) { kvmi_unhook(kvm); } + +static int +kvmi_ioctl_get_feature(const struct kvm_introspection_feature *feat, + bool *allow, s32 *id, unsigned int nbits) +{ + s32 all_bits = -1; + + if (feat->id < 0 && feat->id != all_bits) + return -EINVAL; + + if (feat->id > 0 && feat->id >= nbits) + return -EINVAL; + + if (feat->allow > 1) + return -EINVAL; + + *allow = feat->allow == 1; + *id = feat->id; + + return 0; +} + +static void kvmi_control_allowed_events(struct kvm_introspection *kvmi, + s32 id, bool allow) +{ + s32 all_events = -1; + + if (allow) { + if (id == all_events) + bitmap_fill(kvmi->event_allow_mask, KVMI_NUM_EVENTS); + else + set_bit(id, kvmi->event_allow_mask); + } else { + if (id == all_events) + bitmap_zero(kvmi->event_allow_mask, KVMI_NUM_EVENTS); + else + clear_bit(id, kvmi->event_allow_mask); + } +} + +int kvmi_ioctl_event(struct kvm *kvm, + const struct kvm_introspection_feature *feat) +{ + struct kvm_introspection *kvmi; + bool allow; + int err; + s32 id; + + err = kvmi_ioctl_get_feature(feat, &allow, &id, KVMI_NUM_EVENTS); + if (err) + return err; + + mutex_lock(&kvm->kvmi_lock); + + kvmi = KVMI(kvm); + if (kvmi) + kvmi_control_allowed_events(kvmi, id, allow); + else + err = -EFAULT; + + mutex_unlock(&kvm->kvmi_lock); + return err; +} + +static void kvmi_control_allowed_commands(struct kvm_introspection *kvmi, + s32 id, bool allow) +{ + s32 all_commands = -1; + + if (allow) { + if (id == all_commands) + bitmap_fill(kvmi->cmd_allow_mask, KVMI_NUM_COMMANDS); + else + set_bit(id, kvmi->cmd_allow_mask); + } else { + if (id == all_commands) + bitmap_zero(kvmi->cmd_allow_mask, KVMI_NUM_COMMANDS); + else + clear_bit(id, kvmi->cmd_allow_mask); + } +} + +int kvmi_ioctl_command(struct kvm *kvm, + const struct kvm_introspection_feature *feat) +{ + struct kvm_introspection *kvmi; + bool allow; + int err; + s32 id; + + err = kvmi_ioctl_get_feature(feat, &allow, &id, KVMI_NUM_COMMANDS); + if (err) + return err; + + mutex_lock(&kvm->kvmi_lock); + + kvmi = KVMI(kvm); + if (kvmi) + kvmi_control_allowed_commands(kvmi, id, allow); + else + err = -EFAULT; + + mutex_unlock(&kvm->kvmi_lock); + return err; +} diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 66fa749a2c6e..c7ea1a61c50e 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -4035,6 +4035,24 @@ static long kvm_vm_ioctl(struct file *filp, if (enable_introspection) r = kvmi_ioctl_unhook(kvm); break; + case KVM_INTROSPECTION_COMMAND: + case KVM_INTROSPECTION_EVENT: { + struct kvm_introspection_feature feat; + + r = -EPERM; + if (!enable_introspection) + goto out; + + r = -EFAULT; + if (copy_from_user(&feat, argp, sizeof(feat))) + goto out; + + if (ioctl == KVM_INTROSPECTION_EVENT) + r = kvmi_ioctl_event(kvm, &feat); + else + r = kvmi_ioctl_command(kvm, &feat); + break; + } #endif /* CONFIG_KVM_INTROSPECTION */ default: r = kvm_arch_vm_ioctl(filp, ioctl, arg); From patchwork Wed Nov 25 09:35:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930863 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A603FC64E75 for ; Wed, 25 Nov 2020 09:42:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5C5FA2083E for ; Wed, 25 Nov 2020 09:42:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728665AbgKYJmg (ORCPT ); Wed, 25 Nov 2020 04:42:36 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57240 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728407AbgKYJmE (ORCPT ); Wed, 25 Nov 2020 04:42:04 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 75840305D515; Wed, 25 Nov 2020 11:35:48 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 530D03072785; Wed, 25 Nov 2020 11:35:48 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 35/81] KVM: introspection: add the read/dispatch message function Date: Wed, 25 Nov 2020 11:35:14 +0200 Message-Id: <20201125093600.2766-36-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Based on the common header (struct kvmi_msg_hdr), the receiving thread will read/validate all messages, execute the VM introspection commands (eg. KVMI_VM_GET_INFO) and dispatch the vCPU introspection commands (eg. KVMI_VCPU_GET_REGISTERS) to the vCPU threads. The vCPU threads will reply to vCPU introspection commands without the help of the receiving thread. Same for sending vCPU events, but the vCPU thread will wait for the receiving thread to get the event reply. Meanwhile, it will execute any queued vCPU introspection command. The receiving thread will end when the socket is closed or on the first API error (eg. wrong message size). Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 75 ++++++++ include/uapi/linux/kvmi.h | 11 ++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 100 +++++++++++ virt/kvm/introspection/kvmi.c | 43 ++++- virt/kvm/introspection/kvmi_int.h | 10 ++ virt/kvm/introspection/kvmi_msg.c | 161 +++++++++++++++++- 6 files changed, 398 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 59cc33a39f9f..ae6bbf37aef3 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -65,6 +65,74 @@ been used on that guest (if requested). Obviously, whether the guest can really continue normal execution depends on whether the introspection tool has made any modifications that require an active KVMI channel. +All messages (commands or events) have a common header:: + + struct kvmi_msg_hdr { + __u16 id; + __u16 size; + __u32 seq; + }; + +The replies have the same header, with the sequence number (``seq``) +and message id (``id``) matching the command/event. + +After ``kvmi_msg_hdr``, ``id`` specific data of ``size`` bytes will +follow. + +The message header and its data must be sent with one ``sendmsg()`` call +to the socket. This simplifies the receiver loop and avoids +the reconstruction of messages on the other side. + +The wire protocol uses the host native byte-order. The introspection tool +must check this during the handshake and do the necessary conversion. + +A command reply begins with:: + + struct kvmi_error_code { + __s32 err; + __u32 padding; + } + +followed by the command specific data if the error code ``err`` is zero. + +The error code -KVM_ENOSYS is returned for unsupported commands. + +The error code -KVM_EPERM is returned for disallowed commands (see **Hooking**). + +Other error codes can be returned during message handling, but for +some errors (incomplete messages, wrong sequence numbers, socket errors +etc.) the socket will be closed. The device manager should reconnect. + +When a vCPU thread sends an introspection event, it will wait (and handle +any related introspection command) until it gets the event reply:: + + Host kernel Introspection tool + ----------- ------------------ + event 1 -> + <- command 1 + command 1 reply -> + <- command 2 + command 2 reply -> + <- event 1 reply + +As it can be seen below, the wire protocol specifies occasional padding. This +is to permit working with the data by directly using C structures or to round +the structure size to a multiple of 8 bytes (64bit) to improve the copy +operations that happen during ``recvmsg()`` or ``sendmsg()``. The members +should have the native alignment of the host. All padding must be +initialized with zero otherwise the respective command will fail with +-KVM_EINVAL. + +To describe the commands/events, we reuse some conventions from api.rst: + + - Architectures: which instruction set architectures provide this command/event + + - Versions: which versions provide this command/event + + - Parameters: incoming message data + + - Returns: outgoing/reply message data + Handshake --------- @@ -99,6 +167,13 @@ In the end, the device manager will pass the file descriptor (plus the allowed commands/events) to KVM. It will detect when the socket is shutdown and it will reinitiate the handshake. +Once the file descriptor reaches KVM, the introspection tool should +use the *KVMI_GET_VERSION* command to get the API version and/or the +*KVMI_VM_CHECK_COMMAND* and *KVMI_VM_CHECK_EVENT* commands to see which +commands/events are allowed for this guest. The error code -KVM_EPERM +will be returned if the introspection tool uses a command or tries to +enable an event which is disallowed. + Unhooking --------- diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 85f8622ddf95..2b37eee82c52 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -32,4 +32,15 @@ enum { KVMI_NEXT_VCPU_EVENT }; +struct kvmi_msg_hdr { + __u16 id; + __u16 size; + __u32 seq; +}; + +struct kvmi_error_code { + __s32 err; + __u32 padding; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index ac8f658999f8..95bd0a60eb47 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -15,6 +15,7 @@ #include "processor.h" #include "../lib/kvm_util_internal.h" +#include "linux/kvm_para.h" #include "linux/kvmi.h" #define VCPU_ID 1 @@ -110,10 +111,109 @@ static void unhook_introspection(struct kvm_vm *vm) errno, strerror(errno)); } +static void receive_data(void *dest, size_t size) +{ + ssize_t r; + + r = recv(Userspace_socket, dest, size, MSG_WAITALL); + TEST_ASSERT(r == size, + "recv() failed, expected %zd, result %zd, errno %d (%s)\n", + size, r, errno, strerror(errno)); +} + +static int receive_cmd_reply(struct kvmi_msg_hdr *req, void *rpl, + size_t rpl_size) +{ + struct kvmi_msg_hdr hdr; + struct kvmi_error_code ec; + + receive_data(&hdr, sizeof(hdr)); + + TEST_ASSERT(hdr.seq == req->seq, + "Unexpected messages sequence 0x%x, expected 0x%x\n", + hdr.seq, req->seq); + + TEST_ASSERT(hdr.size >= sizeof(ec), + "Invalid message size %d, expected %zd bytes (at least)\n", + hdr.size, sizeof(ec)); + + receive_data(&ec, sizeof(ec)); + + if (ec.err) { + TEST_ASSERT(hdr.size == sizeof(ec), + "Invalid command reply on error\n"); + } else { + TEST_ASSERT(hdr.size == sizeof(ec) + rpl_size, + "Invalid command reply\n"); + + if (rpl && rpl_size) + receive_data(rpl, rpl_size); + } + + return ec.err; +} + +static unsigned int new_seq(void) +{ + static unsigned int seq; + + return seq++; +} + +static void send_message(int msg_id, struct kvmi_msg_hdr *hdr, size_t size) +{ + ssize_t r; + + hdr->id = msg_id; + hdr->seq = new_seq(); + hdr->size = size - sizeof(*hdr); + + r = send(Userspace_socket, hdr, size, 0); + TEST_ASSERT(r == size, + "send() failed, sending %zd, result %zd, errno %d (%s)\n", + size, r, errno, strerror(errno)); +} + +static const char *kvm_strerror(int error) +{ + switch (error) { + case KVM_ENOSYS: + return "Invalid system call number"; + case KVM_EOPNOTSUPP: + return "Operation not supported on transport endpoint"; + case KVM_EAGAIN: + return "Try again"; + default: + return strerror(error); + } +} + +static int do_command(int cmd_id, struct kvmi_msg_hdr *req, + size_t req_size, void *rpl, size_t rpl_size) +{ + send_message(cmd_id, req, req_size); + return receive_cmd_reply(req, rpl, rpl_size); +} + +static void test_cmd_invalid(void) +{ + int invalid_msg_id = 0xffff; + struct kvmi_msg_hdr req; + int r; + + r = do_command(invalid_msg_id, &req, sizeof(req), NULL, 0); + TEST_ASSERT(r == -KVM_ENOSYS, + "Invalid command didn't failed with KVM_ENOSYS, error %d (%s)\n", + -r, kvm_strerror(-r)); +} + static void test_introspection(struct kvm_vm *vm) { setup_socket(); hook_introspection(vm); + + test_cmd_invalid(); + unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 57487105aa45..8b3ba3a236af 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -13,9 +13,49 @@ #define KVMI_NUM_EVENTS __cmp((int)KVMI_NEXT_VM_EVENT, \ (int)KVMI_NEXT_VCPU_EVENT, >) +#define KVMI_MSG_SIZE_ALLOC (sizeof(struct kvmi_msg_hdr) + KVMI_MAX_MSG_SIZE) + +static struct kmem_cache *msg_cache; + +void *kvmi_msg_alloc(void) +{ + return kmem_cache_zalloc(msg_cache, GFP_KERNEL); +} + +void kvmi_msg_free(void *addr) +{ + if (addr) + kmem_cache_free(msg_cache, addr); +} + +static void kvmi_cache_destroy(void) +{ + kmem_cache_destroy(msg_cache); + msg_cache = NULL; +} + +static int kvmi_cache_create(void) +{ + msg_cache = kmem_cache_create("kvmi_msg", KVMI_MSG_SIZE_ALLOC, + 4096, SLAB_ACCOUNT, NULL); + + if (!msg_cache) { + kvmi_cache_destroy(); + + return -1; + } + + return 0; +} + +bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id) +{ + return id < KVMI_NUM_COMMANDS && test_bit(id, kvmi->cmd_allow_mask); +} + int kvmi_init(void) { - return 0; + return kvmi_cache_create(); } int kvmi_version(void) @@ -25,6 +65,7 @@ int kvmi_version(void) void kvmi_uninit(void) { + kvmi_cache_destroy(); } static void kvmi_free(struct kvm *kvm) diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index c89875bd2bac..206aaf93f8ba 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -7,6 +7,11 @@ #include #define KVMI(kvm) ((kvm)->kvmi) +/* + * This limit is used to accommodate the largest known fixed-length + * message. + */ +#define KVMI_MAX_MSG_SIZE (4096 * 2 - sizeof(struct kvmi_msg_hdr)) /* kvmi_msg.c */ bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd); @@ -14,4 +19,9 @@ void kvmi_sock_shutdown(struct kvm_introspection *kvmi); void kvmi_sock_put(struct kvm_introspection *kvmi); bool kvmi_msg_process(struct kvm_introspection *kvmi); +/* kvmi.c */ +void *kvmi_msg_alloc(void); +void kvmi_msg_free(void *addr); +bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id); + #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 3ae52c61f861..38a5a4a84d5d 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -8,6 +8,10 @@ #include #include "kvmi_int.h" +typedef int (*kvmi_vm_msg_fct)(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *req); + bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd) { struct socket *sock; @@ -33,7 +37,162 @@ void kvmi_sock_shutdown(struct kvm_introspection *kvmi) kernel_sock_shutdown(kvmi->sock, SHUT_RDWR); } +static int handle_sock_rc(int rc, size_t size) +{ + if (unlikely(rc < 0)) + return rc; + if (unlikely(rc != size)) + return -EPIPE; + return 0; +} + +static int kvmi_sock_read(struct kvm_introspection *kvmi, void *buf, + size_t size) +{ + struct kvec vec = { .iov_base = buf, .iov_len = size, }; + struct msghdr m = { }; + int rc; + + rc = kernel_recvmsg(kvmi->sock, &m, &vec, 1, size, MSG_WAITALL); + + return handle_sock_rc(rc, size); +} + +static int kvmi_sock_write(struct kvm_introspection *kvmi, struct kvec *vec, + size_t n, size_t size) +{ + struct msghdr m = { }; + int rc; + + rc = kernel_sendmsg(kvmi->sock, &m, vec, n, size); + + return handle_sock_rc(rc, size); +} + +static int kvmi_msg_reply(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, int err, + const void *rpl, size_t rpl_size) +{ + struct kvmi_error_code ec; + struct kvmi_msg_hdr h; + struct kvec vec[3] = { + { .iov_base = &h, .iov_len = sizeof(h) }, + { .iov_base = &ec, .iov_len = sizeof(ec) }, + { .iov_base = (void *)rpl, .iov_len = rpl_size }, + }; + size_t size = sizeof(h) + sizeof(ec) + (err ? 0 : rpl_size); + size_t n = ARRAY_SIZE(vec) - (err ? 1 : 0); + + memset(&h, 0, sizeof(h)); + h.id = msg->id; + h.seq = msg->seq; + h.size = size - sizeof(h); + + memset(&ec, 0, sizeof(ec)); + ec.err = err; + + return kvmi_sock_write(kvmi, vec, n, size); +} + +static int kvmi_msg_vm_reply(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + int err, const void *rpl, + size_t rpl_size) +{ + return kvmi_msg_reply(kvmi, msg, err, rpl, rpl_size); +} + +/* + * These commands are executed by the receiving thread. + */ +static kvmi_vm_msg_fct const msg_vm[] = { +}; + +static kvmi_vm_msg_fct get_vm_msg_handler(u16 id) +{ + return id < ARRAY_SIZE(msg_vm) ? msg_vm[id] : NULL; +} + +static bool is_vm_message(u16 id) +{ + bool is_vm_msg_id = (id & 1) == 0; + + return is_vm_msg_id && !!get_vm_msg_handler(id); +} + +static bool is_vm_command(u16 id) +{ + return is_vm_message(id); +} + +static struct kvmi_msg_hdr *kvmi_msg_recv(struct kvm_introspection *kvmi) +{ + struct kvmi_msg_hdr *msg; + int err; + + msg = kvmi_msg_alloc(); + if (!msg) + goto out; + + err = kvmi_sock_read(kvmi, msg, sizeof(*msg)); + if (err) + goto out_err; + + if (msg->size) { + if (msg->size > KVMI_MAX_MSG_SIZE) + goto out_err; + + err = kvmi_sock_read(kvmi, msg + 1, msg->size); + if (err) + goto out_err; + } + + return msg; + +out_err: + kvmi_msg_free(msg); +out: + return NULL; +} + +static int kvmi_msg_do_vm_cmd(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg) +{ + kvmi_vm_msg_fct fct = get_vm_msg_handler(msg->id); + + return fct(kvmi, msg, msg + 1); +} + +static int kvmi_msg_vm_reply_ec(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, int ec) +{ + return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); +} + +static int kvmi_msg_handle_vm_cmd(struct kvm_introspection *kvmi, + struct kvmi_msg_hdr *msg) +{ + if (!kvmi_is_command_allowed(kvmi, msg->id)) + return kvmi_msg_vm_reply_ec(kvmi, msg, -KVM_EPERM); + + return kvmi_msg_do_vm_cmd(kvmi, msg); +} + bool kvmi_msg_process(struct kvm_introspection *kvmi) { - return false; + struct kvmi_msg_hdr *msg; + int err = -1; + + msg = kvmi_msg_recv(kvmi); + if (!msg) + goto out; + + if (is_vm_command(msg->id)) + err = kvmi_msg_handle_vm_cmd(kvmi, msg); + else + err = kvmi_msg_vm_reply_ec(kvmi, msg, -KVM_ENOSYS); + + kvmi_msg_free(msg); +out: + return err == 0; } From patchwork Wed Nov 25 09:35:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930899 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 18C49C8300C for ; Wed, 25 Nov 2020 09:42:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C91A32083E for ; Wed, 25 Nov 2020 09:42:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728550AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57196 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728235AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 9BB64305D516; Wed, 25 Nov 2020 11:35:48 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 79AB93072784; Wed, 25 Nov 2020 11:35:48 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 36/81] KVM: introspection: add KVMI_GET_VERSION Date: Wed, 25 Nov 2020 11:35:15 +0200 Message-Id: <20201125093600.2766-37-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org When handling introspection commands from tools built with older or newer versions of the introspection API, the receiving thread silently accepts smaller/larger messages, but it replies with messages related to current/kernel version. Smaller introspection event replies are accepted too. However, larger messages for event replies are not allowed. Even if an introspection tool can use the API version returned by the KVMI_GET_VERSION command to check the supported features, the most important usage of this command is to avoid sending newer versions of event replies that the kernel side doesn't know. On larger messages, the introspection socket will be closed. Any attempt from the device manager to explicitly disallow this command through the KVM_INTROSPECTION_COMMAND ioctl will get -EPERM, unless all commands are disallowed (using id=-1), in which case KVMI_GET_VERSION is silently allowed, without error. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 38 +++++++++++++++++++ include/uapi/linux/kvmi.h | 10 +++++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 35 +++++++++++++++++ virt/kvm/introspection/kvmi.c | 27 +++++++++++-- virt/kvm/introspection/kvmi_msg.c | 13 +++++++ 5 files changed, 119 insertions(+), 4 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index ae6bbf37aef3..d3d672a07872 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -212,3 +212,41 @@ device-specific memory (DMA, emulated MMIO, reserved by a passthrough device etc.). It is up to the user to determine, using the guest operating system data structures, the areas that are safe to access (code, stack, heap etc.). + +Commands +-------- + +The following C structures are meant to be used directly when communicating +over the wire. The peer that detects any size mismatch should simply close +the connection and report the error. + +1. KVMI_GET_VERSION +------------------- + +:Architectures: all +:Versions: >= 1 +:Parameters: none +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_get_version_reply { + __u32 version; + __u32 max_msg_size; + }; + +Returns the introspection API version and the largest accepted message +size (useful for variable length messages). + +This command is always allowed and successful. + +The messages used for introspection commands/events might be extended +in future versions and while the kernel will accept commands with +shorter messages (older versions) or larger messages (newer versions, +ignoring the extra information), it will not accept event replies with +larger messages. + +The introspection tool should use this command to identify the features +supported by the kernel side and what messages must be used for event +replies. diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 2b37eee82c52..77dd727dfe18 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -6,6 +6,9 @@ * KVMI structures and definitions */ +#include +#include + enum { KVMI_VERSION = 0x00000001 }; @@ -14,6 +17,8 @@ enum { #define KVMI_VCPU_MESSAGE_ID(id) (((id) << 1) | 1) enum { + KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1), + KVMI_NEXT_VM_MESSAGE }; @@ -43,4 +48,9 @@ struct kvmi_error_code { __u32 padding; }; +struct kvmi_get_version_reply { + __u32 version; + __u32 max_msg_size; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 95bd0a60eb47..30acd3a2d030 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -92,6 +92,7 @@ static void hook_introspection(struct kvm_vm *vm) do_hook_ioctl(vm, Kvm_socket, 0); do_hook_ioctl(vm, Kvm_socket, EEXIST); + set_command_perm(vm, KVMI_GET_VERSION, disallow, EPERM); set_command_perm(vm, all_IDs, allow_inval, EINVAL); set_command_perm(vm, all_IDs, disallow, 0); set_command_perm(vm, all_IDs, allow, 0); @@ -207,12 +208,46 @@ static void test_cmd_invalid(void) -r, kvm_strerror(-r)); } +static void test_vm_command(int cmd_id, struct kvmi_msg_hdr *req, + size_t req_size, void *rpl, size_t rpl_size, + int expected_err) +{ + int r; + + r = do_command(cmd_id, req, req_size, rpl, rpl_size); + TEST_ASSERT(r == expected_err, + "Command %d failed, error %d (%s) instead of %d (%s)\n", + cmd_id, -r, kvm_strerror(-r), + expected_err, kvm_strerror(expected_err)); +} + +static void cmd_vm_get_version(struct kvmi_get_version_reply *ver) +{ + struct kvmi_msg_hdr req; + + test_vm_command(KVMI_GET_VERSION, &req, sizeof(req), ver, sizeof(*ver), 0); +} + +static void test_cmd_get_version(void) +{ + struct kvmi_get_version_reply rpl; + + cmd_vm_get_version(&rpl); + TEST_ASSERT(rpl.version == KVMI_VERSION, + "Unexpected KVMI version %d, expecting %d\n", + rpl.version, KVMI_VERSION); + + pr_debug("KVMI version: %u\n", rpl.version); + pr_debug("Max message size: %u\n", rpl.max_msg_size); +} + static void test_introspection(struct kvm_vm *vm) { setup_socket(); hook_introspection(vm); test_cmd_invalid(); + test_cmd_get_version(); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 8b3ba3a236af..520be9478697 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -15,6 +15,8 @@ #define KVMI_MSG_SIZE_ALLOC (sizeof(struct kvmi_msg_hdr) + KVMI_MAX_MSG_SIZE) +static DECLARE_BITMAP(Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS); + static struct kmem_cache *msg_cache; void *kvmi_msg_alloc(void) @@ -53,8 +55,16 @@ bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id) return id < KVMI_NUM_COMMANDS && test_bit(id, kvmi->cmd_allow_mask); } +static void kvmi_init_always_allowed_commands(void) +{ + bitmap_zero(Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS); + set_bit(KVMI_GET_VERSION, Kvmi_always_allowed_commands); +} + int kvmi_init(void) { + kvmi_init_always_allowed_commands(); + return kvmi_cache_create(); } @@ -98,6 +108,9 @@ kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook) BUILD_BUG_ON(sizeof(hook->uuid) != sizeof(kvmi->uuid)); memcpy(&kvmi->uuid, &hook->uuid, sizeof(kvmi->uuid)); + bitmap_copy(kvmi->cmd_allow_mask, Kvmi_always_allowed_commands, + KVMI_NUM_COMMANDS); + kvmi->kvm = kvm; return kvmi; @@ -304,8 +317,8 @@ int kvmi_ioctl_event(struct kvm *kvm, return err; } -static void kvmi_control_allowed_commands(struct kvm_introspection *kvmi, - s32 id, bool allow) +static int kvmi_control_allowed_commands(struct kvm_introspection *kvmi, + s32 id, bool allow) { s32 all_commands = -1; @@ -316,10 +329,16 @@ static void kvmi_control_allowed_commands(struct kvm_introspection *kvmi, set_bit(id, kvmi->cmd_allow_mask); } else { if (id == all_commands) - bitmap_zero(kvmi->cmd_allow_mask, KVMI_NUM_COMMANDS); + bitmap_copy(kvmi->cmd_allow_mask, + Kvmi_always_allowed_commands, + KVMI_NUM_COMMANDS); + else if (test_bit(id, Kvmi_always_allowed_commands)) + return -EPERM; else clear_bit(id, kvmi->cmd_allow_mask); } + + return 0; } int kvmi_ioctl_command(struct kvm *kvm, @@ -338,7 +357,7 @@ int kvmi_ioctl_command(struct kvm *kvm, kvmi = KVMI(kvm); if (kvmi) - kvmi_control_allowed_commands(kvmi, id, allow); + err = kvmi_control_allowed_commands(kvmi, id, allow); else err = -EFAULT; diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 38a5a4a84d5d..57708da2af20 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -102,10 +102,23 @@ static int kvmi_msg_vm_reply(struct kvm_introspection *kvmi, return kvmi_msg_reply(kvmi, msg, err, rpl, rpl_size); } +static int handle_get_version(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, const void *req) +{ + struct kvmi_get_version_reply rpl; + + memset(&rpl, 0, sizeof(rpl)); + rpl.version = kvmi_version(); + rpl.max_msg_size = KVMI_MAX_MSG_SIZE; + + return kvmi_msg_vm_reply(kvmi, msg, 0, &rpl, sizeof(rpl)); +} + /* * These commands are executed by the receiving thread. */ static kvmi_vm_msg_fct const msg_vm[] = { + [KVMI_GET_VERSION] = handle_get_version, }; static kvmi_vm_msg_fct get_vm_msg_handler(u16 id) From patchwork Wed Nov 25 09:35:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930847 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 77F1CC83014 for ; Wed, 25 Nov 2020 09:42:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3119220B1F for ; Wed, 25 Nov 2020 09:42:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728632AbgKYJmL (ORCPT ); Wed, 25 Nov 2020 04:42:11 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57194 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728571AbgKYJmJ (ORCPT ); Wed, 25 Nov 2020 04:42:09 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id BFDFD305D517; Wed, 25 Nov 2020 11:35:48 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 9FF153072785; Wed, 25 Nov 2020 11:35:48 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 37/81] KVM: introspection: add KVMI_VM_CHECK_COMMAND and KVMI_VM_CHECK_EVENT Date: Wed, 25 Nov 2020 11:35:16 +0200 Message-Id: <20201125093600.2766-38-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org These commands are used to check what introspection commands and events are supported (kernel) and allowed (device manager). These are alternative methods to KVMI_GET_VERSION in checking if the introspection supports a specific command/event. As with the KVMI_GET_VERSION command, these two commands can never be disallowed by the device manager. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 62 +++++++++++++++++++ include/uapi/linux/kvmi.h | 16 ++++- .../testing/selftests/kvm/x86_64/kvmi_test.c | 45 ++++++++++++++ virt/kvm/introspection/kvmi.c | 19 ++++++ virt/kvm/introspection/kvmi_int.h | 2 + virt/kvm/introspection/kvmi_msg.c | 40 +++++++++++- 6 files changed, 182 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index d3d672a07872..13169575f75f 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -250,3 +250,65 @@ larger messages. The introspection tool should use this command to identify the features supported by the kernel side and what messages must be used for event replies. + +2. KVMI_VM_CHECK_COMMAND +------------------------ + +:Architectures: all +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vm_check_command { + __u16 id; + __u16 padding1; + __u32 padding2; + }; + +:Returns: + +:: + + struct kvmi_error_code; + +Checks if the command specified by ``id`` is supported and allowed. + +This command is always allowed. + +:Errors: + +* -KVM_ENOENT - the command specified by ``id`` is unsupported +* -KVM_EPERM - the command specified by ``id`` is disallowed +* -KVM_EINVAL - the padding is not zero + +3. KVMI_VM_CHECK_EVENT +---------------------- + +:Architectures: all +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vm_check_event { + __u16 id; + __u16 padding1; + __u32 padding2; + }; + +:Returns: + +:: + + struct kvmi_error_code; + +Checks if the event specified by ``id`` is supported and allowed. + +This command is always allowed. + +:Errors: + +* -KVM_ENOENT - the event specified by ``id`` is unsupported +* -KVM_EPERM - the event specified by ``id`` is disallowed +* -KVM_EINVAL - the padding is not zero diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 77dd727dfe18..0c2d0cedde6f 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -17,7 +17,9 @@ enum { #define KVMI_VCPU_MESSAGE_ID(id) (((id) << 1) | 1) enum { - KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1), + KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1), + KVMI_VM_CHECK_COMMAND = KVMI_VM_MESSAGE_ID(2), + KVMI_VM_CHECK_EVENT = KVMI_VM_MESSAGE_ID(3), KVMI_NEXT_VM_MESSAGE }; @@ -53,4 +55,16 @@ struct kvmi_get_version_reply { __u32 max_msg_size; }; +struct kvmi_vm_check_command { + __u16 id; + __u16 padding1; + __u32 padding2; +}; + +struct kvmi_vm_check_event { + __u16 id; + __u16 padding1; + __u32 padding2; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 30acd3a2d030..cd8f16a3ce3a 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -93,6 +93,8 @@ static void hook_introspection(struct kvm_vm *vm) do_hook_ioctl(vm, Kvm_socket, EEXIST); set_command_perm(vm, KVMI_GET_VERSION, disallow, EPERM); + set_command_perm(vm, KVMI_VM_CHECK_COMMAND, disallow, EPERM); + set_command_perm(vm, KVMI_VM_CHECK_EVENT, disallow, EPERM); set_command_perm(vm, all_IDs, allow_inval, EINVAL); set_command_perm(vm, all_IDs, disallow, 0); set_command_perm(vm, all_IDs, allow, 0); @@ -241,6 +243,47 @@ static void test_cmd_get_version(void) pr_debug("Max message size: %u\n", rpl.max_msg_size); } +static void cmd_vm_check_command(__u16 id, int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vm_check_command cmd; + } req = {}; + + req.cmd.id = id; + + test_vm_command(KVMI_VM_CHECK_COMMAND, &req.hdr, sizeof(req), NULL, 0, + expected_err); +} + +static void test_cmd_vm_check_command(void) +{ + __u16 valid_id = KVMI_GET_VERSION, invalid_id = 0xffff; + + cmd_vm_check_command(valid_id, 0); + cmd_vm_check_command(invalid_id, -KVM_ENOENT); +} + +static void cmd_vm_check_event(__u16 id, int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vm_check_event cmd; + } req = {}; + + req.cmd.id = id; + + test_vm_command(KVMI_VM_CHECK_EVENT, &req.hdr, sizeof(req), NULL, 0, + expected_err); +} + +static void test_cmd_vm_check_event(void) +{ + __u16 invalid_id = 0xffff; + + cmd_vm_check_event(invalid_id, -KVM_ENOENT); +} + static void test_introspection(struct kvm_vm *vm) { setup_socket(); @@ -248,6 +291,8 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_invalid(); test_cmd_get_version(); + test_cmd_vm_check_command(); + test_cmd_vm_check_event(); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 520be9478697..9125e6c92ded 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -16,6 +16,7 @@ #define KVMI_MSG_SIZE_ALLOC (sizeof(struct kvmi_msg_hdr) + KVMI_MAX_MSG_SIZE) static DECLARE_BITMAP(Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS); +static DECLARE_BITMAP(Kvmi_known_events, KVMI_NUM_EVENTS); static struct kmem_cache *msg_cache; @@ -55,15 +56,33 @@ bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id) return id < KVMI_NUM_COMMANDS && test_bit(id, kvmi->cmd_allow_mask); } +bool kvmi_is_event_allowed(struct kvm_introspection *kvmi, u16 id) +{ + return id < KVMI_NUM_EVENTS && test_bit(id, kvmi->event_allow_mask); +} + +bool kvmi_is_known_event(u16 id) +{ + return id < KVMI_NUM_EVENTS && test_bit(id, Kvmi_known_events); +} + static void kvmi_init_always_allowed_commands(void) { bitmap_zero(Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS); set_bit(KVMI_GET_VERSION, Kvmi_always_allowed_commands); + set_bit(KVMI_VM_CHECK_COMMAND, Kvmi_always_allowed_commands); + set_bit(KVMI_VM_CHECK_EVENT, Kvmi_always_allowed_commands); +} + +static void kvmi_init_known_events(void) +{ + bitmap_zero(Kvmi_known_events, KVMI_NUM_EVENTS); } int kvmi_init(void) { kvmi_init_always_allowed_commands(); + kvmi_init_known_events(); return kvmi_cache_create(); } diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 206aaf93f8ba..1e1d1fad4035 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -23,5 +23,7 @@ bool kvmi_msg_process(struct kvm_introspection *kvmi); void *kvmi_msg_alloc(void); void kvmi_msg_free(void *addr); bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id); +bool kvmi_is_event_allowed(struct kvm_introspection *kvmi, u16 id); +bool kvmi_is_known_event(u16 id); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 57708da2af20..6538c7af710a 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -12,6 +12,8 @@ typedef int (*kvmi_vm_msg_fct)(struct kvm_introspection *kvmi, const struct kvmi_msg_hdr *msg, const void *req); +static bool is_vm_command(u16 id); + bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd) { struct socket *sock; @@ -114,11 +116,47 @@ static int handle_get_version(struct kvm_introspection *kvmi, return kvmi_msg_vm_reply(kvmi, msg, 0, &rpl, sizeof(rpl)); } +static int handle_vm_check_command(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vm_check_command *req = _req; + int ec = 0; + + if (req->padding1 || req->padding2) + ec = -KVM_EINVAL; + else if (!is_vm_command(req->id)) + ec = -KVM_ENOENT; + else if (!kvmi_is_command_allowed(kvmi, req->id)) + ec = -KVM_EPERM; + + return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); +} + +static int handle_vm_check_event(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vm_check_event *req = _req; + int ec = 0; + + if (req->padding1 || req->padding2) + ec = -KVM_EINVAL; + else if (!kvmi_is_known_event(req->id)) + ec = -KVM_ENOENT; + else if (!kvmi_is_event_allowed(kvmi, req->id)) + ec = -KVM_EPERM; + + return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); +} + /* * These commands are executed by the receiving thread. */ static kvmi_vm_msg_fct const msg_vm[] = { - [KVMI_GET_VERSION] = handle_get_version, + [KVMI_GET_VERSION] = handle_get_version, + [KVMI_VM_CHECK_COMMAND] = handle_vm_check_command, + [KVMI_VM_CHECK_EVENT] = handle_vm_check_event, }; static kvmi_vm_msg_fct get_vm_msg_handler(u16 id) From patchwork Wed Nov 25 09:35:17 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930797 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A9DF3C6379D for ; Wed, 25 Nov 2020 09:41:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 4D52C206F9 for ; Wed, 25 Nov 2020 09:41:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727474AbgKYJly (ORCPT ); Wed, 25 Nov 2020 04:41:54 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57032 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726973AbgKYJlw (ORCPT ); Wed, 25 Nov 2020 04:41:52 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E41D6305D518; Wed, 25 Nov 2020 11:35:48 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C21333072784; Wed, 25 Nov 2020 11:35:48 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 38/81] KVM: introspection: add KVMI_VM_GET_INFO Date: Wed, 25 Nov 2020 11:35:17 +0200 Message-Id: <20201125093600.2766-39-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This command returns the number of online vCPUs. The introspection tool uses the vCPU index to specify to which vCPU the introspection command applies to. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 18 ++++++++++ include/uapi/linux/kvmi.h | 6 ++++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 35 +++++++++++++++++-- virt/kvm/introspection/kvmi_msg.c | 13 +++++++ 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 13169575f75f..6f8583d4aeb2 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -312,3 +312,21 @@ This command is always allowed. * -KVM_ENOENT - the event specified by ``id`` is unsupported * -KVM_EPERM - the event specified by ``id`` is disallowed * -KVM_EINVAL - the padding is not zero + +4. KVMI_VM_GET_INFO +------------------- + +:Architectures: all +:Versions: >= 1 +:Parameters: none +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_vm_get_info_reply { + __u32 vcpu_count; + __u32 padding[3]; + }; + +Returns the number of online vCPUs. diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 0c2d0cedde6f..e06a7b80d4d9 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -20,6 +20,7 @@ enum { KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1), KVMI_VM_CHECK_COMMAND = KVMI_VM_MESSAGE_ID(2), KVMI_VM_CHECK_EVENT = KVMI_VM_MESSAGE_ID(3), + KVMI_VM_GET_INFO = KVMI_VM_MESSAGE_ID(4), KVMI_NEXT_VM_MESSAGE }; @@ -67,4 +68,9 @@ struct kvmi_vm_check_event { __u32 padding2; }; +struct kvmi_vm_get_info_reply { + __u32 vcpu_count; + __u32 padding[3]; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index cd8f16a3ce3a..d60ee23fa833 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -80,6 +80,16 @@ static void set_command_perm(struct kvm_vm *vm, __s32 id, __u32 allow, "KVM_INTROSPECTION_COMMAND"); } +static void disallow_command(struct kvm_vm *vm, __s32 id) +{ + set_command_perm(vm, id, 0, 0); +} + +static void allow_command(struct kvm_vm *vm, __s32 id) +{ + set_command_perm(vm, id, 1, 0); +} + static void hook_introspection(struct kvm_vm *vm) { __u32 allow = 1, disallow = 0, allow_inval = 2; @@ -256,12 +266,16 @@ static void cmd_vm_check_command(__u16 id, int expected_err) expected_err); } -static void test_cmd_vm_check_command(void) +static void test_cmd_vm_check_command(struct kvm_vm *vm) { - __u16 valid_id = KVMI_GET_VERSION, invalid_id = 0xffff; + __u16 valid_id = KVMI_VM_GET_INFO, invalid_id = 0xffff; cmd_vm_check_command(valid_id, 0); cmd_vm_check_command(invalid_id, -KVM_ENOENT); + + disallow_command(vm, valid_id); + cmd_vm_check_command(valid_id, -KVM_EPERM); + allow_command(vm, valid_id); } static void cmd_vm_check_event(__u16 id, int expected_err) @@ -284,6 +298,20 @@ static void test_cmd_vm_check_event(void) cmd_vm_check_event(invalid_id, -KVM_ENOENT); } +static void test_cmd_vm_get_info(void) +{ + struct kvmi_vm_get_info_reply rpl; + struct kvmi_msg_hdr req; + + test_vm_command(KVMI_VM_GET_INFO, &req, sizeof(req), &rpl, + sizeof(rpl), 0); + TEST_ASSERT(rpl.vcpu_count == 1, + "Unexpected number of vCPU count %u\n", + rpl.vcpu_count); + + pr_debug("vcpu count: %u\n", rpl.vcpu_count); +} + static void test_introspection(struct kvm_vm *vm) { setup_socket(); @@ -291,8 +319,9 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_invalid(); test_cmd_get_version(); - test_cmd_vm_check_command(); + test_cmd_vm_check_command(vm); test_cmd_vm_check_event(); + test_cmd_vm_get_info(); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 6538c7af710a..f0f5058403dd 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -150,6 +150,18 @@ static int handle_vm_check_event(struct kvm_introspection *kvmi, return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); } +static int handle_vm_get_info(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *req) +{ + struct kvmi_vm_get_info_reply rpl; + + memset(&rpl, 0, sizeof(rpl)); + rpl.vcpu_count = atomic_read(&kvmi->kvm->online_vcpus); + + return kvmi_msg_vm_reply(kvmi, msg, 0, &rpl, sizeof(rpl)); +} + /* * These commands are executed by the receiving thread. */ @@ -157,6 +169,7 @@ static kvmi_vm_msg_fct const msg_vm[] = { [KVMI_GET_VERSION] = handle_get_version, [KVMI_VM_CHECK_COMMAND] = handle_vm_check_command, [KVMI_VM_CHECK_EVENT] = handle_vm_check_event, + [KVMI_VM_GET_INFO] = handle_vm_get_info, }; static kvmi_vm_msg_fct get_vm_msg_handler(u16 id) From patchwork Wed Nov 25 09:35:18 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930811 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D1FD7C6379D for ; Wed, 25 Nov 2020 09:42:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 820072083E for ; Wed, 25 Nov 2020 09:42:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728574AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57134 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728304AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 23713305D460; Wed, 25 Nov 2020 11:35:49 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id EA19F3072785; Wed, 25 Nov 2020 11:35:48 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 39/81] KVM: introspection: add KVM_INTROSPECTION_PREUNHOOK Date: Wed, 25 Nov 2020 11:35:18 +0200 Message-Id: <20201125093600.2766-40-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org In certain situations (when the guest has to be paused, suspended, migrated, etc.), the device manager will use this new ioctl in order to trigger the KVMI_VM_EVENT_UNHOOK event. If the event is sent successfully (the VM has an active introspection channel), the device manager should delay the action (pause/suspend/...) to give the introspection tool the chance to remove its hooks (eg. breakpoints) while the guest is still running. Once a timeout is reached or the introspection tool has closed the socket, the device manager should resume the action. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/api.rst | 28 ++++++++++++++++++++++++++++ Documentation/virt/kvm/kvmi.rst | 7 ++++--- include/linux/kvmi_host.h | 1 + include/uapi/linux/kvm.h | 2 ++ virt/kvm/introspection/kvmi.c | 30 ++++++++++++++++++++++++++++++ virt/kvm/introspection/kvmi_int.h | 1 + virt/kvm/introspection/kvmi_msg.c | 5 +++++ virt/kvm/kvm_main.c | 5 +++++ 8 files changed, 76 insertions(+), 3 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index f3698413ddab..e6544d94e040 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -4946,6 +4946,34 @@ the event is disallowed. Unless set to -1 (meaning all events), id must be a event ID (e.g. KVMI_VM_EVENT_UNHOOK, KVMI_VCPU_EVENT_CR, etc.) +4.131 KVM_INTROSPECTION_PREUNHOOK +--------------------------------- + +:Capability: KVM_CAP_INTROSPECTION +:Architectures: x86 +:Type: vm ioctl +:Parameters: none +:Returns: 0 on success, a negative value on error + +Errors: + + ====== ============================================================ + EFAULT the VM is not introspected yet (use KVM_INTROSPECTION_HOOK) + ENOENT the socket (passed with KVM_INTROSPECTION_HOOK) had an error + ENOENT the introspection tool didn't subscribed + to this type of introspection event (unhook) + ====== ============================================================ + +This ioctl is used to inform that the current VM is +paused/suspended/migrated/etc. + +KVM should send an 'unhook' introspection event to the introspection tool. + +If this ioctl is successful, the userspace should give the +introspection tool a chance to unhook the VM and then it should use +KVM_INTROSPECTION_UNHOOK to make sure all the introspection structures +are freed. + 5. The kvm_run structure ======================== diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 6f8583d4aeb2..33490bc9d1c1 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -183,9 +183,10 @@ becomes necessary to remove them before the guest is suspended, moved (migrated) or a snapshot with memory is created. The actions are normally performed by the device manager. In the case -of QEMU, it will use another ioctl to notify the introspection tool and -wait for a limited amount of time (a few seconds) for a confirmation that -is OK to proceed (the introspection tool will close the connection). +of QEMU, it will use the *KVM_INTROSPECTION_PREUNHOOK* ioctl to trigger +the *KVMI_VM_EVENT_UNHOOK* event and wait for a limited amount of time (a +few seconds) for a confirmation that is OK to proceed. The introspection +tool will close the connection to signal this. Live migrations --------------- diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index a5ede07686b9..81eac9f53a3f 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -32,6 +32,7 @@ int kvmi_ioctl_command(struct kvm *kvm, const struct kvm_introspection_feature *feat); int kvmi_ioctl_event(struct kvm *kvm, const struct kvm_introspection_feature *feat); +int kvmi_ioctl_preunhook(struct kvm *kvm); #else diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index c69140893f68..a29fbdf93b84 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1661,6 +1661,8 @@ struct kvm_introspection_feature { #define KVM_INTROSPECTION_COMMAND _IOW(KVMIO, 0xca, struct kvm_introspection_feature) #define KVM_INTROSPECTION_EVENT _IOW(KVMIO, 0xcb, struct kvm_introspection_feature) +#define KVM_INTROSPECTION_PREUNHOOK _IO(KVMIO, 0xcc) + #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) #define KVM_DEV_ASSIGN_MASK_INTX (1 << 2) diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 9125e6c92ded..72dd41915048 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -383,3 +383,33 @@ int kvmi_ioctl_command(struct kvm *kvm, mutex_unlock(&kvm->kvmi_lock); return err; } + +static bool kvmi_unhook_event(struct kvm_introspection *kvmi) +{ + int err; + + err = kvmi_msg_send_unhook(kvmi); + + return !err; +} + +int kvmi_ioctl_preunhook(struct kvm *kvm) +{ + struct kvm_introspection *kvmi; + int err = 0; + + mutex_lock(&kvm->kvmi_lock); + + kvmi = KVMI(kvm); + if (!kvmi) { + err = -EFAULT; + goto out; + } + + if (!kvmi_unhook_event(kvmi)) + err = -ENOENT; + +out: + mutex_unlock(&kvm->kvmi_lock); + return err; +} diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 1e1d1fad4035..ef4850e8bfae 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -18,6 +18,7 @@ bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd); void kvmi_sock_shutdown(struct kvm_introspection *kvmi); void kvmi_sock_put(struct kvm_introspection *kvmi); bool kvmi_msg_process(struct kvm_introspection *kvmi); +int kvmi_msg_send_unhook(struct kvm_introspection *kvmi); /* kvmi.c */ void *kvmi_msg_alloc(void); diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index f0f5058403dd..513681290305 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -260,3 +260,8 @@ bool kvmi_msg_process(struct kvm_introspection *kvmi) out: return err == 0; } + +int kvmi_msg_send_unhook(struct kvm_introspection *kvmi) +{ + return -1; +} diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index c7ea1a61c50e..f30d1bd9495a 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -4053,6 +4053,11 @@ static long kvm_vm_ioctl(struct file *filp, r = kvmi_ioctl_command(kvm, &feat); break; } + case KVM_INTROSPECTION_PREUNHOOK: + r = -EPERM; + if (enable_introspection) + r = kvmi_ioctl_preunhook(kvm); + break; #endif /* CONFIG_KVM_INTROSPECTION */ default: r = kvm_arch_vm_ioctl(filp, ioctl, arg); From patchwork Wed Nov 25 09:35:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930831 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 33C37C63777 for ; Wed, 25 Nov 2020 09:42:18 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D9A7B2083E for ; Wed, 25 Nov 2020 09:42:17 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728652AbgKYJmR (ORCPT ); Wed, 25 Nov 2020 04:42:17 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57188 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728590AbgKYJmK (ORCPT ); Wed, 25 Nov 2020 04:42:10 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 4645A305D461; Wed, 25 Nov 2020 11:35:49 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 278B63072784; Wed, 25 Nov 2020 11:35:49 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 40/81] KVM: introspection: add KVMI_VM_EVENT_UNHOOK Date: Wed, 25 Nov 2020 11:35:19 +0200 Message-Id: <20201125093600.2766-41-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This event is sent when the guest is about to be paused/suspended/migrated. The introspection tool has the chance to remove its hooks (e.g. breakpoints) while the guest is still running. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 31 +++++++++ arch/x86/kvm/Makefile | 2 +- arch/x86/kvm/kvmi.c | 10 +++ include/linux/kvmi_host.h | 2 + include/uapi/linux/kvmi.h | 9 +++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 68 ++++++++++++++++++- virt/kvm/introspection/kvmi.c | 13 +++- virt/kvm/introspection/kvmi_int.h | 3 + virt/kvm/introspection/kvmi_msg.c | 42 +++++++++++- 9 files changed, 173 insertions(+), 7 deletions(-) create mode 100644 arch/x86/kvm/kvmi.c diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 33490bc9d1c1..e9c40c7ae154 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -331,3 +331,34 @@ This command is always allowed. }; Returns the number of online vCPUs. + +Events +====== + +The VM introspection events are sent using the KVMI_VM_EVENT message id. +The message data begins with a common structure having the event id:: + + struct kvmi_event_hdr { + __u16 event; + __u16 padding[3]; + }; + +Specific event data can follow this common structure. + +1. KVMI_VM_EVENT_UNHOOK +----------------------- + +:Architectures: all +:Versions: >= 1 +:Actions: none +:Parameters: + +:: + + struct kvmi_event_hdr; + +:Returns: none + +This event is sent when the device manager has to pause/stop/migrate the +guest (see **Unhooking**). The introspection tool has a chance to unhook +and close the KVMI channel (signaling that the operation can proceed). diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index db4121b4112d..8fad40649bcf 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -14,7 +14,7 @@ kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \ $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o \ $(KVM)/dirty_ring.o kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o -kvm-$(CONFIG_KVM_INTROSPECTION) += $(KVMI)/kvmi.o $(KVMI)/kvmi_msg.o +kvm-$(CONFIG_KVM_INTROSPECTION) += $(KVMI)/kvmi.o $(KVMI)/kvmi_msg.o kvmi.o kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c new file mode 100644 index 000000000000..35742d927be5 --- /dev/null +++ b/arch/x86/kvm/kvmi.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KVM introspection - x86 + * + * Copyright (C) 2019-2020 Bitdefender S.R.L. + */ + +void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) +{ +} diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 81eac9f53a3f..6476c7d6a4d3 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -17,6 +17,8 @@ struct kvm_introspection { unsigned long *cmd_allow_mask; unsigned long *event_allow_mask; + + atomic_t ev_seq; }; int kvmi_version(void); diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index e06a7b80d4d9..18fb51078d48 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -17,6 +17,8 @@ enum { #define KVMI_VCPU_MESSAGE_ID(id) (((id) << 1) | 1) enum { + KVMI_VM_EVENT = KVMI_VM_MESSAGE_ID(0), + KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1), KVMI_VM_CHECK_COMMAND = KVMI_VM_MESSAGE_ID(2), KVMI_VM_CHECK_EVENT = KVMI_VM_MESSAGE_ID(3), @@ -33,6 +35,8 @@ enum { #define KVMI_VCPU_EVENT_ID(id) (((id) << 1) | 1) enum { + KVMI_VM_EVENT_UNHOOK = KVMI_VM_EVENT_ID(0), + KVMI_NEXT_VM_EVENT }; @@ -73,4 +77,9 @@ struct kvmi_vm_get_info_reply { __u32 padding[3]; }; +struct kvmi_event_hdr { + __u16 event; + __u16 padding[3]; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index d60ee23fa833..01b260379c2a 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -68,6 +68,11 @@ static void set_event_perm(struct kvm_vm *vm, __s32 id, __u32 allow, "KVM_INTROSPECTION_EVENT"); } +static void disallow_event(struct kvm_vm *vm, __s32 event_id) +{ + set_event_perm(vm, event_id, 0, 0); +} + static void allow_event(struct kvm_vm *vm, __s32 event_id) { set_event_perm(vm, event_id, 1, 0); @@ -291,11 +296,16 @@ static void cmd_vm_check_event(__u16 id, int expected_err) expected_err); } -static void test_cmd_vm_check_event(void) +static void test_cmd_vm_check_event(struct kvm_vm *vm) { - __u16 invalid_id = 0xffff; + __u16 valid_id = KVMI_VM_EVENT_UNHOOK, invalid_id = 0xffff; cmd_vm_check_event(invalid_id, -KVM_ENOENT); + cmd_vm_check_event(valid_id, 0); + + disallow_event(vm, valid_id); + cmd_vm_check_event(valid_id, -KVM_EPERM); + allow_event(vm, valid_id); } static void test_cmd_vm_get_info(void) @@ -312,6 +322,57 @@ static void test_cmd_vm_get_info(void) pr_debug("vcpu count: %u\n", rpl.vcpu_count); } +static void trigger_event_unhook_notification(struct kvm_vm *vm) +{ + int r; + + r = ioctl(vm->fd, KVM_INTROSPECTION_PREUNHOOK, NULL); + TEST_ASSERT(r == 0, + "KVM_INTROSPECTION_PREUNHOOK failed, errno %d (%s)\n", + errno, strerror(errno)); +} + +static void receive_event(struct kvmi_msg_hdr *msg_hdr, u16 msg_id, + struct kvmi_event_hdr *ev_hdr, u16 event_id, + size_t ev_size) +{ + size_t to_read = ev_size; + + receive_data(msg_hdr, sizeof(*msg_hdr)); + + TEST_ASSERT(msg_hdr->id == msg_id, + "Unexpected messages id %d, expected %d\n", + msg_hdr->id, msg_id); + + if (to_read > msg_hdr->size) + to_read = msg_hdr->size; + + receive_data(ev_hdr, to_read); + TEST_ASSERT(ev_hdr->event == event_id, + "Unexpected event %d, expected %d\n", + ev_hdr->event, event_id); + + TEST_ASSERT(msg_hdr->size == ev_size, + "Invalid event size %d, expected %zd bytes\n", + msg_hdr->size, ev_size); +} + +static void receive_vm_event_unhook(void) +{ + struct kvmi_msg_hdr msg_hdr; + struct kvmi_event_hdr ev_hdr; + + receive_event(&msg_hdr, KVMI_VM_EVENT, + &ev_hdr, KVMI_VM_EVENT_UNHOOK, sizeof(ev_hdr)); +} + +static void test_event_unhook(struct kvm_vm *vm) +{ + trigger_event_unhook_notification(vm); + + receive_vm_event_unhook(); +} + static void test_introspection(struct kvm_vm *vm) { setup_socket(); @@ -320,8 +381,9 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_invalid(); test_cmd_get_version(); test_cmd_vm_check_command(vm); - test_cmd_vm_check_event(); + test_cmd_vm_check_event(vm); test_cmd_vm_get_info(); + test_event_unhook(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 72dd41915048..3746fd243bd8 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -17,6 +17,8 @@ static DECLARE_BITMAP(Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS); static DECLARE_BITMAP(Kvmi_known_events, KVMI_NUM_EVENTS); +static DECLARE_BITMAP(Kvmi_known_vm_events, KVMI_NUM_EVENTS); +static DECLARE_BITMAP(Kvmi_known_vcpu_events, KVMI_NUM_EVENTS); static struct kmem_cache *msg_cache; @@ -76,7 +78,14 @@ static void kvmi_init_always_allowed_commands(void) static void kvmi_init_known_events(void) { - bitmap_zero(Kvmi_known_events, KVMI_NUM_EVENTS); + bitmap_zero(Kvmi_known_vm_events, KVMI_NUM_EVENTS); + set_bit(KVMI_VM_EVENT_UNHOOK, Kvmi_known_vm_events); + + bitmap_zero(Kvmi_known_vcpu_events, KVMI_NUM_EVENTS); + kvmi_arch_init_vcpu_events_mask(Kvmi_known_vcpu_events); + + bitmap_or(Kvmi_known_events, Kvmi_known_vm_events, + Kvmi_known_vcpu_events, KVMI_NUM_EVENTS); } int kvmi_init(void) @@ -130,6 +139,8 @@ kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook) bitmap_copy(kvmi->cmd_allow_mask, Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS); + atomic_set(&kvmi->ev_seq, 0); + kvmi->kvm = kvm; return kvmi; diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index ef4850e8bfae..57c22f20e74f 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -27,4 +27,7 @@ bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id); bool kvmi_is_event_allowed(struct kvm_introspection *kvmi, u16 id); bool kvmi_is_known_event(u16 id); +/* arch */ +void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); + #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 513681290305..4acdb595301d 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -186,7 +186,7 @@ static bool is_vm_message(u16 id) static bool is_vm_command(u16 id) { - return is_vm_message(id); + return is_vm_message(id) && id != KVMI_VM_EVENT; } static struct kvmi_msg_hdr *kvmi_msg_recv(struct kvm_introspection *kvmi) @@ -261,7 +261,45 @@ bool kvmi_msg_process(struct kvm_introspection *kvmi) return err == 0; } +static void kvmi_fill_ev_msg_hdr(struct kvm_introspection *kvmi, + struct kvmi_msg_hdr *msg_hdr, + struct kvmi_event_hdr *ev_hdr, + u16 msg_id, u32 msg_seq, + size_t msg_size, u16 ev_id) +{ + memset(msg_hdr, 0, sizeof(*msg_hdr)); + msg_hdr->id = msg_id; + msg_hdr->seq = msg_seq; + msg_hdr->size = msg_size - sizeof(*msg_hdr); + + memset(ev_hdr, 0, sizeof(*ev_hdr)); + ev_hdr->event = ev_id; +} + +static void kvmi_fill_vm_event(struct kvm_introspection *kvmi, + struct kvmi_msg_hdr *msg_hdr, + struct kvmi_event_hdr *ev_hdr, + u16 ev_id, size_t msg_size) +{ + u32 msg_seq = atomic_inc_return(&kvmi->ev_seq); + + kvmi_fill_ev_msg_hdr(kvmi, msg_hdr, ev_hdr, KVMI_VM_EVENT, + msg_seq, msg_size, ev_id); +} + int kvmi_msg_send_unhook(struct kvm_introspection *kvmi) { - return -1; + struct kvmi_msg_hdr msg_hdr; + struct kvmi_event_hdr ev_hdr; + struct kvec vec[] = { + {.iov_base = &msg_hdr, .iov_len = sizeof(msg_hdr)}, + {.iov_base = &ev_hdr, .iov_len = sizeof(ev_hdr) }, + }; + size_t msg_size = sizeof(msg_hdr) + sizeof(ev_hdr); + size_t n = ARRAY_SIZE(vec); + + kvmi_fill_vm_event(kvmi, &msg_hdr, &ev_hdr, + KVMI_VM_EVENT_UNHOOK, msg_size); + + return kvmi_sock_write(kvmi, vec, n, msg_size); } From patchwork Wed Nov 25 09:35:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930923 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C9D26C64E7C for ; Wed, 25 Nov 2020 09:42:58 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8074D2083E for ; Wed, 25 Nov 2020 09:42:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728461AbgKYJm5 (ORCPT ); Wed, 25 Nov 2020 04:42:57 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57144 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727971AbgKYJl6 (ORCPT ); Wed, 25 Nov 2020 04:41:58 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 6A508305D462; Wed, 25 Nov 2020 11:35:49 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 487D03072785; Wed, 25 Nov 2020 11:35:49 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 41/81] KVM: introspection: add KVMI_VM_CONTROL_EVENTS Date: Wed, 25 Nov 2020 11:35:20 +0200 Message-Id: <20201125093600.2766-42-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org By default, all introspection VM events are disabled. The introspection tool must explicitly enable the VM events it wants to receive. With this command it can enable/disable any VM event (e.g. KVMI_VM_EVENT_UNHOOK) if allowed by the device manager. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 42 ++++++++++++++-- include/linux/kvmi_host.h | 2 + include/uapi/linux/kvmi.h | 16 +++++-- .../testing/selftests/kvm/x86_64/kvmi_test.c | 48 +++++++++++++++++++ virt/kvm/introspection/kvmi.c | 30 +++++++++++- virt/kvm/introspection/kvmi_int.h | 3 ++ virt/kvm/introspection/kvmi_msg.c | 29 +++++++++-- 7 files changed, 158 insertions(+), 12 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index e9c40c7ae154..b4ce7db32150 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -332,10 +332,44 @@ This command is always allowed. Returns the number of online vCPUs. +5. KVMI_VM_CONTROL_EVENTS +------------------------- + +:Architectures: all +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vm_control_events { + __u16 event_id; + __u8 enable; + __u8 padding1; + __u32 padding2; + }; + +:Returns: + +:: + + struct kvmi_error_code + +Enables/disables VM introspection events. This command can be used with +the following events:: + + KVMI_VM_EVENT_UNHOOK + +:Errors: + +* -KVM_EINVAL - the padding is not zero +* -KVM_EINVAL - the event ID is unknown (use *KVMI_VM_CHECK_EVENT* first) +* -KVM_EPERM - the access is disallowed (use *KVMI_VM_CHECK_EVENT* first) + Events ====== The VM introspection events are sent using the KVMI_VM_EVENT message id. +No event is sent unless it is explicitly enabled. The message data begins with a common structure having the event id:: struct kvmi_event_hdr { @@ -359,6 +393,8 @@ Specific event data can follow this common structure. :Returns: none -This event is sent when the device manager has to pause/stop/migrate the -guest (see **Unhooking**). The introspection tool has a chance to unhook -and close the KVMI channel (signaling that the operation can proceed). +This event is sent when the device manager has to pause/stop/migrate +the guest (see **Unhooking**) and the introspection has been enabled for +this event (see **KVMI_VM_CONTROL_EVENTS**). The introspection tool has +a chance to unhook and close the introspection socket (signaling that +the operation can proceed). diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 6476c7d6a4d3..a59307dac6bf 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -18,6 +18,8 @@ struct kvm_introspection { unsigned long *cmd_allow_mask; unsigned long *event_allow_mask; + unsigned long *vm_event_enable_mask; + atomic_t ev_seq; }; diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 18fb51078d48..9a10ef2cd890 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -19,10 +19,11 @@ enum { enum { KVMI_VM_EVENT = KVMI_VM_MESSAGE_ID(0), - KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1), - KVMI_VM_CHECK_COMMAND = KVMI_VM_MESSAGE_ID(2), - KVMI_VM_CHECK_EVENT = KVMI_VM_MESSAGE_ID(3), - KVMI_VM_GET_INFO = KVMI_VM_MESSAGE_ID(4), + KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1), + KVMI_VM_CHECK_COMMAND = KVMI_VM_MESSAGE_ID(2), + KVMI_VM_CHECK_EVENT = KVMI_VM_MESSAGE_ID(3), + KVMI_VM_GET_INFO = KVMI_VM_MESSAGE_ID(4), + KVMI_VM_CONTROL_EVENTS = KVMI_VM_MESSAGE_ID(5), KVMI_NEXT_VM_MESSAGE }; @@ -82,4 +83,11 @@ struct kvmi_event_hdr { __u16 padding[3]; }; +struct kvmi_vm_control_events { + __u16 event_id; + __u8 enable; + __u8 padding1; + __u32 padding2; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 01b260379c2a..430685a3371e 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -332,6 +332,31 @@ static void trigger_event_unhook_notification(struct kvm_vm *vm) errno, strerror(errno)); } +static void cmd_vm_control_events(__u16 event_id, __u8 enable, + int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vm_control_events cmd; + } req = {}; + + req.cmd.event_id = event_id; + req.cmd.enable = enable; + + test_vm_command(KVMI_VM_CONTROL_EVENTS, &req.hdr, sizeof(req), + NULL, 0, expected_err); +} + +static void enable_vm_event(__u16 event_id) +{ + cmd_vm_control_events(event_id, 1, 0); +} + +static void disable_vm_event(__u16 event_id) +{ + cmd_vm_control_events(event_id, 0, 0); +} + static void receive_event(struct kvmi_msg_hdr *msg_hdr, u16 msg_id, struct kvmi_event_hdr *ev_hdr, u16 event_id, size_t ev_size) @@ -368,9 +393,31 @@ static void receive_vm_event_unhook(void) static void test_event_unhook(struct kvm_vm *vm) { + u16 id = KVMI_VM_EVENT_UNHOOK; + + enable_vm_event(id); + trigger_event_unhook_notification(vm); receive_vm_event_unhook(); + + disable_vm_event(id); +} + +static void test_cmd_vm_control_events(struct kvm_vm *vm) +{ + __u16 id = KVMI_VM_EVENT_UNHOOK, invalid_id = 0xffff; + __u8 enable = 1, enable_inval = 2; + + enable_vm_event(id); + disable_vm_event(id); + + cmd_vm_control_events(id, enable_inval, -KVM_EINVAL); + cmd_vm_control_events(invalid_id, enable, -KVM_EINVAL); + + disallow_event(vm, id); + cmd_vm_control_events(id, enable, -KVM_EPERM); + allow_event(vm, id); } static void test_introspection(struct kvm_vm *vm) @@ -384,6 +431,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vm_check_event(vm); test_cmd_vm_get_info(); test_event_unhook(vm); + test_cmd_vm_control_events(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 3746fd243bd8..7a0dac2e2f84 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -68,6 +68,16 @@ bool kvmi_is_known_event(u16 id) return id < KVMI_NUM_EVENTS && test_bit(id, Kvmi_known_events); } +bool kvmi_is_known_vm_event(u16 id) +{ + return id < KVMI_NUM_EVENTS && test_bit(id, Kvmi_known_vm_events); +} + +static bool kvmi_is_vm_event_enabled(struct kvm_introspection *kvmi, u16 id) +{ + return id < KVMI_NUM_EVENTS && test_bit(id, kvmi->vm_event_enable_mask); +} + static void kvmi_init_always_allowed_commands(void) { bitmap_zero(Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS); @@ -110,6 +120,7 @@ static void kvmi_free(struct kvm *kvm) { bitmap_free(kvm->kvmi->cmd_allow_mask); bitmap_free(kvm->kvmi->event_allow_mask); + bitmap_free(kvm->kvmi->vm_event_enable_mask); kfree(kvm->kvmi); kvm->kvmi = NULL; @@ -126,9 +137,12 @@ kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook) kvmi->cmd_allow_mask = bitmap_zalloc(KVMI_NUM_COMMANDS, GFP_KERNEL); kvmi->event_allow_mask = bitmap_zalloc(KVMI_NUM_EVENTS, GFP_KERNEL); - if (!kvmi->cmd_allow_mask || !kvmi->event_allow_mask) { + kvmi->vm_event_enable_mask = bitmap_zalloc(KVMI_NUM_EVENTS, GFP_KERNEL); + if (!kvmi->cmd_allow_mask || !kvmi->event_allow_mask || + !kvmi->vm_event_enable_mask) { bitmap_free(kvmi->cmd_allow_mask); bitmap_free(kvmi->event_allow_mask); + bitmap_free(kvmi->vm_event_enable_mask); kfree(kvmi); return NULL; } @@ -399,6 +413,9 @@ static bool kvmi_unhook_event(struct kvm_introspection *kvmi) { int err; + if (!kvmi_is_vm_event_enabled(kvmi, KVMI_VM_EVENT_UNHOOK)) + return false; + err = kvmi_msg_send_unhook(kvmi); return !err; @@ -424,3 +441,14 @@ int kvmi_ioctl_preunhook(struct kvm *kvm) mutex_unlock(&kvm->kvmi_lock); return err; } + +int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, + u16 event_id, bool enable) +{ + if (enable) + set_bit(event_id, kvmi->vm_event_enable_mask); + else + clear_bit(event_id, kvmi->vm_event_enable_mask); + + return 0; +} diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 57c22f20e74f..987513d6c1a7 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -26,6 +26,9 @@ void kvmi_msg_free(void *addr); bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id); bool kvmi_is_event_allowed(struct kvm_introspection *kvmi, u16 id); bool kvmi_is_known_event(u16 id); +bool kvmi_is_known_vm_event(u16 id); +int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, + u16 event_id, bool enable); /* arch */ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 4acdb595301d..ffd7d95b664f 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -162,14 +162,35 @@ static int handle_vm_get_info(struct kvm_introspection *kvmi, return kvmi_msg_vm_reply(kvmi, msg, 0, &rpl, sizeof(rpl)); } +static int handle_vm_control_events(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vm_control_events *req = _req; + int ec; + + if (req->padding1 || req->padding2 || req->enable > 1) + ec = -KVM_EINVAL; + else if (!kvmi_is_known_vm_event(req->event_id)) + ec = -KVM_EINVAL; + else if (!kvmi_is_event_allowed(kvmi, req->event_id)) + ec = -KVM_EPERM; + else + ec = kvmi_cmd_vm_control_events(kvmi, req->event_id, + req->enable == 1); + + return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); +} + /* * These commands are executed by the receiving thread. */ static kvmi_vm_msg_fct const msg_vm[] = { - [KVMI_GET_VERSION] = handle_get_version, - [KVMI_VM_CHECK_COMMAND] = handle_vm_check_command, - [KVMI_VM_CHECK_EVENT] = handle_vm_check_event, - [KVMI_VM_GET_INFO] = handle_vm_get_info, + [KVMI_GET_VERSION] = handle_get_version, + [KVMI_VM_CHECK_COMMAND] = handle_vm_check_command, + [KVMI_VM_CHECK_EVENT] = handle_vm_check_event, + [KVMI_VM_CONTROL_EVENTS] = handle_vm_control_events, + [KVMI_VM_GET_INFO] = handle_vm_get_info, }; static kvmi_vm_msg_fct get_vm_msg_handler(u16 id) From patchwork Wed Nov 25 09:35:21 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930833 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-11.0 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1FEB6C64E7A for ; Wed, 25 Nov 2020 09:42:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E2B09206F9 for ; Wed, 25 Nov 2020 09:42:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728527AbgKYJmD (ORCPT ); Wed, 25 Nov 2020 04:42:03 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57100 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728143AbgKYJmA (ORCPT ); Wed, 25 Nov 2020 04:42:00 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 9D2B1305D463; Wed, 25 Nov 2020 11:35:49 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 6E72B3072784; Wed, 25 Nov 2020 11:35:49 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 42/81] KVM: introspection: add KVMI_VM_READ_PHYSICAL/KVMI_VM_WRITE_PHYSICAL Date: Wed, 25 Nov 2020 11:35:21 +0200 Message-Id: <20201125093600.2766-43-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu These commands allow the introspection tool to read/write from/to the guest memory. Signed-off-by: Mihai Donțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 68 ++++++++++ include/uapi/linux/kvmi.h | 17 +++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 124 ++++++++++++++++++ virt/kvm/introspection/kvmi.c | 98 ++++++++++++++ virt/kvm/introspection/kvmi_int.h | 7 + virt/kvm/introspection/kvmi_msg.c | 44 +++++++ 6 files changed, 358 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index b4ce7db32150..7812d62240c0 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -365,6 +365,74 @@ the following events:: * -KVM_EINVAL - the event ID is unknown (use *KVMI_VM_CHECK_EVENT* first) * -KVM_EPERM - the access is disallowed (use *KVMI_VM_CHECK_EVENT* first) +6. KVMI_VM_READ_PHYSICAL +------------------------ + +:Architectures: all +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vm_read_physical { + __u64 gpa; + __u16 size; + __u16 padding1; + __u32 padding2; + }; + +:Returns: + +:: + + struct kvmi_error_code; + __u8 data[0]; + +Reads from the guest memory. + +Currently, the size must be non-zero and the read must be restricted to +one page (offset + size <= PAGE_SIZE). + +:Errors: + +* -KVM_ENOENT - the guest page doesn't exists +* -KVM_EINVAL - the specified gpa/size pair is invalid +* -KVM_EINVAL - the padding is not zero + +7. KVMI_VM_WRITE_PHYSICAL +------------------------- + +:Architectures: all +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vm_write_physical { + __u64 gpa; + __u16 size; + __u16 padding1; + __u32 padding2; + __u8 data[0]; + }; + +:Returns: + +:: + + struct kvmi_error_code + +Writes into the guest memory. + +Currently, the size must be non-zero and the write must be restricted to +one page (offset + size <= PAGE_SIZE). + +:Errors: + +* -KVM_ENOENT - the guest page doesn't exists +* -KVM_EINVAL - the specified gpa/size pair is invalid +* -KVM_EINVAL - the padding is not zero + Events ====== diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 9a10ef2cd890..048afad01be6 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -24,6 +24,8 @@ enum { KVMI_VM_CHECK_EVENT = KVMI_VM_MESSAGE_ID(3), KVMI_VM_GET_INFO = KVMI_VM_MESSAGE_ID(4), KVMI_VM_CONTROL_EVENTS = KVMI_VM_MESSAGE_ID(5), + KVMI_VM_READ_PHYSICAL = KVMI_VM_MESSAGE_ID(6), + KVMI_VM_WRITE_PHYSICAL = KVMI_VM_MESSAGE_ID(7), KVMI_NEXT_VM_MESSAGE }; @@ -90,4 +92,19 @@ struct kvmi_vm_control_events { __u32 padding2; }; +struct kvmi_vm_read_physical { + __u64 gpa; + __u16 size; + __u16 padding1; + __u32 padding2; +}; + +struct kvmi_vm_write_physical { + __u64 gpa; + __u16 size; + __u16 padding1; + __u32 padding2; + __u8 data[0]; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 430685a3371e..b493edb534b0 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -8,6 +8,7 @@ #define _GNU_SOURCE /* for program_invocation_short_name */ #include #include +#include #include "test_util.h" @@ -24,6 +25,12 @@ static int socket_pair[2]; #define Kvm_socket socket_pair[0] #define Userspace_socket socket_pair[1] +static vm_vaddr_t test_gva; +static void *test_hva; +static vm_paddr_t test_gpa; + +static int page_size; + void setup_socket(void) { int r; @@ -420,8 +427,112 @@ static void test_cmd_vm_control_events(struct kvm_vm *vm) allow_event(vm, id); } +static void cmd_vm_write_page(__u64 gpa, __u64 size, void *p, + int expected_err) +{ + struct kvmi_vm_write_physical *cmd; + struct kvmi_msg_hdr *req; + size_t req_size; + + req_size = sizeof(*req) + sizeof(*cmd) + size; + req = calloc(1, req_size); + + cmd = (struct kvmi_vm_write_physical *)(req + 1); + cmd->gpa = gpa; + cmd->size = size; + + memcpy(cmd + 1, p, size); + + test_vm_command(KVMI_VM_WRITE_PHYSICAL, req, req_size, NULL, 0, + expected_err); + + free(req); +} + +static void write_guest_page(__u64 gpa, void *p) +{ + cmd_vm_write_page(gpa, page_size, p, 0); +} + +static void write_with_invalid_arguments(__u64 gpa, __u64 size, void *p) +{ + cmd_vm_write_page(gpa, size, p, -KVM_EINVAL); +} + +static void write_invalid_guest_page(struct kvm_vm *vm, void *p) +{ + __u64 gpa = vm->max_gfn << vm->page_shift; + __u64 size = 1; + + cmd_vm_write_page(gpa, size, p, -KVM_ENOENT); +} + +static void cmd_vm_read_page(__u64 gpa, __u64 size, void *p, + int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vm_read_physical cmd; + } req = { }; + + req.cmd.gpa = gpa; + req.cmd.size = size; + + test_vm_command(KVMI_VM_READ_PHYSICAL, &req.hdr, sizeof(req), p, size, + expected_err); +} + +static void read_guest_page(__u64 gpa, void *p) +{ + cmd_vm_read_page(gpa, page_size, p, 0); +} + +static void read_with_invalid_arguments(__u64 gpa, __u64 size, void *p) +{ + cmd_vm_read_page(gpa, size, p, -KVM_EINVAL); +} + +static void read_invalid_guest_page(struct kvm_vm *vm) +{ + __u64 gpa = vm->max_gfn << vm->page_shift; + __u64 size = 1; + + cmd_vm_read_page(gpa, size, NULL, -KVM_ENOENT); +} + +static void test_memory_access(struct kvm_vm *vm) +{ + void *pw, *pr; + + pw = malloc(page_size); + TEST_ASSERT(pw, "Insufficient Memory\n"); + + memset(pw, 1, page_size); + + write_guest_page(test_gpa, pw); + TEST_ASSERT(memcmp(pw, test_hva, page_size) == 0, + "Write page test failed"); + + pr = malloc(page_size); + TEST_ASSERT(pr, "Insufficient Memory\n"); + + read_guest_page(test_gpa, pr); + TEST_ASSERT(memcmp(pw, pr, page_size) == 0, + "Read page test failed"); + + read_with_invalid_arguments(test_gpa, 0, pr); + write_with_invalid_arguments(test_gpa, 0, pw); + write_invalid_guest_page(vm, pw); + + free(pw); + free(pr); + + read_invalid_guest_page(vm); +} + static void test_introspection(struct kvm_vm *vm) { + srandom(time(0)); setup_socket(); hook_introspection(vm); @@ -432,10 +543,20 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vm_get_info(); test_event_unhook(vm); test_cmd_vm_control_events(vm); + test_memory_access(vm); unhook_introspection(vm); } +static void setup_test_pages(struct kvm_vm *vm) +{ + test_gva = vm_vaddr_alloc(vm, page_size, KVM_UTIL_MIN_VADDR, 0, 0); + sync_global_to_guest(vm, test_gva); + + test_hva = addr_gva2hva(vm, test_gva); + test_gpa = addr_gva2gpa(vm, test_gva); +} + int main(int argc, char *argv[]) { struct kvm_vm *vm; @@ -448,6 +569,9 @@ int main(int argc, char *argv[]) vm = vm_create_default(VCPU_ID, 0, NULL); vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); + page_size = getpagesize(); + setup_test_pages(vm); + test_introspection(vm); kvm_vm_free(vm); diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 7a0dac2e2f84..2bff4707cc57 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -5,7 +5,9 @@ * Copyright (C) 2017-2020 Bitdefender S.R.L. * */ +#include #include +#include #include "kvmi_int.h" #define KVMI_NUM_COMMANDS __cmp((int)KVMI_NEXT_VM_MESSAGE, \ @@ -452,3 +454,99 @@ int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, return 0; } + +static long +get_user_pages_remote_unlocked(struct mm_struct *mm, unsigned long start, + unsigned long nr_pages, unsigned int gup_flags, + struct page **pages) +{ + struct vm_area_struct **vmas = NULL; + int locked = 1; + long r; + + mmap_read_lock(mm); + r = get_user_pages_remote(mm, start, nr_pages, gup_flags, + pages, vmas, &locked); + if (locked) + mmap_read_unlock(mm); + + return r; +} + +static void *get_page_ptr(struct kvm *kvm, gpa_t gpa, struct page **page, + bool write, int *srcu_idx) +{ + unsigned int flags = write ? FOLL_WRITE : 0; + unsigned long hva; + + *page = NULL; + + *srcu_idx = srcu_read_lock(&kvm->srcu); + hva = gfn_to_hva(kvm, gpa_to_gfn(gpa)); + + if (kvm_is_error_hva(hva)) + goto out_err; + + if (get_user_pages_remote_unlocked(kvm->mm, hva, 1, flags, page) != 1) + goto out_err; + + return write ? kmap_atomic(*page) : kmap(*page); +out_err: + srcu_read_unlock(&kvm->srcu, *srcu_idx); + return NULL; +} + +static void put_page_ptr(struct kvm *kvm, void *ptr, struct page *page, + bool write, int srcu_idx) +{ + if (write) + kunmap_atomic(ptr); + else + kunmap(ptr); + + put_page(page); + + srcu_read_unlock(&kvm->srcu, srcu_idx); +} + +int kvmi_cmd_read_physical(struct kvm *kvm, u64 gpa, size_t size, + int (*send)(struct kvm_introspection *, + const struct kvmi_msg_hdr *, + int err, const void *buf, size_t), + const struct kvmi_msg_hdr *ctx) +{ + struct page *page; + void *ptr_page; + int srcu_idx; + int err; + + ptr_page = get_page_ptr(kvm, gpa, &page, false, &srcu_idx); + if (!ptr_page) { + err = send(KVMI(kvm), ctx, -KVM_ENOENT, NULL, 0); + } else { + err = send(KVMI(kvm), ctx, 0, + ptr_page + (gpa & ~PAGE_MASK), size); + + put_page_ptr(kvm, ptr_page, page, false, srcu_idx); + } + + return err; +} + +int kvmi_cmd_write_physical(struct kvm *kvm, u64 gpa, size_t size, + const void *buf) +{ + int ec = -KVM_ENOENT; + struct page *page; + int srcu_idx; + void *ptr; + + ptr = get_page_ptr(kvm, gpa, &page, true, &srcu_idx); + if (ptr) { + memcpy(ptr + (gpa & ~PAGE_MASK), buf, size); + put_page_ptr(kvm, ptr, page, true, srcu_idx); + ec = 0; + } + + return ec; +} diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 987513d6c1a7..b7c8730e7e6d 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -29,6 +29,13 @@ bool kvmi_is_known_event(u16 id); bool kvmi_is_known_vm_event(u16 id); int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, u16 event_id, bool enable); +int kvmi_cmd_read_physical(struct kvm *kvm, u64 gpa, size_t size, + int (*send)(struct kvm_introspection *, + const struct kvmi_msg_hdr*, + int err, const void *buf, size_t), + const struct kvmi_msg_hdr *ctx); +int kvmi_cmd_write_physical(struct kvm *kvm, u64 gpa, size_t size, + const void *buf); /* arch */ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index ffd7d95b664f..4fe385265758 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -182,6 +182,48 @@ static int handle_vm_control_events(struct kvm_introspection *kvmi, return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); } +static bool invalid_page_access(u64 gpa, u64 size) +{ + u64 off = gpa & ~PAGE_MASK; + + return (size == 0 || size > PAGE_SIZE || off + size > PAGE_SIZE); +} + +static int handle_vm_read_physical(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vm_read_physical *req = _req; + + if (invalid_page_access(req->gpa, req->size) || + req->padding1 || req->padding2) + return kvmi_msg_vm_reply(kvmi, msg, -KVM_EINVAL, NULL, 0); + + return kvmi_cmd_read_physical(kvmi->kvm, req->gpa, req->size, + kvmi_msg_vm_reply, msg); +} + +static int handle_vm_write_physical(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vm_write_physical *req = _req; + int ec; + + if (msg->size < struct_size(req, data, req->size)) + return -EINVAL; + + if (invalid_page_access(req->gpa, req->size)) + ec = -KVM_EINVAL; + else if (req->padding1 || req->padding2) + ec = -KVM_EINVAL; + else + ec = kvmi_cmd_write_physical(kvmi->kvm, req->gpa, + req->size, req->data); + + return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); +} + /* * These commands are executed by the receiving thread. */ @@ -191,6 +233,8 @@ static kvmi_vm_msg_fct const msg_vm[] = { [KVMI_VM_CHECK_EVENT] = handle_vm_check_event, [KVMI_VM_CONTROL_EVENTS] = handle_vm_control_events, [KVMI_VM_GET_INFO] = handle_vm_get_info, + [KVMI_VM_READ_PHYSICAL] = handle_vm_read_physical, + [KVMI_VM_WRITE_PHYSICAL] = handle_vm_write_physical, }; static kvmi_vm_msg_fct get_vm_msg_handler(u16 id) From patchwork Wed Nov 25 09:35:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930943 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id DCE7BC71156 for ; Wed, 25 Nov 2020 09:43:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A141520B1F for ; Wed, 25 Nov 2020 09:43:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728809AbgKYJm6 (ORCPT ); Wed, 25 Nov 2020 04:42:58 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57138 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727630AbgKYJl6 (ORCPT ); Wed, 25 Nov 2020 04:41:58 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id C2264305D464; Wed, 25 Nov 2020 11:35:49 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id A080C3072785; Wed, 25 Nov 2020 11:35:49 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mircea_C=C3=AErjaliu?= , =?utf-8?q?Ada?= =?utf-8?q?lbert_Laz=C4=83r?= Subject: [PATCH v10 43/81] KVM: introspection: add vCPU related data Date: Wed, 25 Nov 2020 11:35:22 +0200 Message-Id: <20201125093600.2766-44-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mircea Cîrjaliu Add an introspection structure to all vCPUs when the VM is hooked. Signed-off-by: Mircea Cîrjaliu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvmi_host.h | 3 ++ include/linux/kvm_host.h | 1 + include/linux/kvmi_host.h | 6 ++++ virt/kvm/introspection/kvmi.c | 51 ++++++++++++++++++++++++++++++++ virt/kvm/kvm_main.c | 2 ++ 5 files changed, 63 insertions(+) diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 38c398262913..360a57dd9019 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -2,6 +2,9 @@ #ifndef _ASM_X86_KVMI_HOST_H #define _ASM_X86_KVMI_HOST_H +struct kvm_vcpu_arch_introspection { +}; + struct kvm_arch_introspection { }; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 51e6a4d7e5c9..60347c3a0e95 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -321,6 +321,7 @@ struct kvm_vcpu { bool ready; struct kvm_vcpu_arch arch; struct kvm_dirty_ring dirty_ring; + struct kvm_vcpu_introspection *kvmi; }; static inline int kvm_vcpu_exiting_guest_mode(struct kvm_vcpu *vcpu) diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index a59307dac6bf..9b0008c66321 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -6,6 +6,10 @@ #include +struct kvm_vcpu_introspection { + struct kvm_vcpu_arch_introspection arch; +}; + struct kvm_introspection { struct kvm_arch_introspection arch; struct kvm *kvm; @@ -28,6 +32,7 @@ int kvmi_init(void); void kvmi_uninit(void); void kvmi_create_vm(struct kvm *kvm); void kvmi_destroy_vm(struct kvm *kvm); +void kvmi_vcpu_uninit(struct kvm_vcpu *vcpu); int kvmi_ioctl_hook(struct kvm *kvm, const struct kvm_introspection_hook *hook); @@ -45,6 +50,7 @@ static inline int kvmi_init(void) { return 0; } static inline void kvmi_uninit(void) { } static inline void kvmi_create_vm(struct kvm *kvm) { } static inline void kvmi_destroy_vm(struct kvm *kvm) { } +static inline void kvmi_vcpu_uninit(struct kvm_vcpu *vcpu) { } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 2bff4707cc57..358dc6c2a969 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -118,8 +118,41 @@ void kvmi_uninit(void) kvmi_cache_destroy(); } +static bool kvmi_alloc_vcpui(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_introspection *vcpui; + + vcpui = kzalloc(sizeof(*vcpui), GFP_KERNEL); + if (!vcpui) + return false; + + vcpu->kvmi = vcpui; + + return true; +} + +static int kvmi_create_vcpui(struct kvm_vcpu *vcpu) +{ + if (!kvmi_alloc_vcpui(vcpu)) + return -ENOMEM; + + return 0; +} + +static void kvmi_free_vcpui(struct kvm_vcpu *vcpu) +{ + kfree(vcpu->kvmi); + vcpu->kvmi = NULL; +} + static void kvmi_free(struct kvm *kvm) { + struct kvm_vcpu *vcpu; + int i; + + kvm_for_each_vcpu(i, vcpu, kvm) + kvmi_free_vcpui(vcpu); + bitmap_free(kvm->kvmi->cmd_allow_mask); bitmap_free(kvm->kvmi->event_allow_mask); bitmap_free(kvm->kvmi->vm_event_enable_mask); @@ -128,10 +161,19 @@ static void kvmi_free(struct kvm *kvm) kvm->kvmi = NULL; } +void kvmi_vcpu_uninit(struct kvm_vcpu *vcpu) +{ + mutex_lock(&vcpu->kvm->kvmi_lock); + kvmi_free_vcpui(vcpu); + mutex_unlock(&vcpu->kvm->kvmi_lock); +} + static struct kvm_introspection * kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook) { struct kvm_introspection *kvmi; + struct kvm_vcpu *vcpu; + int i; kvmi = kzalloc(sizeof(*kvmi), GFP_KERNEL); if (!kvmi) @@ -157,6 +199,15 @@ kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook) atomic_set(&kvmi->ev_seq, 0); + kvm_for_each_vcpu(i, vcpu, kvm) { + int err = kvmi_create_vcpui(vcpu); + + if (err) { + kvmi_free(kvm); + return NULL; + } + } + kvmi->kvm = kvm; return kvmi; diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index f30d1bd9495a..55017a3f6283 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -418,6 +418,7 @@ static void kvm_vcpu_init(struct kvm_vcpu *vcpu, struct kvm *kvm, unsigned id) void kvm_vcpu_destroy(struct kvm_vcpu *vcpu) { + kvmi_vcpu_uninit(vcpu); kvm_dirty_ring_free(&vcpu->dirty_ring); kvm_arch_vcpu_destroy(vcpu); @@ -3250,6 +3251,7 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id) unlock_vcpu_destroy: mutex_unlock(&kvm->lock); + kvmi_vcpu_uninit(vcpu); kvm_dirty_ring_free(&vcpu->dirty_ring); arch_vcpu_destroy: kvm_arch_vcpu_destroy(vcpu); From patchwork Wed Nov 25 09:35:23 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930949 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1C76EC64E7B for ; Wed, 25 Nov 2020 09:43:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id C522B2083E for ; Wed, 25 Nov 2020 09:43:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727713AbgKYJl4 (ORCPT ); Wed, 25 Nov 2020 04:41:56 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57134 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726903AbgKYJlz (ORCPT ); Wed, 25 Nov 2020 04:41:55 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E794D305D465; Wed, 25 Nov 2020 11:35:49 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C59513072784; Wed, 25 Nov 2020 11:35:49 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= , =?utf-8?b?Tmlj?= =?utf-8?b?dciZb3IgQ8OuyJt1?= Subject: [PATCH v10 44/81] KVM: introspection: add a jobs list to every introspected vCPU Date: Wed, 25 Nov 2020 11:35:23 +0200 Message-Id: <20201125093600.2766-45-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Every vCPU has a lock-protected list in which the receiving thread places the jobs that has to be done by the vCPU thread once it is kicked out of guest (KVM_REQ_INTROSPECTION). Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- include/linux/kvmi_host.h | 10 +++++ virt/kvm/introspection/kvmi.c | 72 ++++++++++++++++++++++++++++++- virt/kvm/introspection/kvmi_int.h | 1 + 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 9b0008c66321..b3874419511d 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -6,8 +6,18 @@ #include +struct kvmi_job { + struct list_head link; + void *ctx; + void (*fct)(struct kvm_vcpu *vcpu, void *ctx); + void (*free_fct)(void *ctx); +}; + struct kvm_vcpu_introspection { struct kvm_vcpu_arch_introspection arch; + + struct list_head job_list; + spinlock_t job_lock; }; struct kvm_introspection { diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 358dc6c2a969..cdb4175aecff 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -23,6 +23,7 @@ static DECLARE_BITMAP(Kvmi_known_vm_events, KVMI_NUM_EVENTS); static DECLARE_BITMAP(Kvmi_known_vcpu_events, KVMI_NUM_EVENTS); static struct kmem_cache *msg_cache; +static struct kmem_cache *job_cache; void *kvmi_msg_alloc(void) { @@ -39,14 +40,19 @@ static void kvmi_cache_destroy(void) { kmem_cache_destroy(msg_cache); msg_cache = NULL; + kmem_cache_destroy(job_cache); + job_cache = NULL; } static int kvmi_cache_create(void) { msg_cache = kmem_cache_create("kvmi_msg", KVMI_MSG_SIZE_ALLOC, 4096, SLAB_ACCOUNT, NULL); + job_cache = kmem_cache_create("kvmi_job", + sizeof(struct kvmi_job), + 0, SLAB_ACCOUNT, NULL); - if (!msg_cache) { + if (!msg_cache || !job_cache) { kvmi_cache_destroy(); return -1; @@ -118,6 +124,48 @@ void kvmi_uninit(void) kvmi_cache_destroy(); } +static int __kvmi_add_job(struct kvm_vcpu *vcpu, + void (*fct)(struct kvm_vcpu *vcpu, void *ctx), + void *ctx, void (*free_fct)(void *ctx)) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + struct kvmi_job *job; + + job = kmem_cache_zalloc(job_cache, GFP_KERNEL); + if (unlikely(!job)) + return -ENOMEM; + + INIT_LIST_HEAD(&job->link); + job->fct = fct; + job->ctx = ctx; + job->free_fct = free_fct; + + spin_lock(&vcpui->job_lock); + list_add_tail(&job->link, &vcpui->job_list); + spin_unlock(&vcpui->job_lock); + + return 0; +} + +int kvmi_add_job(struct kvm_vcpu *vcpu, + void (*fct)(struct kvm_vcpu *vcpu, void *ctx), + void *ctx, void (*free_fct)(void *ctx)) +{ + int err; + + err = __kvmi_add_job(vcpu, fct, ctx, free_fct); + + return err; +} + +static void kvmi_free_job(struct kvmi_job *job) +{ + if (job->free_fct) + job->free_fct(job->ctx); + + kmem_cache_free(job_cache, job); +} + static bool kvmi_alloc_vcpui(struct kvm_vcpu *vcpu) { struct kvm_vcpu_introspection *vcpui; @@ -126,6 +174,9 @@ static bool kvmi_alloc_vcpui(struct kvm_vcpu *vcpu) if (!vcpui) return false; + INIT_LIST_HEAD(&vcpui->job_list); + spin_lock_init(&vcpui->job_lock); + vcpu->kvmi = vcpui; return true; @@ -139,9 +190,26 @@ static int kvmi_create_vcpui(struct kvm_vcpu *vcpu) return 0; } +static void kvmi_free_vcpu_jobs(struct kvm_vcpu_introspection *vcpui) +{ + struct kvmi_job *cur, *next; + + list_for_each_entry_safe(cur, next, &vcpui->job_list, link) { + list_del(&cur->link); + kvmi_free_job(cur); + } +} + static void kvmi_free_vcpui(struct kvm_vcpu *vcpu) { - kfree(vcpu->kvmi); + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + + if (!vcpui) + return; + + kvmi_free_vcpu_jobs(vcpui); + + kfree(vcpui); vcpu->kvmi = NULL; } diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index b7c8730e7e6d..c3aa12554c2b 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -7,6 +7,7 @@ #include #define KVMI(kvm) ((kvm)->kvmi) +#define VCPUI(vcpu) ((vcpu)->kvmi) /* * This limit is used to accommodate the largest known fixed-length * message. From patchwork Wed Nov 25 09:35:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930929 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 43C26C63777 for ; Wed, 25 Nov 2020 09:42:34 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 078D22083E for ; Wed, 25 Nov 2020 09:42:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728711AbgKYJmb (ORCPT ); Wed, 25 Nov 2020 04:42:31 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57200 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728528AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 1D257305D466; Wed, 25 Nov 2020 11:35:50 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id ECBDB3072785; Wed, 25 Nov 2020 11:35:49 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Mircea_C?= =?utf-8?q?=C3=AErjaliu?= , =?utf-8?q?Adalbert_La?= =?utf-8?q?z=C4=83r?= Subject: [PATCH v10 45/81] KVM: introspection: handle vCPU introspection requests Date: Wed, 25 Nov 2020 11:35:24 +0200 Message-Id: <20201125093600.2766-46-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu The receiving thread dispatches the vCPU introspection commands by adding them to the vCPU's jobs list and kicking the vCPU. Before entering in guest, the vCPU thread checks the introspection request (KVM_REQ_INTROSPECTION) and runs its queued jobs. Signed-off-by: Mihai Donțu Co-developed-by: Mircea Cîrjaliu Signed-off-by: Mircea Cîrjaliu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/x86.c | 3 ++ include/linux/kvm_host.h | 1 + include/linux/kvmi_host.h | 4 ++ virt/kvm/introspection/kvmi.c | 73 +++++++++++++++++++++++++++++++++++ virt/kvm/kvm_main.c | 2 + 5 files changed, 83 insertions(+) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index fcf7e68cb6c8..4e91d6794b5e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9147,6 +9147,9 @@ static int vcpu_run(struct kvm_vcpu *vcpu) vcpu->arch.l1tf_flush_l1d = true; for (;;) { + if (kvm_check_request(KVM_REQ_INTROSPECTION, vcpu)) + kvmi_handle_requests(vcpu); + if (kvm_vcpu_running(vcpu)) { r = vcpu_enter_guest(vcpu); } else { diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 60347c3a0e95..4d02f682782a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -147,6 +147,7 @@ static inline bool is_error_page(struct page *page) #define KVM_REQ_MMU_RELOAD (1 | KVM_REQUEST_WAIT | KVM_REQUEST_NO_WAKEUP) #define KVM_REQ_PENDING_TIMER 2 #define KVM_REQ_UNHALT 3 +#define KVM_REQ_INTROSPECTION 4 #define KVM_REQUEST_ARCH_BASE 8 #define KVM_ARCH_REQ_FLAGS(nr, flags) ({ \ diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index b3874419511d..736edb400c05 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -53,6 +53,8 @@ int kvmi_ioctl_event(struct kvm *kvm, const struct kvm_introspection_feature *feat); int kvmi_ioctl_preunhook(struct kvm *kvm); +void kvmi_handle_requests(struct kvm_vcpu *vcpu); + #else static inline int kvmi_version(void) { return 0; } @@ -62,6 +64,8 @@ static inline void kvmi_create_vm(struct kvm *kvm) { } static inline void kvmi_destroy_vm(struct kvm *kvm) { } static inline void kvmi_vcpu_uninit(struct kvm_vcpu *vcpu) { } +static inline void kvmi_handle_requests(struct kvm_vcpu *vcpu) { } + #endif /* CONFIG_KVM_INTROSPECTION */ #endif diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index cdb4175aecff..b608751780fc 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -124,6 +124,12 @@ void kvmi_uninit(void) kvmi_cache_destroy(); } +static void kvmi_make_request(struct kvm_vcpu *vcpu) +{ + kvm_make_request(KVM_REQ_INTROSPECTION, vcpu); + kvm_vcpu_kick(vcpu); +} + static int __kvmi_add_job(struct kvm_vcpu *vcpu, void (*fct)(struct kvm_vcpu *vcpu, void *ctx), void *ctx, void (*free_fct)(void *ctx)) @@ -155,6 +161,9 @@ int kvmi_add_job(struct kvm_vcpu *vcpu, err = __kvmi_add_job(vcpu, fct, ctx, free_fct); + if (!err) + kvmi_make_request(vcpu); + return err; } @@ -323,6 +332,14 @@ int kvmi_ioctl_unhook(struct kvm *kvm) return 0; } +struct kvm_introspection * __must_check kvmi_get(struct kvm *kvm) +{ + if (refcount_inc_not_zero(&kvm->kvmi_ref)) + return kvm->kvmi; + + return NULL; +} + void kvmi_put(struct kvm *kvm) { if (refcount_dec_and_test(&kvm->kvmi_ref)) @@ -340,6 +357,19 @@ static int __kvmi_hook(struct kvm *kvm, return 0; } +static void kvmi_job_release_vcpu(struct kvm_vcpu *vcpu, void *ctx) +{ +} + +static void kvmi_release_vcpus(struct kvm *kvm) +{ + struct kvm_vcpu *vcpu; + int i; + + kvm_for_each_vcpu(i, vcpu, kvm) + kvmi_add_job(vcpu, kvmi_job_release_vcpu, NULL, NULL); +} + static int kvmi_recv_thread(void *arg) { struct kvm_introspection *kvmi = arg; @@ -350,6 +380,8 @@ static int kvmi_recv_thread(void *arg) /* Signal userspace and prevent the vCPUs from sending events. */ kvmi_sock_shutdown(kvmi); + kvmi_release_vcpus(kvmi->kvm); + kvmi_put(kvmi->kvm); return 0; } @@ -381,6 +413,10 @@ int kvmi_hook(struct kvm *kvm, const struct kvm_introspection_hook *hook) init_completion(&kvm->kvmi_complete); refcount_set(&kvm->kvmi_ref, 1); + /* + * Paired with refcount_inc_not_zero() from kvmi_get(). + */ + smp_wmb(); kvmi->recv = kthread_run(kvmi_recv_thread, kvmi, "kvmi-recv"); if (IS_ERR(kvmi->recv)) { @@ -669,3 +705,40 @@ int kvmi_cmd_write_physical(struct kvm *kvm, u64 gpa, size_t size, return ec; } + +static struct kvmi_job *kvmi_pull_job(struct kvm_vcpu_introspection *vcpui) +{ + struct kvmi_job *job = NULL; + + spin_lock(&vcpui->job_lock); + job = list_first_entry_or_null(&vcpui->job_list, typeof(*job), link); + if (job) + list_del(&job->link); + spin_unlock(&vcpui->job_lock); + + return job; +} + +void kvmi_run_jobs(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + struct kvmi_job *job; + + while ((job = kvmi_pull_job(vcpui))) { + job->fct(vcpu, job->ctx); + kvmi_free_job(job); + } +} + +void kvmi_handle_requests(struct kvm_vcpu *vcpu) +{ + struct kvm_introspection *kvmi; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return; + + kvmi_run_jobs(vcpu); + + kvmi_put(vcpu->kvm); +} diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 55017a3f6283..667b358ceb9c 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2793,6 +2793,8 @@ static int kvm_vcpu_check_block(struct kvm_vcpu *vcpu) goto out; if (signal_pending(current)) goto out; + if (kvm_test_request(KVM_REQ_INTROSPECTION, vcpu)) + goto out; ret = 0; out: From patchwork Wed Nov 25 09:35:25 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930917 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A48FAC6379D for ; Wed, 25 Nov 2020 09:42:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5C05E2083E for ; Wed, 25 Nov 2020 09:42:16 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728646AbgKYJmP (ORCPT ); Wed, 25 Nov 2020 04:42:15 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57200 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728593AbgKYJmK (ORCPT ); Wed, 25 Nov 2020 04:42:10 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 4EC57305D467; Wed, 25 Nov 2020 11:35:50 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 21A1C3072784; Wed, 25 Nov 2020 11:35:50 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?b?TmljdciZ?= =?utf-8?b?b3IgQ8OuyJt1?= , =?utf-8?q?Adalbert_Laz?= =?utf-8?q?=C4=83r?= Subject: [PATCH v10 46/81] KVM: introspection: handle vCPU commands Date: Wed, 25 Nov 2020 11:35:25 +0200 Message-Id: <20201125093600.2766-47-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu Based on the common structure (kvmi_vcpu_hdr) used for all vCPU commands, the receiving thread validates and dispatches the message to the proper vCPU (adding the handling function to its jobs list). Signed-off-by: Mihai Donțu Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 8 ++ arch/x86/kvm/Makefile | 2 +- arch/x86/kvm/kvmi_msg.c | 17 ++++ include/uapi/linux/kvmi.h | 6 ++ virt/kvm/introspection/kvmi_int.h | 16 ++++ virt/kvm/introspection/kvmi_msg.c | 150 +++++++++++++++++++++++++++++- 6 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 arch/x86/kvm/kvmi_msg.c diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 7812d62240c0..4d340528d2f4 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -221,6 +221,14 @@ The following C structures are meant to be used directly when communicating over the wire. The peer that detects any size mismatch should simply close the connection and report the error. +The vCPU commands start with:: + + struct kvmi_vcpu_hdr { + __u16 vcpu; + __u16 padding1; + __u32 padding2; + } + 1. KVMI_GET_VERSION ------------------- diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index 8fad40649bcf..6d04731e235e 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -14,7 +14,7 @@ kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \ $(KVM)/eventfd.o $(KVM)/irqchip.o $(KVM)/vfio.o \ $(KVM)/dirty_ring.o kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o -kvm-$(CONFIG_KVM_INTROSPECTION) += $(KVMI)/kvmi.o $(KVMI)/kvmi_msg.o kvmi.o +kvm-$(CONFIG_KVM_INTROSPECTION) += $(KVMI)/kvmi.o $(KVMI)/kvmi_msg.o kvmi.o kvmi_msg.o kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \ i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \ diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c new file mode 100644 index 000000000000..0f4717ca5fa8 --- /dev/null +++ b/arch/x86/kvm/kvmi_msg.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * KVM introspection (message handling) - x86 + * + * Copyright (C) 2020 Bitdefender S.R.L. + * + */ + +#include "../../../virt/kvm/introspection/kvmi_int.h" + +static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { +}; + +kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) +{ + return id < ARRAY_SIZE(msg_vcpu) ? msg_vcpu[id] : NULL; +} diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 048afad01be6..7ba1c8758aba 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -107,4 +107,10 @@ struct kvmi_vm_write_physical { __u8 data[0]; }; +struct kvmi_vcpu_hdr { + __u16 vcpu; + __u16 padding1; + __u32 padding2; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index c3aa12554c2b..c3e4da7e7f20 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -14,6 +14,18 @@ */ #define KVMI_MAX_MSG_SIZE (4096 * 2 - sizeof(struct kvmi_msg_hdr)) +struct kvmi_vcpu_msg_job { + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + } *msg; + struct kvm_vcpu *vcpu; +}; + +typedef int (*kvmi_vcpu_msg_job_fct)(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *req); + /* kvmi_msg.c */ bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd); void kvmi_sock_shutdown(struct kvm_introspection *kvmi); @@ -28,6 +40,9 @@ bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id); bool kvmi_is_event_allowed(struct kvm_introspection *kvmi, u16 id); bool kvmi_is_known_event(u16 id); bool kvmi_is_known_vm_event(u16 id); +int kvmi_add_job(struct kvm_vcpu *vcpu, + void (*fct)(struct kvm_vcpu *vcpu, void *ctx), + void *ctx, void (*free_fct)(void *ctx)); int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, u16 event_id, bool enable); int kvmi_cmd_read_physical(struct kvm *kvm, u64 gpa, size_t size, @@ -40,5 +55,6 @@ int kvmi_cmd_write_physical(struct kvm *kvm, u64 gpa, size_t size, /* arch */ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); +kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 4fe385265758..6f2fe245a8b1 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -13,6 +13,7 @@ typedef int (*kvmi_vm_msg_fct)(struct kvm_introspection *kvmi, const void *req); static bool is_vm_command(u16 id); +static bool is_vcpu_command(u16 id); bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd) { @@ -104,6 +105,17 @@ static int kvmi_msg_vm_reply(struct kvm_introspection *kvmi, return kvmi_msg_reply(kvmi, msg, err, rpl, rpl_size); } +static struct kvm_vcpu *kvmi_get_vcpu(struct kvm_introspection *kvmi, + unsigned int vcpu_idx) +{ + struct kvm *kvm = kvmi->kvm; + + if (vcpu_idx >= atomic_read(&kvm->online_vcpus)) + return NULL; + + return kvm_get_vcpu(kvm, vcpu_idx); +} + static int handle_get_version(struct kvm_introspection *kvmi, const struct kvmi_msg_hdr *msg, const void *req) { @@ -125,7 +137,7 @@ static int handle_vm_check_command(struct kvm_introspection *kvmi, if (req->padding1 || req->padding2) ec = -KVM_EINVAL; - else if (!is_vm_command(req->id)) + else if (!is_vm_command(req->id) && !is_vcpu_command(req->id)) ec = -KVM_ENOENT; else if (!kvmi_is_command_allowed(kvmi, req->id)) ec = -KVM_EPERM; @@ -254,6 +266,74 @@ static bool is_vm_command(u16 id) return is_vm_message(id) && id != KVMI_VM_EVENT; } +/* + * These functions are executed from the vCPU thread. The receiving thread + * passes the messages using a newly allocated 'struct kvmi_vcpu_msg_job' + * and signals the vCPU to handle the message (which includes + * sending back the reply). + */ +static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { +}; + +static kvmi_vcpu_msg_job_fct get_vcpu_msg_handler(u16 id) +{ + kvmi_vcpu_msg_job_fct fct; + + fct = id < ARRAY_SIZE(msg_vcpu) ? msg_vcpu[id] : NULL; + + if (!fct) + fct = kvmi_arch_vcpu_msg_handler(id); + + return fct; +} + +static bool is_vcpu_message(u16 id) +{ + bool is_vcpu_msg_id = id & 1; + + return is_vcpu_msg_id && !!get_vcpu_msg_handler(id); +} + +static bool is_vcpu_command(u16 id) +{ + return is_vcpu_message(id); +} + +static void kvmi_job_vcpu_msg(struct kvm_vcpu *vcpu, void *ctx) +{ + struct kvmi_vcpu_msg_job *job = ctx; + kvmi_vcpu_msg_job_fct fct; + int err; + + job->vcpu = vcpu; + + fct = get_vcpu_msg_handler(job->msg->hdr.id); + err = fct(job, &job->msg->hdr, job->msg + 1); + + /* + * The soft errors are sent with the reply. + * On hard errors, like this one, + * we shut down the socket. + */ + if (err) + kvmi_sock_shutdown(KVMI(vcpu->kvm)); +} + +static void kvmi_free_ctx(void *_ctx) +{ + const struct kvmi_vcpu_msg_job *ctx = _ctx; + + kvmi_msg_free(ctx->msg); + kfree(ctx); +} + +static int kvmi_msg_queue_to_vcpu(struct kvm_vcpu *vcpu, + const struct kvmi_vcpu_msg_job *cmd) +{ + return kvmi_add_job(vcpu, kvmi_job_vcpu_msg, (void *)cmd, + kvmi_free_ctx); +} + static struct kvmi_msg_hdr *kvmi_msg_recv(struct kvm_introspection *kvmi) { struct kvmi_msg_hdr *msg; @@ -307,9 +387,72 @@ static int kvmi_msg_handle_vm_cmd(struct kvm_introspection *kvmi, return kvmi_msg_do_vm_cmd(kvmi, msg); } +static bool vcpu_can_handle_messages(struct kvm_vcpu *vcpu) +{ + return vcpu->arch.mp_state != KVM_MP_STATE_UNINITIALIZED; +} + +static int kvmi_get_vcpu_if_ready(struct kvm_introspection *kvmi, + unsigned int vcpu_idx, + struct kvm_vcpu **vcpu) +{ + *vcpu = kvmi_get_vcpu(kvmi, vcpu_idx); + if (*vcpu == NULL) + return -KVM_EINVAL; + + if (!vcpu_can_handle_messages(*vcpu)) + return -KVM_EAGAIN; + + return 0; +} + +static int kvmi_msg_dispatch_vcpu_msg(struct kvm_introspection *kvmi, + struct kvmi_msg_hdr *msg, + struct kvm_vcpu *vcpu) +{ + struct kvmi_vcpu_msg_job *job_cmd; + int err; + + job_cmd = kzalloc(sizeof(*job_cmd), GFP_KERNEL); + if (!job_cmd) + return -ENOMEM; + + job_cmd->msg = (void *)msg; + + err = kvmi_msg_queue_to_vcpu(vcpu, job_cmd); + if (err) + kfree(job_cmd); + + return err; +} + +static int kvmi_msg_handle_vcpu_msg(struct kvm_introspection *kvmi, + struct kvmi_msg_hdr *msg, + bool *queued) +{ + struct kvmi_vcpu_hdr *vcpu_hdr = (struct kvmi_vcpu_hdr *)(msg + 1); + struct kvm_vcpu *vcpu = NULL; + int err, ec; + + if (!kvmi_is_command_allowed(kvmi, msg->id)) + return kvmi_msg_vm_reply_ec(kvmi, msg, -KVM_EPERM); + + if (vcpu_hdr->padding1 || vcpu_hdr->padding2) + return kvmi_msg_vm_reply_ec(kvmi, msg, -KVM_EINVAL); + + ec = kvmi_get_vcpu_if_ready(kvmi, vcpu_hdr->vcpu, &vcpu); + if (ec) + return kvmi_msg_vm_reply_ec(kvmi, msg, ec); + + err = kvmi_msg_dispatch_vcpu_msg(kvmi, msg, vcpu); + *queued = err == 0; + return err; +} + bool kvmi_msg_process(struct kvm_introspection *kvmi) { struct kvmi_msg_hdr *msg; + bool queued = false; int err = -1; msg = kvmi_msg_recv(kvmi); @@ -318,10 +461,13 @@ bool kvmi_msg_process(struct kvm_introspection *kvmi) if (is_vm_command(msg->id)) err = kvmi_msg_handle_vm_cmd(kvmi, msg); + else if (is_vcpu_message(msg->id)) + err = kvmi_msg_handle_vcpu_msg(kvmi, msg, &queued); else err = kvmi_msg_vm_reply_ec(kvmi, msg, -KVM_ENOSYS); - kvmi_msg_free(msg); + if (!queued) + kvmi_msg_free(msg); out: return err == 0; } From patchwork Wed Nov 25 09:35:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930953 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-10.9 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,UNWANTED_LANGUAGE_BODY, URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id D54DFC83012 for ; Wed, 25 Nov 2020 09:42:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A11072083E for ; Wed, 25 Nov 2020 09:42:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728748AbgKYJmj (ORCPT ); Wed, 25 Nov 2020 04:42:39 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57100 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728459AbgKYJmE (ORCPT ); Wed, 25 Nov 2020 04:42:04 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 76477305D468; Wed, 25 Nov 2020 11:35:50 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 514363072785; Wed, 25 Nov 2020 11:35:50 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 47/81] KVM: introspection: add KVMI_VCPU_GET_INFO Date: Wed, 25 Nov 2020 11:35:26 +0200 Message-Id: <20201125093600.2766-48-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This command returns the TSC frequency (in HZ) for the specified vCPU if available (otherwise it returns zero). Signed-off-by: Mihai Donțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 29 ++++ arch/x86/include/asm/kvmi_host.h | 2 + arch/x86/include/uapi/asm/kvmi.h | 13 ++ arch/x86/kvm/kvmi_msg.c | 14 ++ include/uapi/linux/kvmi.h | 2 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 144 +++++++++++++++++- virt/kvm/introspection/kvmi_int.h | 3 + virt/kvm/introspection/kvmi_msg.c | 9 ++ 8 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 arch/x86/include/uapi/asm/kvmi.h diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 4d340528d2f4..902ced4dd0c4 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -441,6 +441,35 @@ one page (offset + size <= PAGE_SIZE). * -KVM_EINVAL - the specified gpa/size pair is invalid * -KVM_EINVAL - the padding is not zero +8. KVMI_VCPU_GET_INFO +--------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_vcpu_get_info_reply { + __u64 tsc_speed; + }; + +Returns the TSC frequency (in HZ) for the specified vCPU if available +(otherwise it returns zero). + +:Errors: + +* -KVM_EINVAL - the padding is not zero +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EAGAIN - the selected vCPU can't be introspected yet + Events ====== diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 360a57dd9019..05ade3a16b24 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -2,6 +2,8 @@ #ifndef _ASM_X86_KVMI_HOST_H #define _ASM_X86_KVMI_HOST_H +#include + struct kvm_vcpu_arch_introspection { }; diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h new file mode 100644 index 000000000000..2b6192e1a9a4 --- /dev/null +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _UAPI_ASM_X86_KVMI_H +#define _UAPI_ASM_X86_KVMI_H + +/* + * KVM introspection - x86 specific structures and definitions + */ + +struct kvmi_vcpu_get_info_reply { + __u64 tsc_speed; +}; + +#endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 0f4717ca5fa8..77552bf50984 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -8,7 +8,21 @@ #include "../../../virt/kvm/introspection/kvmi_int.h" +static int handle_vcpu_get_info(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *req) +{ + struct kvmi_vcpu_get_info_reply rpl; + + memset(&rpl, 0, sizeof(rpl)); + if (kvm_has_tsc_control) + rpl.tsc_speed = 1000ul * job->vcpu->arch.virtual_tsc_khz; + + return kvmi_msg_vcpu_reply(job, msg, 0, &rpl, sizeof(rpl)); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { + [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, }; kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 7ba1c8758aba..da766427231e 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -31,6 +31,8 @@ enum { }; enum { + KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), + KVMI_NEXT_VCPU_MESSAGE }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index b493edb534b0..9350ba8b7f9b 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "test_util.h" @@ -18,6 +19,7 @@ #include "linux/kvm_para.h" #include "linux/kvmi.h" +#include "asm/kvmi.h" #define VCPU_ID 1 @@ -25,12 +27,46 @@ static int socket_pair[2]; #define Kvm_socket socket_pair[0] #define Userspace_socket socket_pair[1] +static int test_id; static vm_vaddr_t test_gva; static void *test_hva; static vm_paddr_t test_gpa; static int page_size; +struct vcpu_worker_data { + struct kvm_vm *vm; + int vcpu_id; + int test_id; +}; + +enum { + GUEST_TEST_NOOP = 0, +}; + +#define GUEST_REQUEST_TEST() GUEST_SYNC(0) +#define GUEST_SIGNAL_TEST_DONE() GUEST_SYNC(1) + +#define HOST_SEND_TEST(uc) (uc.cmd == UCALL_SYNC && uc.args[1] == 0) +#define HOST_TEST_DONE(uc) (uc.cmd == UCALL_SYNC && uc.args[1] == 1) + +static int guest_test_id(void) +{ + GUEST_REQUEST_TEST(); + return READ_ONCE(test_id); +} + +static void guest_code(void) +{ + while (true) { + switch (guest_test_id()) { + case GUEST_TEST_NOOP: + break; + } + GUEST_SIGNAL_TEST_DONE(); + } +} + void setup_socket(void) { int r; @@ -530,6 +566,111 @@ static void test_memory_access(struct kvm_vm *vm) read_invalid_guest_page(vm); } +static void *vcpu_worker(void *data) +{ + struct vcpu_worker_data *ctx = data; + struct kvm_run *run; + + run = vcpu_state(ctx->vm, ctx->vcpu_id); + + while (true) { + struct ucall uc; + + vcpu_run(ctx->vm, ctx->vcpu_id); + + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "vcpu_run() failed, test_id %d, exit reason %u (%s)\n", + ctx->test_id, run->exit_reason, + exit_reason_str(run->exit_reason)); + + TEST_ASSERT(get_ucall(ctx->vm, ctx->vcpu_id, &uc), + "No guest request\n"); + + if (HOST_SEND_TEST(uc)) { + test_id = READ_ONCE(ctx->test_id); + sync_global_to_guest(ctx->vm, test_id); + } else if (HOST_TEST_DONE(uc)) { + break; + } + } + + return NULL; +} + +static pthread_t start_vcpu_worker(struct vcpu_worker_data *data) +{ + pthread_t thread_id; + + pthread_create(&thread_id, NULL, vcpu_worker, data); + + return thread_id; +} + +static void wait_vcpu_worker(pthread_t vcpu_thread) +{ + pthread_join(vcpu_thread, NULL); +} + +static int do_vcpu_command(struct kvm_vm *vm, int cmd_id, + struct kvmi_msg_hdr *req, size_t req_size, + void *rpl, size_t rpl_size) +{ + struct vcpu_worker_data data = {.vm = vm, .vcpu_id = VCPU_ID }; + pthread_t vcpu_thread; + int r; + + vcpu_thread = start_vcpu_worker(&data); + + r = do_command(cmd_id, req, req_size, rpl, rpl_size); + + wait_vcpu_worker(vcpu_thread); + return r; +} + +static int do_vcpu0_command(struct kvm_vm *vm, int cmd_id, + struct kvmi_msg_hdr *req, size_t req_size, + void *rpl, size_t rpl_size) +{ + struct kvmi_vcpu_hdr *vcpu_hdr = (struct kvmi_vcpu_hdr *)(req + 1); + + vcpu_hdr->vcpu = 0; + + return do_vcpu_command(vm, cmd_id, req, req_size, rpl, rpl_size); +} + +static void test_vcpu0_command(struct kvm_vm *vm, int cmd_id, + struct kvmi_msg_hdr *req, size_t req_size, + void *rpl, size_t rpl_size, + int expected_err) +{ + int r; + + r = do_vcpu0_command(vm, cmd_id, req, req_size, rpl, rpl_size); + TEST_ASSERT(r == expected_err, + "Command %d failed, error %d (%s) instead of %d (%s)\n", + cmd_id, -r, kvm_strerror(-r), + expected_err, kvm_strerror(expected_err)); +} + +static void test_cmd_vcpu_get_info(struct kvm_vm *vm) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + } req = {}; + struct kvmi_vcpu_get_info_reply rpl; + int cmd_id = KVMI_VCPU_GET_INFO; + + test_vcpu0_command(vm, cmd_id, &req.hdr, sizeof(req), + &rpl, sizeof(rpl), 0); + + pr_debug("tsc_speed: %llu HZ\n", rpl.tsc_speed); + + req.vcpu_hdr.vcpu = 99; + test_vm_command(cmd_id, &req.hdr, sizeof(req), + &rpl, sizeof(rpl), -KVM_EINVAL); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -544,6 +685,7 @@ static void test_introspection(struct kvm_vm *vm) test_event_unhook(vm); test_cmd_vm_control_events(vm); test_memory_access(vm); + test_cmd_vcpu_get_info(vm); unhook_introspection(vm); } @@ -566,7 +708,7 @@ int main(int argc, char *argv[]) exit(KSFT_SKIP); } - vm = vm_create_default(VCPU_ID, 0, NULL); + vm = vm_create_default(VCPU_ID, 0, guest_code); vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); page_size = getpagesize(); diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index c3e4da7e7f20..126e72201518 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -32,6 +32,9 @@ void kvmi_sock_shutdown(struct kvm_introspection *kvmi); void kvmi_sock_put(struct kvm_introspection *kvmi); bool kvmi_msg_process(struct kvm_introspection *kvmi); int kvmi_msg_send_unhook(struct kvm_introspection *kvmi); +int kvmi_msg_vcpu_reply(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, int err, + const void *rpl, size_t rpl_size); /* kvmi.c */ void *kvmi_msg_alloc(void); diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 6f2fe245a8b1..efd04b4aa4ff 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -105,6 +105,15 @@ static int kvmi_msg_vm_reply(struct kvm_introspection *kvmi, return kvmi_msg_reply(kvmi, msg, err, rpl, rpl_size); } +int kvmi_msg_vcpu_reply(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, int err, + const void *rpl, size_t rpl_size) +{ + struct kvm_introspection *kvmi = KVMI(job->vcpu->kvm); + + return kvmi_msg_reply(kvmi, msg, err, rpl, rpl_size); +} + static struct kvm_vcpu *kvmi_get_vcpu(struct kvm_introspection *kvmi, unsigned int vcpu_idx) { From patchwork Wed Nov 25 09:35:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930819 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8850BC8300C for ; Wed, 25 Nov 2020 09:42:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 366B8206F9 for ; Wed, 25 Nov 2020 09:42:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728567AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57340 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728193AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 9A899305D497; Wed, 25 Nov 2020 11:35:50 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 7A4203072784; Wed, 25 Nov 2020 11:35:50 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 48/81] KVM: introspection: add KVMI_VM_PAUSE_VCPU Date: Wed, 25 Nov 2020 11:35:27 +0200 Message-Id: <20201125093600.2766-49-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This command increments a pause requests counter for a vCPU and kicks it out of guest. The introspection tool can pause a VM by sending this command for all vCPUs. If it sets 'wait=1', it can consider that the VM is paused when it receives the reply for the last KVMI_VM_PAUSE_VCPU command. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 39 +++++++++++++++ include/linux/kvmi_host.h | 2 + include/uapi/linux/kvmi.h | 8 ++++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 30 ++++++++++++ virt/kvm/introspection/kvmi.c | 47 +++++++++++++++++-- virt/kvm/introspection/kvmi_int.h | 1 + virt/kvm/introspection/kvmi_msg.c | 24 ++++++++++ 7 files changed, 147 insertions(+), 4 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 902ced4dd0c4..a71fb78d546e 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -470,6 +470,45 @@ Returns the TSC frequency (in HZ) for the specified vCPU if available * -KVM_EINVAL - the selected vCPU is invalid * -KVM_EAGAIN - the selected vCPU can't be introspected yet +9. KVMI_VM_PAUSE_VCPU +--------------------- + +:Architectures: all +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vm_pause_vcpu { + __u16 vcpu; + __u8 wait; + __u8 padding1; + __u32 padding2; + }; + +:Returns: + +:: + + struct kvmi_error_code; + +Kicks the vCPU out of guest. + +If `wait` is 1, the command will wait for vCPU to acknowledge the IPI. + +The vCPU will handle the pending commands/events and send the +*KVMI_VCPU_EVENT_PAUSE* event (one for every successful *KVMI_VM_PAUSE_VCPU* +command) before returning to guest. + +:Errors: + +* -KVM_EINVAL - the padding is not zero +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_EBUSY - the selected vCPU has too many queued + *KVMI_VCPU_EVENT_PAUSE* events +* -KVM_EPERM - the *KVMI_VCPU_EVENT_PAUSE* event is disallowed + Events ====== diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 736edb400c05..59e645d9ea34 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -18,6 +18,8 @@ struct kvm_vcpu_introspection { struct list_head job_list; spinlock_t job_lock; + + atomic_t pause_requests; }; struct kvm_introspection { diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index da766427231e..bb90d03f059b 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -26,6 +26,7 @@ enum { KVMI_VM_CONTROL_EVENTS = KVMI_VM_MESSAGE_ID(5), KVMI_VM_READ_PHYSICAL = KVMI_VM_MESSAGE_ID(6), KVMI_VM_WRITE_PHYSICAL = KVMI_VM_MESSAGE_ID(7), + KVMI_VM_PAUSE_VCPU = KVMI_VM_MESSAGE_ID(8), KVMI_NEXT_VM_MESSAGE }; @@ -115,4 +116,11 @@ struct kvmi_vcpu_hdr { __u32 padding2; }; +struct kvmi_vm_pause_vcpu { + __u16 vcpu; + __u8 wait; + __u8 padding1; + __u32 padding2; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 9350ba8b7f9b..52765ca3f9c8 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -671,6 +671,35 @@ static void test_cmd_vcpu_get_info(struct kvm_vm *vm) &rpl, sizeof(rpl), -KVM_EINVAL); } +static void cmd_vcpu_pause(__u8 wait, int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vm_pause_vcpu cmd; + } req = {}; + __u16 vcpu_idx = 0; + + req.cmd.wait = wait; + req.cmd.vcpu = vcpu_idx; + + test_vm_command(KVMI_VM_PAUSE_VCPU, &req.hdr, sizeof(req), NULL, 0, expected_err); +} + +static void pause_vcpu(void) +{ + cmd_vcpu_pause(1, 0); +} + +static void test_pause(struct kvm_vm *vm) +{ + __u8 wait = 1, wait_inval = 2; + + pause_vcpu(); + + cmd_vcpu_pause(wait, 0); + cmd_vcpu_pause(wait_inval, -KVM_EINVAL); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -686,6 +715,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vm_control_events(vm); test_memory_access(vm); test_cmd_vcpu_get_info(vm); + test_pause(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index b608751780fc..4999132a65bc 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -17,6 +17,8 @@ #define KVMI_MSG_SIZE_ALLOC (sizeof(struct kvmi_msg_hdr) + KVMI_MAX_MSG_SIZE) +#define MAX_PAUSE_REQUESTS 1001 + static DECLARE_BITMAP(Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS); static DECLARE_BITMAP(Kvmi_known_events, KVMI_NUM_EVENTS); static DECLARE_BITMAP(Kvmi_known_vm_events, KVMI_NUM_EVENTS); @@ -124,10 +126,14 @@ void kvmi_uninit(void) kvmi_cache_destroy(); } -static void kvmi_make_request(struct kvm_vcpu *vcpu) +static void kvmi_make_request(struct kvm_vcpu *vcpu, bool wait) { kvm_make_request(KVM_REQ_INTROSPECTION, vcpu); - kvm_vcpu_kick(vcpu); + + if (wait) + kvm_vcpu_kick_and_wait(vcpu); + else + kvm_vcpu_kick(vcpu); } static int __kvmi_add_job(struct kvm_vcpu *vcpu, @@ -162,7 +168,7 @@ int kvmi_add_job(struct kvm_vcpu *vcpu, err = __kvmi_add_job(vcpu, fct, ctx, free_fct); if (!err) - kvmi_make_request(vcpu); + kvmi_make_request(vcpu, false); return err; } @@ -359,6 +365,9 @@ static int __kvmi_hook(struct kvm *kvm, static void kvmi_job_release_vcpu(struct kvm_vcpu *vcpu, void *ctx) { + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + + atomic_set(&vcpui->pause_requests, 0); } static void kvmi_release_vcpus(struct kvm *kvm) @@ -730,15 +739,45 @@ void kvmi_run_jobs(struct kvm_vcpu *vcpu) } } +static void kvmi_vcpu_pause_event(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + + atomic_dec(&vcpui->pause_requests); + /* to be implemented */ +} + void kvmi_handle_requests(struct kvm_vcpu *vcpu) { + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); struct kvm_introspection *kvmi; kvmi = kvmi_get(vcpu->kvm); if (!kvmi) return; - kvmi_run_jobs(vcpu); + for (;;) { + kvmi_run_jobs(vcpu); + + if (atomic_read(&vcpui->pause_requests)) + kvmi_vcpu_pause_event(vcpu); + else + break; + } kvmi_put(vcpu->kvm); } + +int kvmi_cmd_vcpu_pause(struct kvm_vcpu *vcpu, bool wait) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + + if (atomic_read(&vcpui->pause_requests) > MAX_PAUSE_REQUESTS) + return -KVM_EBUSY; + + atomic_inc(&vcpui->pause_requests); + + kvmi_make_request(vcpu, wait); + + return 0; +} diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 126e72201518..f1caa67dbdc3 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -55,6 +55,7 @@ int kvmi_cmd_read_physical(struct kvm *kvm, u64 gpa, size_t size, const struct kvmi_msg_hdr *ctx); int kvmi_cmd_write_physical(struct kvm *kvm, u64 gpa, size_t size, const void *buf); +int kvmi_cmd_vcpu_pause(struct kvm_vcpu *vcpu, bool wait); /* arch */ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index efd04b4aa4ff..02bdec5bfbe7 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -245,6 +245,29 @@ static int handle_vm_write_physical(struct kvm_introspection *kvmi, return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); } +static int handle_vm_pause_vcpu(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vm_pause_vcpu *req = _req; + struct kvm_vcpu *vcpu; + int err; + + if (req->wait > 1 || req->padding1 || req->padding2) { + err = -KVM_EINVAL; + goto reply; + } + + vcpu = kvmi_get_vcpu(kvmi, req->vcpu); + if (!vcpu) + err = -KVM_EINVAL; + else + err = kvmi_cmd_vcpu_pause(vcpu, req->wait == 1); + +reply: + return kvmi_msg_vm_reply(kvmi, msg, err, NULL, 0); +} + /* * These commands are executed by the receiving thread. */ @@ -254,6 +277,7 @@ static kvmi_vm_msg_fct const msg_vm[] = { [KVMI_VM_CHECK_EVENT] = handle_vm_check_event, [KVMI_VM_CONTROL_EVENTS] = handle_vm_control_events, [KVMI_VM_GET_INFO] = handle_vm_get_info, + [KVMI_VM_PAUSE_VCPU] = handle_vm_pause_vcpu, [KVMI_VM_READ_PHYSICAL] = handle_vm_read_physical, [KVMI_VM_WRITE_PHYSICAL] = handle_vm_write_physical, }; From patchwork Wed Nov 25 09:35:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930897 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B589AC5519F for ; Wed, 25 Nov 2020 09:42:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 64DA72083E for ; Wed, 25 Nov 2020 09:42:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728399AbgKYJmB (ORCPT ); Wed, 25 Nov 2020 04:42:01 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57094 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727669AbgKYJl7 (ORCPT ); Wed, 25 Nov 2020 04:41:59 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id C3453305D498; Wed, 25 Nov 2020 11:35:50 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 9E5283072785; Wed, 25 Nov 2020 11:35:50 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 49/81] KVM: introspection: add support for vCPU events Date: Wed, 25 Nov 2020 11:35:28 +0200 Message-Id: <20201125093600.2766-50-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This is the common code used by vCPU threads to send events and wait for replies (received and dispatched by the receiving thread). While waiting for an event reply, the vCPU thread will handle any introspection command already queued or received during this period. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 56 ++++++++++- arch/x86/include/uapi/asm/kvmi.h | 20 ++++ arch/x86/kvm/kvmi.c | 85 ++++++++++++++++ include/linux/kvmi_host.h | 11 +++ include/uapi/linux/kvmi.h | 23 +++++ virt/kvm/introspection/kvmi.c | 1 + virt/kvm/introspection/kvmi_int.h | 6 ++ virt/kvm/introspection/kvmi_msg.c | 156 +++++++++++++++++++++++++++++- 8 files changed, 354 insertions(+), 4 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index a71fb78d546e..5e99baf7e2f3 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -521,7 +521,61 @@ The message data begins with a common structure having the event id:: __u16 padding[3]; }; -Specific event data can follow this common structure. +The vCPU introspection events are sent using the KVMI_VCPU_EVENT message id. +No event is sent unless it is explicitly enabled or requested +(e.g. *KVMI_VCPU_EVENT_PAUSE*). +A vCPU event begins with a common structure having the size of the +structure and the vCPU index:: + + struct kvmi_vcpu_event { + __u16 size; + __u16 vcpu; + __u32 padding; + struct kvmi_vcpu_event_arch arch; + }; + +On x86:: + + struct kvmi_vcpu_event_arch { + __u8 mode; + __u8 padding[7]; + struct kvm_regs regs; + struct kvm_sregs sregs; + struct { + __u64 sysenter_cs; + __u64 sysenter_esp; + __u64 sysenter_eip; + __u64 efer; + __u64 star; + __u64 lstar; + __u64 cstar; + __u64 pat; + __u64 shadow_gs; + } msrs; + }; + +It contains information about the vCPU state at the time of the event. + +A vCPU event reply begins with two common structures:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply { + __u8 action; + __u8 event; + __u16 padding1; + __u32 padding2; + }; + +All events accept the KVMI_EVENT_ACTION_CRASH action, which stops the +guest ungracefully, but as soon as possible. + +Most events accept the KVMI_EVENT_ACTION_CONTINUE action, which +means that KVM will continue handling the event. + +Some events accept the KVMI_EVENT_ACTION_RETRY action, which means that +KVM will stop handling the event and re-enter in guest. + +Specific event data can follow these common structures. 1. KVMI_VM_EVENT_UNHOOK ----------------------- diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 2b6192e1a9a4..9d9df09d381a 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -6,8 +6,28 @@ * KVM introspection - x86 specific structures and definitions */ +#include + struct kvmi_vcpu_get_info_reply { __u64 tsc_speed; }; +struct kvmi_vcpu_event_arch { + __u8 mode; /* 2, 4 or 8 */ + __u8 padding[7]; + struct kvm_regs regs; + struct kvm_sregs sregs; + struct { + __u64 sysenter_cs; + __u64 sysenter_esp; + __u64 sysenter_eip; + __u64 efer; + __u64 star; + __u64 lstar; + __u64 cstar; + __u64 pat; + __u64 shadow_gs; + } msrs; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 35742d927be5..383b19dcf054 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -5,6 +5,91 @@ * Copyright (C) 2019-2020 Bitdefender S.R.L. */ +#include "linux/kvm_host.h" +#include "x86.h" +#include "../../../virt/kvm/introspection/kvmi_int.h" + void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) { } + +static unsigned int kvmi_vcpu_mode(const struct kvm_vcpu *vcpu, + const struct kvm_sregs *sregs) +{ + unsigned int mode = 0; + + if (is_long_mode((struct kvm_vcpu *) vcpu)) { + if (sregs->cs.l) + mode = 8; + else if (!sregs->cs.db) + mode = 2; + else + mode = 4; + } else if (sregs->cr0 & X86_CR0_PE) { + if (!sregs->cs.db) + mode = 2; + else + mode = 4; + } else if (!sregs->cs.db) { + mode = 2; + } else { + mode = 4; + } + + return mode; +} + +static void kvmi_get_msrs(struct kvm_vcpu *vcpu, + struct kvmi_vcpu_event_arch *event) +{ + struct msr_data msr; + + msr.host_initiated = true; + + msr.index = MSR_IA32_SYSENTER_CS; + kvm_x86_ops.get_msr(vcpu, &msr); + event->msrs.sysenter_cs = msr.data; + + msr.index = MSR_IA32_SYSENTER_ESP; + kvm_x86_ops.get_msr(vcpu, &msr); + event->msrs.sysenter_esp = msr.data; + + msr.index = MSR_IA32_SYSENTER_EIP; + kvm_x86_ops.get_msr(vcpu, &msr); + event->msrs.sysenter_eip = msr.data; + + msr.index = MSR_EFER; + kvm_x86_ops.get_msr(vcpu, &msr); + event->msrs.efer = msr.data; + + msr.index = MSR_STAR; + kvm_x86_ops.get_msr(vcpu, &msr); + event->msrs.star = msr.data; + + msr.index = MSR_LSTAR; + kvm_x86_ops.get_msr(vcpu, &msr); + event->msrs.lstar = msr.data; + + msr.index = MSR_CSTAR; + kvm_x86_ops.get_msr(vcpu, &msr); + event->msrs.cstar = msr.data; + + msr.index = MSR_IA32_CR_PAT; + kvm_x86_ops.get_msr(vcpu, &msr); + event->msrs.pat = msr.data; + + msr.index = MSR_KERNEL_GS_BASE; + kvm_x86_ops.get_msr(vcpu, &msr); + event->msrs.shadow_gs = msr.data; +} + +void kvmi_arch_setup_vcpu_event(struct kvm_vcpu *vcpu, + struct kvmi_vcpu_event *ev) +{ + struct kvmi_vcpu_event_arch *event = &ev->arch; + + kvm_arch_vcpu_get_regs(vcpu, &event->regs); + kvm_arch_vcpu_get_sregs(vcpu, &event->sregs); + ev->arch.mode = kvmi_vcpu_mode(vcpu, &event->sregs); + kvmi_get_msrs(vcpu, event); +} diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 59e645d9ea34..4a43e51a44c9 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -6,6 +6,14 @@ #include +struct kvmi_vcpu_reply { + int error; + u32 action; + u32 seq; + void *data; + size_t size; +}; + struct kvmi_job { struct list_head link; void *ctx; @@ -20,6 +28,9 @@ struct kvm_vcpu_introspection { spinlock_t job_lock; atomic_t pause_requests; + + struct kvmi_vcpu_reply reply; + bool waiting_for_reply; }; struct kvm_introspection { diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index bb90d03f059b..6a57efb5664d 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -8,6 +8,7 @@ #include #include +#include enum { KVMI_VERSION = 0x00000001 @@ -32,6 +33,8 @@ enum { }; enum { + KVMI_VCPU_EVENT = KVMI_VCPU_MESSAGE_ID(0), + KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), KVMI_NEXT_VCPU_MESSAGE @@ -50,6 +53,12 @@ enum { KVMI_NEXT_VCPU_EVENT }; +enum { + KVMI_EVENT_ACTION_CONTINUE = 0, + KVMI_EVENT_ACTION_RETRY = 1, + KVMI_EVENT_ACTION_CRASH = 2, +}; + struct kvmi_msg_hdr { __u16 id; __u16 size; @@ -123,4 +132,18 @@ struct kvmi_vm_pause_vcpu { __u32 padding2; }; +struct kvmi_vcpu_event { + __u16 size; + __u16 vcpu; + __u32 padding; + struct kvmi_vcpu_event_arch arch; +}; + +struct kvmi_vcpu_event_reply { + __u8 action; + __u8 event; + __u16 padding1; + __u32 padding2; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 4999132a65bc..771e6b545698 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -368,6 +368,7 @@ static void kvmi_job_release_vcpu(struct kvm_vcpu *vcpu, void *ctx) struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); atomic_set(&vcpui->pause_requests, 0); + vcpui->waiting_for_reply = false; } static void kvmi_release_vcpus(struct kvm *kvm) diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index f1caa67dbdc3..65d8c1c37796 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -32,6 +32,9 @@ void kvmi_sock_shutdown(struct kvm_introspection *kvmi); void kvmi_sock_put(struct kvm_introspection *kvmi); bool kvmi_msg_process(struct kvm_introspection *kvmi); int kvmi_msg_send_unhook(struct kvm_introspection *kvmi); +int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, + void *ev, size_t ev_size, + void *rpl, size_t rpl_size, u32 *action); int kvmi_msg_vcpu_reply(const struct kvmi_vcpu_msg_job *job, const struct kvmi_msg_hdr *msg, int err, const void *rpl, size_t rpl_size); @@ -46,6 +49,7 @@ bool kvmi_is_known_vm_event(u16 id); int kvmi_add_job(struct kvm_vcpu *vcpu, void (*fct)(struct kvm_vcpu *vcpu, void *ctx), void *ctx, void (*free_fct)(void *ctx)); +void kvmi_run_jobs(struct kvm_vcpu *vcpu); int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, u16 event_id, bool enable); int kvmi_cmd_read_physical(struct kvm *kvm, u64 gpa, size_t size, @@ -60,5 +64,7 @@ int kvmi_cmd_vcpu_pause(struct kvm_vcpu *vcpu, bool wait); /* arch */ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id); +void kvmi_arch_setup_vcpu_event(struct kvm_vcpu *vcpu, + struct kvmi_vcpu_event *ev); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 02bdec5bfbe7..467c88185be1 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -299,6 +299,66 @@ static bool is_vm_command(u16 id) return is_vm_message(id) && id != KVMI_VM_EVENT; } +static int check_event_reply(const struct kvmi_msg_hdr *msg, + const struct kvmi_vcpu_event_reply *reply, + const struct kvmi_vcpu_reply *expected, + u8 *action, size_t *received) +{ + size_t msg_size, common, event_size; + int err = -EINVAL; + + if (unlikely(msg->seq != expected->seq)) + return err; + + msg_size = msg->size; + common = sizeof(struct kvmi_vcpu_hdr) + sizeof(*reply); + + if (check_sub_overflow(msg_size, common, &event_size)) + return err; + + if (unlikely(event_size > expected->size)) + return err; + + if (unlikely(reply->padding1 || reply->padding2)) + return err; + + *received = event_size; + *action = reply->action; + return 0; +} + +static int handle_vcpu_event_reply(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *rpl) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(job->vcpu); + struct kvmi_vcpu_reply *expected = &vcpui->reply; + const struct kvmi_vcpu_event_reply *reply = rpl; + const void *reply_data = reply + 1; + size_t useful, received; + u8 action; + + expected->error = check_event_reply(msg, reply, expected, &action, + &received); + if (unlikely(expected->error)) + goto out; + + useful = min(received, expected->size); + if (useful) + memcpy(expected->data, reply_data, useful); + + if (expected->size > useful) + memset((char *)expected->data + useful, 0, + expected->size - useful); + + expected->action = action; + expected->error = 0; + +out: + vcpui->waiting_for_reply = false; + return expected->error; +} + /* * These functions are executed from the vCPU thread. The receiving thread * passes the messages using a newly allocated 'struct kvmi_vcpu_msg_job' @@ -306,6 +366,7 @@ static bool is_vm_command(u16 id) * sending back the reply). */ static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { + [KVMI_VCPU_EVENT] = handle_vcpu_event_reply, }; static kvmi_vcpu_msg_job_fct get_vcpu_msg_handler(u16 id) @@ -329,7 +390,7 @@ static bool is_vcpu_message(u16 id) static bool is_vcpu_command(u16 id) { - return is_vcpu_message(id); + return is_vcpu_message(id) && id != KVMI_VCPU_EVENT; } static void kvmi_job_vcpu_msg(struct kvm_vcpu *vcpu, void *ctx) @@ -422,7 +483,8 @@ static int kvmi_msg_handle_vm_cmd(struct kvm_introspection *kvmi, static bool vcpu_can_handle_messages(struct kvm_vcpu *vcpu) { - return vcpu->arch.mp_state != KVM_MP_STATE_UNINITIALIZED; + return VCPUI(vcpu)->waiting_for_reply + || vcpu->arch.mp_state != KVM_MP_STATE_UNINITIALIZED; } static int kvmi_get_vcpu_if_ready(struct kvm_introspection *kvmi, @@ -467,7 +529,8 @@ static int kvmi_msg_handle_vcpu_msg(struct kvm_introspection *kvmi, struct kvm_vcpu *vcpu = NULL; int err, ec; - if (!kvmi_is_command_allowed(kvmi, msg->id)) + if (msg->id != KVMI_VCPU_EVENT && + !kvmi_is_command_allowed(kvmi, msg->id)) return kvmi_msg_vm_reply_ec(kvmi, msg, -KVM_EPERM); if (vcpu_hdr->padding1 || vcpu_hdr->padding2) @@ -547,3 +610,90 @@ int kvmi_msg_send_unhook(struct kvm_introspection *kvmi) return kvmi_sock_write(kvmi, vec, n, msg_size); } + +static int kvmi_wait_for_reply(struct kvm_vcpu *vcpu) +{ + struct rcuwait *waitp = kvm_arch_vcpu_get_wait(vcpu); + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + int err = 0; + + while (vcpui->waiting_for_reply && !err) { + kvmi_run_jobs(vcpu); + + err = rcuwait_wait_event(waitp, + !vcpui->waiting_for_reply || + !list_empty(&vcpui->job_list), + TASK_KILLABLE); + } + + return err; +} + +static void kvmi_setup_vcpu_reply(struct kvm_vcpu_introspection *vcpui, + u32 msg_seq, void *rpl, size_t rpl_size) +{ + memset(&vcpui->reply, 0, sizeof(vcpui->reply)); + + vcpui->reply.seq = msg_seq; + vcpui->reply.data = rpl; + vcpui->reply.size = rpl_size; + vcpui->reply.error = -EINTR; + vcpui->waiting_for_reply = true; +} + +static int kvmi_fill_and_sent_vcpu_event(struct kvm_vcpu *vcpu, + u32 ev_id, void *ev, + size_t ev_size, u32 msg_seq) +{ + struct kvmi_msg_hdr msg_hdr; + struct kvmi_event_hdr ev_hdr; + struct kvmi_vcpu_event common; + struct kvec vec[] = { + {.iov_base = &msg_hdr, .iov_len = sizeof(msg_hdr)}, + {.iov_base = &ev_hdr, .iov_len = sizeof(ev_hdr) }, + {.iov_base = &common, .iov_len = sizeof(common) }, + {.iov_base = ev, .iov_len = ev_size }, + }; + size_t msg_size = sizeof(msg_hdr) + sizeof(ev_hdr) + + sizeof(common) + ev_size; + size_t n = ARRAY_SIZE(vec) - (ev_size == 0 ? 1 : 0); + struct kvm_introspection *kvmi = KVMI(vcpu->kvm); + + kvmi_fill_ev_msg_hdr(kvmi, &msg_hdr, &ev_hdr, KVMI_VCPU_EVENT, + msg_seq, msg_size, ev_id); + + common.size = sizeof(common); + common.vcpu = kvm_vcpu_get_idx(vcpu); + + kvmi_arch_setup_vcpu_event(vcpu, &common); + + return kvmi_sock_write(kvmi, vec, n, msg_size); +} + +int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, + void *ev, size_t ev_size, + void *rpl, size_t rpl_size, u32 *action) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + struct kvm_introspection *kvmi = KVMI(vcpu->kvm); + u32 msg_seq = atomic_inc_return(&kvmi->ev_seq); + int err; + + kvmi_setup_vcpu_reply(vcpui, msg_seq, rpl, rpl_size); + + err = kvmi_fill_and_sent_vcpu_event(vcpu, ev_id, ev, ev_size, msg_seq); + if (err) + goto out; + + err = kvmi_wait_for_reply(vcpu); + if (!err) + err = vcpui->reply.error; + +out: + if (err) + kvmi_sock_shutdown(kvmi); + else + *action = vcpui->reply.action; + + return err; +} From patchwork Wed Nov 25 09:35:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930799 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 403ACC63777 for ; Wed, 25 Nov 2020 09:41:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E8A21206F9 for ; Wed, 25 Nov 2020 09:41:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727942AbgKYJl4 (ORCPT ); Wed, 25 Nov 2020 04:41:56 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57094 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726620AbgKYJlz (ORCPT ); Wed, 25 Nov 2020 04:41:55 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E785C305D49A; Wed, 25 Nov 2020 11:35:50 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C5CE63072784; Wed, 25 Nov 2020 11:35:50 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 50/81] KVM: introspection: add KVMI_VCPU_EVENT_PAUSE Date: Wed, 25 Nov 2020 11:35:29 +0200 Message-Id: <20201125093600.2766-51-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This event is sent by the vCPU thread as a response to the KVMI_VM_PAUSE_VCPU command, but it has a lower priority, being sent after any other introspection event and when no other introspection command is queued. The number of KVMI_VCPU_EVENT_PAUSE will match the number of successful KVMI_VM_PAUSE_VCPU commands. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 26 ++++++++ include/uapi/linux/kvmi.h | 2 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 65 ++++++++++++++++++- virt/kvm/introspection/kvmi.c | 26 +++++++- virt/kvm/introspection/kvmi_int.h | 1 + virt/kvm/introspection/kvmi_msg.c | 18 +++++ 6 files changed, 136 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 5e99baf7e2f3..c86c83566c3d 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -596,3 +596,29 @@ the guest (see **Unhooking**) and the introspection has been enabled for this event (see **KVMI_VM_CONTROL_EVENTS**). The introspection tool has a chance to unhook and close the introspection socket (signaling that the operation can proceed). + +2. KVMI_VCPU_EVENT_PAUSE +------------------------ + +:Architectures: all +:Versions: >= 1 +:Actions: CONTINUE, CRASH +:Parameters: + +:: + + struct kvmi_event_hdr; + struct kvmi_vcpu_event; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + +This event is sent in response to a *KVMI_VCPU_PAUSE* command and +cannot be controlled with *KVMI_VCPU_CONTROL_EVENTS*. +Because it has a low priority, it will be sent after any other vCPU +introspection event and when no other vCPU introspection command is +queued. diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 6a57efb5664d..757d4b84f473 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -50,6 +50,8 @@ enum { }; enum { + KVMI_VCPU_EVENT_PAUSE = KVMI_VCPU_EVENT_ID(0), + KVMI_NEXT_VCPU_EVENT }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 52765ca3f9c8..4c9dc6560ad9 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -34,6 +34,17 @@ static vm_paddr_t test_gpa; static int page_size; +struct vcpu_event { + struct kvmi_event_hdr hdr; + struct kvmi_vcpu_event common; +}; + +struct vcpu_reply { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_event_reply reply; +}; + struct vcpu_worker_data { struct kvm_vm *vm; int vcpu_id; @@ -690,14 +701,66 @@ static void pause_vcpu(void) cmd_vcpu_pause(1, 0); } +static void reply_to_event(struct kvmi_msg_hdr *ev_hdr, struct vcpu_event *ev, + __u8 action, struct vcpu_reply *rpl, size_t rpl_size) +{ + ssize_t r; + + rpl->hdr.id = ev_hdr->id; + rpl->hdr.seq = ev_hdr->seq; + rpl->hdr.size = rpl_size - sizeof(rpl->hdr); + + rpl->vcpu_hdr.vcpu = ev->common.vcpu; + + rpl->reply.action = action; + rpl->reply.event = ev->hdr.event; + + r = send(Userspace_socket, rpl, rpl_size, 0); + TEST_ASSERT(r == rpl_size, + "send() failed, sending %zd, result %zd, errno %d (%s)\n", + rpl_size, r, errno, strerror(errno)); +} + +static void receive_vcpu_event(struct kvmi_msg_hdr *msg_hdr, + struct vcpu_event *ev, + size_t ev_size, u16 ev_id) +{ + receive_event(msg_hdr, KVMI_VCPU_EVENT, + &ev->hdr, ev_id, ev_size); +} + +static void discard_pause_event(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = {.vm = vm, .vcpu_id = VCPU_ID}; + struct vcpu_reply rpl = {}; + struct kvmi_msg_hdr hdr; + pthread_t vcpu_thread; + struct vcpu_event ev; + + vcpu_thread = start_vcpu_worker(&data); + + receive_vcpu_event(&hdr, &ev, sizeof(ev), KVMI_VCPU_EVENT_PAUSE); + + reply_to_event(&hdr, &ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl, sizeof(rpl)); + + wait_vcpu_worker(vcpu_thread); +} + static void test_pause(struct kvm_vm *vm) { - __u8 wait = 1, wait_inval = 2; + __u8 no_wait = 0, wait = 1, wait_inval = 2; pause_vcpu(); + discard_pause_event(vm); cmd_vcpu_pause(wait, 0); + discard_pause_event(vm); cmd_vcpu_pause(wait_inval, -KVM_EINVAL); + + disallow_event(vm, KVMI_VCPU_EVENT_PAUSE); + cmd_vcpu_pause(no_wait, -KVM_EPERM); + allow_event(vm, KVMI_VCPU_EVENT_PAUSE); } static void test_introspection(struct kvm_vm *vm) diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 771e6b545698..3d26a7319fb7 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -103,6 +103,7 @@ static void kvmi_init_known_events(void) bitmap_zero(Kvmi_known_vcpu_events, KVMI_NUM_EVENTS); kvmi_arch_init_vcpu_events_mask(Kvmi_known_vcpu_events); + set_bit(KVMI_VCPU_EVENT_PAUSE, Kvmi_known_vcpu_events); bitmap_or(Kvmi_known_events, Kvmi_known_vm_events, Kvmi_known_vcpu_events, KVMI_NUM_EVENTS); @@ -740,12 +741,35 @@ void kvmi_run_jobs(struct kvm_vcpu *vcpu) } } +static void kvmi_handle_unsupported_event_action(struct kvm *kvm) +{ + kvmi_sock_shutdown(KVMI(kvm)); +} + +void kvmi_handle_common_event_actions(struct kvm_vcpu *vcpu, u32 action) +{ + struct kvm *kvm = vcpu->kvm; + + switch (action) { + default: + kvmi_handle_unsupported_event_action(kvm); + } +} + static void kvmi_vcpu_pause_event(struct kvm_vcpu *vcpu) { struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + u32 action; atomic_dec(&vcpui->pause_requests); - /* to be implemented */ + + action = kvmi_msg_send_vcpu_pause(vcpu); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + } } void kvmi_handle_requests(struct kvm_vcpu *vcpu) diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 65d8c1c37796..0876740dfa24 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -38,6 +38,7 @@ int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, int kvmi_msg_vcpu_reply(const struct kvmi_vcpu_msg_job *job, const struct kvmi_msg_hdr *msg, int err, const void *rpl, size_t rpl_size); +u32 kvmi_msg_send_vcpu_pause(struct kvm_vcpu *vcpu); /* kvmi.c */ void *kvmi_msg_alloc(void); diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 467c88185be1..71a28e26d382 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -258,6 +258,11 @@ static int handle_vm_pause_vcpu(struct kvm_introspection *kvmi, goto reply; } + if (!kvmi_is_event_allowed(kvmi, KVMI_VCPU_EVENT_PAUSE)) { + err = -KVM_EPERM; + goto reply; + } + vcpu = kvmi_get_vcpu(kvmi, req->vcpu); if (!vcpu) err = -KVM_EINVAL; @@ -697,3 +702,16 @@ int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, return err; } + +u32 kvmi_msg_send_vcpu_pause(struct kvm_vcpu *vcpu) +{ + u32 action; + int err; + + err = kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_PAUSE, NULL, 0, + NULL, 0, &action); + if (err) + return KVMI_EVENT_ACTION_CONTINUE; + + return action; +} From patchwork Wed Nov 25 09:35:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930825 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4FDDAC83012 for ; Wed, 25 Nov 2020 09:42:10 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 01CBB206F9 for ; Wed, 25 Nov 2020 09:42:09 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728360AbgKYJmJ (ORCPT ); Wed, 25 Nov 2020 04:42:09 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57330 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728241AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 16FBE305D49C; Wed, 25 Nov 2020 11:35:51 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id EABB13072786; Wed, 25 Nov 2020 11:35:50 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 51/81] KVM: introspection: add the crash action handling on the event reply Date: Wed, 25 Nov 2020 11:35:30 +0200 Message-Id: <20201125093600.2766-52-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This action is used in extreme cases such as blocking the spread of malware as fast as possible. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- virt/kvm/introspection/kvmi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 3d26a7319fb7..d25b83dce8ed 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -751,6 +751,10 @@ void kvmi_handle_common_event_actions(struct kvm_vcpu *vcpu, u32 action) struct kvm *kvm = vcpu->kvm; switch (action) { + case KVMI_EVENT_ACTION_CRASH: + vcpu->run->exit_reason = KVM_EXIT_SHUTDOWN; + break; + default: kvmi_handle_unsupported_event_action(kvm); } From patchwork Wed Nov 25 09:35:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930807 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C133CC64E90 for ; Wed, 25 Nov 2020 09:42:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 74F02206F9 for ; Wed, 25 Nov 2020 09:42:01 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728346AbgKYJmA (ORCPT ); Wed, 25 Nov 2020 04:42:00 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57224 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727455AbgKYJl7 (ORCPT ); Wed, 25 Nov 2020 04:41:59 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 3D3E2305D3C0; Wed, 25 Nov 2020 11:35:51 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 191A03072784; Wed, 25 Nov 2020 11:35:51 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 52/81] KVM: introspection: add KVMI_VCPU_CONTROL_EVENTS Date: Wed, 25 Nov 2020 11:35:31 +0200 Message-Id: <20201125093600.2766-53-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu By default, all introspection events are disabled. The introspection tool must explicitly enable the events it wants to receive. With this command (KVMI_VCPU_CONTROL_EVENTS) it can enable/disable any vCPU event allowed by the device manager. Some vCPU events doesn't have to be explicitly enabled (and can't be disabled) with this command because they are implicitly enabled or requested by the use of certain commands. For example, if the introspection tool uses the KVMI_VM_PAUSE_VCPU command, it wants to receive an KVMI_VCPU_EVENT_PAUSE event. Signed-off-by: Mihai Donțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 48 +++++++++++++++++++ include/linux/kvmi_host.h | 2 + include/uapi/linux/kvmi.h | 10 +++- .../testing/selftests/kvm/x86_64/kvmi_test.c | 46 ++++++++++++++++++ virt/kvm/introspection/kvmi.c | 26 ++++++++++ virt/kvm/introspection/kvmi_int.h | 3 ++ virt/kvm/introspection/kvmi_msg.c | 24 +++++++++- 7 files changed, 157 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index c86c83566c3d..a502cf9baead 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -367,6 +367,9 @@ the following events:: KVMI_VM_EVENT_UNHOOK +The vCPU events (e.g. *KVMI_VCPU_EVENT_PAUSE*) are controlled with +the *KVMI_VCPU_CONTROL_EVENTS* command. + :Errors: * -KVM_EINVAL - the padding is not zero @@ -509,6 +512,51 @@ command) before returning to guest. *KVMI_VCPU_EVENT_PAUSE* events * -KVM_EPERM - the *KVMI_VCPU_EVENT_PAUSE* event is disallowed +10. KVMI_VCPU_CONTROL_EVENTS +---------------------------- + +:Architectures: all +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_control_events { + __u16 event_id; + __u8 enable; + __u8 padding1; + __u32 padding2; + }; + +:Returns: + +:: + + struct kvmi_error_code + +Enables/disables vCPU introspection events. + +When an event is enabled, the introspection tool is notified and +must reply with: continue, retry, crash, etc. (see **Events** below). + +The following vCPU events doesn't have to be enabled and can't be disabled, +because these are sent as a result of certain commands (but they can be +disallowed by the device manager) :: + + KVMI_VCPU_EVENT_PAUSE + +The VM events (e.g. *KVMI_VM_EVENT_UNHOOK*) are controlled with +the *KVMI_VM_CONTROL_EVENTS* command. + +:Errors: + +* -KVM_EINVAL - the padding is not zero +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the event ID is unknown (use *KVMI_VM_CHECK_EVENT* first) +* -KVM_EPERM - the access is disallowed (use *KVMI_VM_CHECK_EVENT* first) +* -KVM_EAGAIN - the selected vCPU can't be introspected yet + Events ====== diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 4a43e51a44c9..5e5d255e5a2c 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -31,6 +31,8 @@ struct kvm_vcpu_introspection { struct kvmi_vcpu_reply reply; bool waiting_for_reply; + + unsigned long *ev_enable_mask; }; struct kvm_introspection { diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 757d4b84f473..acd00e883dc9 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -35,7 +35,8 @@ enum { enum { KVMI_VCPU_EVENT = KVMI_VCPU_MESSAGE_ID(0), - KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), + KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), + KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2), KVMI_NEXT_VCPU_MESSAGE }; @@ -148,4 +149,11 @@ struct kvmi_vcpu_event_reply { __u32 padding2; }; +struct kvmi_vcpu_control_events { + __u16 event_id; + __u8 enable; + __u8 padding1; + __u32 padding2; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 4c9dc6560ad9..5948f9b79ed0 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -763,6 +763,51 @@ static void test_pause(struct kvm_vm *vm) allow_event(vm, KVMI_VCPU_EVENT_PAUSE); } +static void cmd_vcpu_control_event(struct kvm_vm *vm, __u16 event_id, + __u8 enable, int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_control_events cmd; + } req = {}; + + req.cmd.event_id = event_id; + req.cmd.enable = enable; + + test_vcpu0_command(vm, KVMI_VCPU_CONTROL_EVENTS, + &req.hdr, sizeof(req), NULL, 0, + expected_err); +} + + +static void enable_vcpu_event(struct kvm_vm *vm, __u16 event_id) +{ + cmd_vcpu_control_event(vm, event_id, 1, 0); +} + +static void disable_vcpu_event(struct kvm_vm *vm, __u16 event_id) +{ + cmd_vcpu_control_event(vm, event_id, 0, 0); +} + +static void test_cmd_vcpu_control_events(struct kvm_vm *vm) +{ + __u16 id = KVMI_VCPU_EVENT_PAUSE, invalid_id = 0xffff; + __u8 enable = 1, enable_inval = 2; + + enable_vcpu_event(vm, id); + disable_vcpu_event(vm, id); + + cmd_vcpu_control_event(vm, id, enable_inval, -KVM_EINVAL); + cmd_vcpu_control_event(vm, invalid_id, enable, -KVM_EINVAL); + + disallow_event(vm, id); + cmd_vcpu_control_event(vm, id, enable, -KVM_EPERM); + allow_event(vm, id); + +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -779,6 +824,7 @@ static void test_introspection(struct kvm_vm *vm) test_memory_access(vm); test_cmd_vcpu_get_info(vm); test_pause(vm); + test_cmd_vcpu_control_events(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index d25b83dce8ed..457d1b9122ad 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -83,6 +83,11 @@ bool kvmi_is_known_vm_event(u16 id) return id < KVMI_NUM_EVENTS && test_bit(id, Kvmi_known_vm_events); } +bool kvmi_is_known_vcpu_event(u16 id) +{ + return id < KVMI_NUM_EVENTS && test_bit(id, Kvmi_known_vcpu_events); +} + static bool kvmi_is_vm_event_enabled(struct kvm_introspection *kvmi, u16 id) { return id < KVMI_NUM_EVENTS && test_bit(id, kvmi->vm_event_enable_mask); @@ -190,6 +195,12 @@ static bool kvmi_alloc_vcpui(struct kvm_vcpu *vcpu) if (!vcpui) return false; + vcpui->ev_enable_mask = bitmap_zalloc(KVMI_NUM_EVENTS, GFP_KERNEL); + if (!vcpui->ev_enable_mask) { + kfree(vcpu); + return false; + } + INIT_LIST_HEAD(&vcpui->job_list); spin_lock_init(&vcpui->job_lock); @@ -225,6 +236,8 @@ static void kvmi_free_vcpui(struct kvm_vcpu *vcpu) kvmi_free_vcpu_jobs(vcpui); + bitmap_free(vcpui->ev_enable_mask); + kfree(vcpui); vcpu->kvmi = NULL; } @@ -621,6 +634,19 @@ int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, return 0; } +int kvmi_cmd_vcpu_control_events(struct kvm_vcpu *vcpu, + u16 event_id, bool enable) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + + if (enable) + set_bit(event_id, vcpui->ev_enable_mask); + else + clear_bit(event_id, vcpui->ev_enable_mask); + + return 0; +} + static long get_user_pages_remote_unlocked(struct mm_struct *mm, unsigned long start, unsigned long nr_pages, unsigned int gup_flags, diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 0876740dfa24..8059029cadf4 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -47,12 +47,15 @@ bool kvmi_is_command_allowed(struct kvm_introspection *kvmi, u16 id); bool kvmi_is_event_allowed(struct kvm_introspection *kvmi, u16 id); bool kvmi_is_known_event(u16 id); bool kvmi_is_known_vm_event(u16 id); +bool kvmi_is_known_vcpu_event(u16 id); int kvmi_add_job(struct kvm_vcpu *vcpu, void (*fct)(struct kvm_vcpu *vcpu, void *ctx), void *ctx, void (*free_fct)(void *ctx)); void kvmi_run_jobs(struct kvm_vcpu *vcpu); int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, u16 event_id, bool enable); +int kvmi_cmd_vcpu_control_events(struct kvm_vcpu *vcpu, + u16 event_id, bool enable); int kvmi_cmd_read_physical(struct kvm *kvm, u64 gpa, size_t size, int (*send)(struct kvm_introspection *, const struct kvmi_msg_hdr*, diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 71a28e26d382..f7549baf5c41 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -364,6 +364,27 @@ static int handle_vcpu_event_reply(const struct kvmi_vcpu_msg_job *job, return expected->error; } +static int handle_vcpu_control_events(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + struct kvm_introspection *kvmi = KVMI(job->vcpu->kvm); + const struct kvmi_vcpu_control_events *req = _req; + int ec; + + if (req->padding1 || req->padding2 || req->enable > 1) + ec = -KVM_EINVAL; + else if (!kvmi_is_known_vcpu_event(req->event_id)) + ec = -KVM_EINVAL; + else if (!kvmi_is_event_allowed(kvmi, req->event_id)) + ec = -KVM_EPERM; + else + ec = kvmi_cmd_vcpu_control_events(job->vcpu, req->event_id, + req->enable == 1); + + return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); +} + /* * These functions are executed from the vCPU thread. The receiving thread * passes the messages using a newly allocated 'struct kvmi_vcpu_msg_job' @@ -371,7 +392,8 @@ static int handle_vcpu_event_reply(const struct kvmi_vcpu_msg_job *job, * sending back the reply). */ static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { - [KVMI_VCPU_EVENT] = handle_vcpu_event_reply, + [KVMI_VCPU_EVENT] = handle_vcpu_event_reply, + [KVMI_VCPU_CONTROL_EVENTS] = handle_vcpu_control_events, }; static kvmi_vcpu_msg_job_fct get_vcpu_msg_handler(u16 id) From patchwork Wed Nov 25 09:35:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930905 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 15223C83013 for ; Wed, 25 Nov 2020 09:42:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D31522083E for ; Wed, 25 Nov 2020 09:42:45 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728519AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57240 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728007AbgKYJmA (ORCPT ); Wed, 25 Nov 2020 04:42:00 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 62E05305D3C1; Wed, 25 Nov 2020 11:35:51 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 3F14A3072785; Wed, 25 Nov 2020 11:35:51 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 53/81] KVM: introspection: add KVMI_VCPU_GET_REGISTERS Date: Wed, 25 Nov 2020 11:35:32 +0200 Message-Id: <20201125093600.2766-54-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This command is used to get kvm_regs and kvm_sregs structures, plus a list of struct kvm_msrs from a specific vCPU. While the kvm_regs and kvm_sregs structures are included with every event, this command allows reading any MSR. Signed-off-by: Mihai Donțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 44 ++++++++++++ arch/x86/include/uapi/asm/kvmi.h | 15 ++++ arch/x86/kvm/kvmi.c | 25 +++++++ arch/x86/kvm/kvmi.h | 9 +++ arch/x86/kvm/kvmi_msg.c | 72 ++++++++++++++++++- include/uapi/linux/kvmi.h | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 59 +++++++++++++++ 7 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 arch/x86/kvm/kvmi.h diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index a502cf9baead..dbaedbee9dee 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -557,6 +557,50 @@ the *KVMI_VM_CONTROL_EVENTS* command. * -KVM_EPERM - the access is disallowed (use *KVMI_VM_CHECK_EVENT* first) * -KVM_EAGAIN - the selected vCPU can't be introspected yet +11. KVMI_VCPU_GET_REGISTERS +--------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_get_registers { + __u16 nmsrs; + __u16 padding1; + __u32 padding2; + __u32 msrs_idx[0]; + }; + +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_vcpu_get_registers_reply { + __u32 mode; + __u32 padding; + struct kvm_regs regs; + struct kvm_sregs sregs; + struct kvm_msrs msrs; + }; + +For the given vCPU and the ``nmsrs`` sized array of MSRs registers, +returns the current vCPU mode (in bytes: 2, 4 or 8), the general purpose +registers, the special registers and the requested set of MSRs. + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - one of the indicated MSRs is invalid +* -KVM_EINVAL - the padding is not zero +* -KVM_EINVAL - the reply size is larger than + kvmi_get_version_reply.max_msg_size (too many MSRs) +* -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_ENOMEM - there is not enough memory to allocate the reply + Events ====== diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 9d9df09d381a..11835bf9bdc6 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -30,4 +30,19 @@ struct kvmi_vcpu_event_arch { } msrs; }; +struct kvmi_vcpu_get_registers { + __u16 nmsrs; + __u16 padding1; + __u32 padding2; + __u32 msrs_idx[0]; +}; + +struct kvmi_vcpu_get_registers_reply { + __u32 mode; + __u32 padding; + struct kvm_regs regs; + struct kvm_sregs sregs; + struct kvm_msrs msrs; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 383b19dcf054..fa9b20277dad 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -93,3 +93,28 @@ void kvmi_arch_setup_vcpu_event(struct kvm_vcpu *vcpu, ev->arch.mode = kvmi_vcpu_mode(vcpu, &event->sregs); kvmi_get_msrs(vcpu, event); } + +int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu, + const struct kvmi_vcpu_get_registers *req, + struct kvmi_vcpu_get_registers_reply *rpl) +{ + struct msr_data m = {.host_initiated = true}; + int k, err = 0; + + kvm_arch_vcpu_get_regs(vcpu, &rpl->regs); + kvm_arch_vcpu_get_sregs(vcpu, &rpl->sregs); + rpl->mode = kvmi_vcpu_mode(vcpu, &rpl->sregs); + rpl->msrs.nmsrs = req->nmsrs; + + for (k = 0; k < req->nmsrs && !err; k++) { + m.index = req->msrs_idx[k]; + + err = kvm_x86_ops.get_msr(vcpu, &m); + if (!err) { + rpl->msrs.entries[k].index = m.index; + rpl->msrs.entries[k].data = m.data; + } + } + + return err ? -KVM_EINVAL : 0; +} diff --git a/arch/x86/kvm/kvmi.h b/arch/x86/kvm/kvmi.h new file mode 100644 index 000000000000..7aab4aaabcda --- /dev/null +++ b/arch/x86/kvm/kvmi.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef ARCH_X86_KVM_KVMI_H +#define ARCH_X86_KVM_KVMI_H + +int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu, + const struct kvmi_vcpu_get_registers *req, + struct kvmi_vcpu_get_registers_reply *rpl); + +#endif diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 77552bf50984..fd837c241340 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -7,6 +7,7 @@ */ #include "../../../virt/kvm/introspection/kvmi_int.h" +#include "kvmi.h" static int handle_vcpu_get_info(const struct kvmi_vcpu_msg_job *job, const struct kvmi_msg_hdr *msg, @@ -21,8 +22,77 @@ static int handle_vcpu_get_info(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, 0, &rpl, sizeof(rpl)); } +static bool is_valid_get_regs_request(const struct kvmi_msg_hdr *msg, + const struct kvmi_vcpu_get_registers *req) +{ + size_t req_size, msg_size; + + if (req->padding1 || req->padding2) + return false; + + req_size = struct_size(req, msrs_idx, req->nmsrs); + + if (check_add_overflow(sizeof(struct kvmi_vcpu_hdr), + req_size, &msg_size)) + return false; + + if (msg_size > msg->size) + return false; + + return true; +} + +static bool is_valid_get_regs_reply(const struct kvmi_vcpu_get_registers *req, + size_t *ptr_rpl_size) +{ + struct kvmi_vcpu_get_registers_reply *rpl; + size_t rpl_size, msg_size; + + rpl_size = struct_size(rpl, msrs.entries, req->nmsrs); + + if (check_add_overflow(sizeof(struct kvmi_error_code), + rpl_size, &msg_size)) + return false; + + if (msg_size > KVMI_MAX_MSG_SIZE) + return false; + + *ptr_rpl_size = rpl_size; + return true; +} + +static int handle_vcpu_get_registers(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *req) +{ + struct kvmi_vcpu_get_registers_reply *rpl = NULL; + size_t rpl_size; + int err, ec; + + if (!is_valid_get_regs_request(msg, req) || + !is_valid_get_regs_reply(req, &rpl_size)) { + ec = -KVM_EINVAL; + goto reply; + } + + rpl = kvmi_msg_alloc(); + if (!rpl) { + ec = -KVM_ENOMEM; + goto reply; + } + + ec = kvmi_arch_cmd_vcpu_get_registers(job->vcpu, req, rpl); + +reply: + err = kvmi_msg_vcpu_reply(job, msg, ec, rpl, rpl ? rpl_size : 0); + + kvmi_msg_free(rpl); + return err; +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { - [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, + [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, + [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, }; kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index acd00e883dc9..8548ead451c1 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -37,6 +37,7 @@ enum { KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2), + KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3), KVMI_NEXT_VCPU_MESSAGE }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 5948f9b79ed0..f91a70ad1013 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -808,6 +808,64 @@ static void test_cmd_vcpu_control_events(struct kvm_vm *vm) } +static void cmd_vcpu_get_registers(struct kvm_vm *vm, struct kvm_regs *regs) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_get_registers cmd; + } req = {}; + struct kvmi_vcpu_get_registers_reply rpl; + + test_vcpu0_command(vm, KVMI_VCPU_GET_REGISTERS, &req.hdr, sizeof(req), + &rpl, sizeof(rpl), 0); + + memcpy(regs, &rpl.regs, sizeof(*regs)); +} + +static void test_invalid_vcpu_get_registers(struct kvm_vm *vm) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_get_registers cmd; + __u32 msrs_idx[1]; + } req = {}; + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_get_registers cmd; + } *req_big; + struct kvmi_vcpu_get_registers_reply rpl; + struct kvmi_get_version_reply version; + + req.cmd.nmsrs = 1; + req.cmd.msrs_idx[0] = 0xffffffff; + test_vcpu0_command(vm, KVMI_VCPU_GET_REGISTERS, + &req.hdr, sizeof(req), + &rpl, sizeof(rpl), -KVM_EINVAL); + + cmd_vm_get_version(&version); + + req_big = calloc(1, version.max_msg_size); + req_big->cmd.nmsrs = (version.max_msg_size - sizeof(*req_big)) / sizeof(__u32); + test_vcpu0_command(vm, KVMI_VCPU_GET_REGISTERS, + &req.hdr, sizeof(req), + &rpl, sizeof(rpl), -KVM_EINVAL); + free(req_big); +} + +static void test_cmd_vcpu_get_registers(struct kvm_vm *vm) +{ + struct kvm_regs regs = {}; + + cmd_vcpu_get_registers(vm, ®s); + + pr_debug("get_registers rip 0x%llx\n", regs.rip); + + test_invalid_vcpu_get_registers(vm); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -825,6 +883,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_get_info(vm); test_pause(vm); test_cmd_vcpu_control_events(vm); + test_cmd_vcpu_get_registers(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930927 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 05DB7C56201 for ; Wed, 25 Nov 2020 09:43:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B24EA2083E for ; Wed, 25 Nov 2020 09:42:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728753AbgKYJm7 (ORCPT ); Wed, 25 Nov 2020 04:42:59 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57142 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727673AbgKYJl6 (ORCPT ); Wed, 25 Nov 2020 04:41:58 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 8E452305D3C2; Wed, 25 Nov 2020 11:35:51 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 67B0C3072784; Wed, 25 Nov 2020 11:35:51 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Mircea_C?= =?utf-8?q?=C3=AErjaliu?= , =?utf-8?q?Adalbert_La?= =?utf-8?q?z=C4=83r?= Subject: [PATCH v10 54/81] KVM: introspection: add KVMI_VCPU_SET_REGISTERS Date: Wed, 25 Nov 2020 11:35:33 +0200 Message-Id: <20201125093600.2766-55-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu During an introspection event, the introspection tool might need to change the vCPU state, for example, to skip the current instruction. This command is allowed only during vCPU events and the registers will be set when the reply has been received. Signed-off-by: Mihai Donțu Co-developed-by: Mircea Cîrjaliu Signed-off-by: Mircea Cîrjaliu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 29 +++++++ arch/x86/include/asm/kvmi_host.h | 2 + arch/x86/kvm/kvmi.c | 22 +++++ arch/x86/kvm/kvmi.h | 2 + arch/x86/kvm/kvmi_msg.c | 18 ++++ include/uapi/linux/kvmi.h | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 83 +++++++++++++++++++ virt/kvm/introspection/kvmi_int.h | 1 + virt/kvm/introspection/kvmi_msg.c | 6 +- 9 files changed, 162 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index dbaedbee9dee..178832304458 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -601,6 +601,35 @@ registers, the special registers and the requested set of MSRs. * -KVM_EAGAIN - the selected vCPU can't be introspected yet * -KVM_ENOMEM - there is not enough memory to allocate the reply +12. KVMI_VCPU_SET_REGISTERS +--------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvm_regs; + +:Returns: + +:: + + struct kvmi_error_code + +Sets the general purpose registers for the given vCPU. The changes become +visible to other threads accessing the KVM vCPU structure after the event +currently being handled is replied to. + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_EOPNOTSUPP - the command hasn't been received during an introspection event + Events ====== diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 05ade3a16b24..cc945151cb36 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -5,6 +5,8 @@ #include struct kvm_vcpu_arch_introspection { + struct kvm_regs delayed_regs; + bool have_delayed_regs; }; struct kvm_arch_introspection { diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index fa9b20277dad..39638af7757e 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -118,3 +118,25 @@ int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu, return err ? -KVM_EINVAL : 0; } + +void kvmi_arch_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu, + const struct kvm_regs *regs) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + struct kvm_regs *dest = &vcpui->arch.delayed_regs; + + memcpy(dest, regs, sizeof(*dest)); + + vcpui->arch.have_delayed_regs = true; +} + +void kvmi_arch_post_reply(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + + if (!vcpui->arch.have_delayed_regs) + return; + + kvm_arch_vcpu_set_regs(vcpu, &vcpui->arch.delayed_regs, false); + vcpui->arch.have_delayed_regs = false; +} diff --git a/arch/x86/kvm/kvmi.h b/arch/x86/kvm/kvmi.h index 7aab4aaabcda..4eeb0c900083 100644 --- a/arch/x86/kvm/kvmi.h +++ b/arch/x86/kvm/kvmi.h @@ -5,5 +5,7 @@ int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu, const struct kvmi_vcpu_get_registers *req, struct kvmi_vcpu_get_registers_reply *rpl); +void kvmi_arch_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu, + const struct kvm_regs *regs); #endif diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index fd837c241340..4046a5c4d306 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -90,9 +90,27 @@ static int handle_vcpu_get_registers(const struct kvmi_vcpu_msg_job *job, return err; } +static int handle_vcpu_set_registers(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *req) +{ + const struct kvm_regs *regs = req; + int ec = 0; + + if (msg->size < sizeof(*regs)) + ec = -KVM_EINVAL; + else if (!VCPUI(job->vcpu)->waiting_for_reply) + ec = -KVM_EOPNOTSUPP; + else + kvmi_arch_cmd_vcpu_set_registers(job->vcpu, regs); + + return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, + [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, }; kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 8548ead451c1..4b756d388ad3 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -38,6 +38,7 @@ enum { KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2), KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3), + KVMI_VCPU_SET_REGISTERS = KVMI_VCPU_MESSAGE_ID(4), KVMI_NEXT_VCPU_MESSAGE }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index f91a70ad1013..311a050c26c1 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -638,6 +638,16 @@ static int do_vcpu_command(struct kvm_vm *vm, int cmd_id, return r; } +static int __do_vcpu0_command(int cmd_id, struct kvmi_msg_hdr *req, + size_t req_size, void *rpl, size_t rpl_size) +{ + struct kvmi_vcpu_hdr *vcpu_hdr = (struct kvmi_vcpu_hdr *)(req + 1); + + vcpu_hdr->vcpu = 0; + + return do_command(cmd_id, req, req_size, rpl, rpl_size); +} + static int do_vcpu0_command(struct kvm_vm *vm, int cmd_id, struct kvmi_msg_hdr *req, size_t req_size, void *rpl, size_t rpl_size) @@ -866,6 +876,78 @@ static void test_cmd_vcpu_get_registers(struct kvm_vm *vm) test_invalid_vcpu_get_registers(vm); } +static int __cmd_vcpu_set_registers(struct kvm_vm *vm, + struct kvm_regs *regs) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvm_regs regs; + } req = {}; + + memcpy(&req.regs, regs, sizeof(req.regs)); + + return __do_vcpu0_command(KVMI_VCPU_SET_REGISTERS, + &req.hdr, sizeof(req), NULL, 0); +} + +static void test_invalid_cmd_vcpu_set_registers(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = {.vm = vm, .vcpu_id = VCPU_ID}; + pthread_t vcpu_thread; + struct kvm_regs regs; + int r; + + vcpu_thread = start_vcpu_worker(&data); + + r = __cmd_vcpu_set_registers(vm, ®s); + + wait_vcpu_worker(vcpu_thread); + + TEST_ASSERT(r == -KVM_EOPNOTSUPP, + "KVMI_VCPU_SET_REGISTERS didn't failed with KVM_EOPNOTSUPP, error %d(%s)\n", + -r, kvm_strerror(-r)); +} + +static void __set_registers(struct kvm_vm *vm, + struct kvm_regs *regs) +{ + int r; + + r = __cmd_vcpu_set_registers(vm, regs); + TEST_ASSERT(r == 0, + "KVMI_VCPU_SET_REGISTERS failed, error %d(%s)\n", + -r, kvm_strerror(-r)); +} + +static void test_cmd_vcpu_set_registers(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = {.vm = vm, .vcpu_id = VCPU_ID}; + __u16 event_id = KVMI_VCPU_EVENT_PAUSE; + struct kvmi_msg_hdr hdr; + pthread_t vcpu_thread; + struct vcpu_event ev; + struct vcpu_reply rpl = {}; + struct kvm_regs regs = {}; + + cmd_vcpu_get_registers(vm, ®s); + + test_invalid_cmd_vcpu_set_registers(vm); + + pause_vcpu(); + + vcpu_thread = start_vcpu_worker(&data); + + receive_vcpu_event(&hdr, &ev, sizeof(ev), event_id); + + __set_registers(vm, &ev.common.arch.regs); + + reply_to_event(&hdr, &ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl, sizeof(rpl)); + + wait_vcpu_worker(vcpu_thread); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -884,6 +966,7 @@ static void test_introspection(struct kvm_vm *vm) test_pause(vm); test_cmd_vcpu_control_events(vm); test_cmd_vcpu_get_registers(vm); + test_cmd_vcpu_set_registers(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 8059029cadf4..018764ca1b71 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -70,5 +70,6 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id); void kvmi_arch_setup_vcpu_event(struct kvm_vcpu *vcpu, struct kvmi_vcpu_event *ev); +void kvmi_arch_post_reply(struct kvm_vcpu *vcpu); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index f7549baf5c41..b7de39d4afa6 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -717,10 +717,12 @@ int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, err = vcpui->reply.error; out: - if (err) + if (err) { kvmi_sock_shutdown(kvmi); - else + } else { + kvmi_arch_post_reply(vcpu); *action = vcpui->reply.action; + } return err; } From patchwork Wed Nov 25 09:35:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930873 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BBC75C64E7B for ; Wed, 25 Nov 2020 09:42:22 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 798B420B1F for ; Wed, 25 Nov 2020 09:42:20 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728664AbgKYJmT (ORCPT ); Wed, 25 Nov 2020 04:42:19 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57094 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728579AbgKYJmH (ORCPT ); Wed, 25 Nov 2020 04:42:07 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id B4D77305D3C3; Wed, 25 Nov 2020 11:35:51 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 907AD3072785; Wed, 25 Nov 2020 11:35:51 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , Marian Rotariu , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 55/81] KVM: introspection: add KVMI_VCPU_GET_CPUID Date: Wed, 25 Nov 2020 11:35:34 +0200 Message-Id: <20201125093600.2766-56-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Marian Rotariu This command returns a CPUID leaf (as seen by the guest OS). Signed-off-by: Marian Rotariu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 36 +++++++++++++++++++ arch/x86/include/uapi/asm/kvmi.h | 12 +++++++ arch/x86/kvm/kvmi_msg.c | 26 ++++++++++++++ include/uapi/linux/kvmi.h | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 30 ++++++++++++++++ 5 files changed, 105 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 178832304458..10966430621c 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -630,6 +630,42 @@ currently being handled is replied to. * -KVM_EAGAIN - the selected vCPU can't be introspected yet * -KVM_EOPNOTSUPP - the command hasn't been received during an introspection event +13. KVMI_VCPU_GET_CPUID +----------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_get_cpuid { + __u32 function; + __u32 index; + }; + +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_vcpu_get_cpuid_reply { + __u32 eax; + __u32 ebx; + __u32 ecx; + __u32 edx; + }; + +Returns a CPUID leaf (as seen by the guest OS). + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_ENOENT - the selected leaf is not present or is invalid + Events ====== diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 11835bf9bdc6..3631da9eef8c 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -45,4 +45,16 @@ struct kvmi_vcpu_get_registers_reply { struct kvm_msrs msrs; }; +struct kvmi_vcpu_get_cpuid { + __u32 function; + __u32 index; +}; + +struct kvmi_vcpu_get_cpuid_reply { + __u32 eax; + __u32 ebx; + __u32 ecx; + __u32 edx; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 4046a5c4d306..1651ef877e3e 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -6,6 +6,7 @@ * */ +#include "cpuid.h" #include "../../../virt/kvm/introspection/kvmi_int.h" #include "kvmi.h" @@ -107,7 +108,32 @@ static int handle_vcpu_set_registers(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); } +static int handle_vcpu_get_cpuid(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vcpu_get_cpuid *req = _req; + struct kvmi_vcpu_get_cpuid_reply rpl; + struct kvm_cpuid_entry2 *entry; + int ec = 0; + + entry = kvm_find_cpuid_entry(job->vcpu, req->function, req->index); + if (!entry) { + ec = -KVM_ENOENT; + } else { + memset(&rpl, 0, sizeof(rpl)); + + rpl.eax = entry->eax; + rpl.ebx = entry->ebx; + rpl.ecx = entry->ecx; + rpl.edx = entry->edx; + } + + return kvmi_msg_vcpu_reply(job, msg, ec, &rpl, sizeof(rpl)); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { + [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 4b756d388ad3..2c93a36bfa43 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -39,6 +39,7 @@ enum { KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2), KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3), KVMI_VCPU_SET_REGISTERS = KVMI_VCPU_MESSAGE_ID(4), + KVMI_VCPU_GET_CPUID = KVMI_VCPU_MESSAGE_ID(5), KVMI_NEXT_VCPU_MESSAGE }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 311a050c26c1..542b59466d12 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -948,6 +948,35 @@ static void test_cmd_vcpu_set_registers(struct kvm_vm *vm) wait_vcpu_worker(vcpu_thread); } +static void cmd_vcpu_get_cpuid(struct kvm_vm *vm, + __u32 function, __u32 index, + struct kvmi_vcpu_get_cpuid_reply *rpl) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_get_cpuid cmd; + } req = {}; + + req.cmd.function = function; + req.cmd.index = index; + + test_vcpu0_command(vm, KVMI_VCPU_GET_CPUID, &req.hdr, sizeof(req), + rpl, sizeof(*rpl), 0); +} + +static void test_cmd_vcpu_get_cpuid(struct kvm_vm *vm) +{ + struct kvmi_vcpu_get_cpuid_reply rpl = {}; + __u32 function = 0; + __u32 index = 0; + + cmd_vcpu_get_cpuid(vm, function, index, &rpl); + + pr_debug("cpuid(%u, %u) => eax 0x%.8x, ebx 0x%.8x, ecx 0x%.8x, edx 0x%.8x\n", + function, index, rpl.eax, rpl.ebx, rpl.ecx, rpl.edx); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -967,6 +996,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_control_events(vm); test_cmd_vcpu_get_registers(vm); test_cmd_vcpu_set_registers(vm); + test_cmd_vcpu_get_cpuid(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:35 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930835 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A480DC8301E for ; Wed, 25 Nov 2020 09:42:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6389121D7A for ; Wed, 25 Nov 2020 09:42:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728656AbgKYJmT (ORCPT ); Wed, 25 Nov 2020 04:42:19 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57196 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728548AbgKYJmH (ORCPT ); Wed, 25 Nov 2020 04:42:07 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id DF791305D3C4; Wed, 25 Nov 2020 11:35:51 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id B92E83072784; Wed, 25 Nov 2020 11:35:51 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 56/81] KVM: introspection: add KVMI_VCPU_EVENT_HYPERCALL Date: Wed, 25 Nov 2020 11:35:35 +0200 Message-Id: <20201125093600.2766-57-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This event is sent on a specific hypercall. It is used by the code residing inside the introspected guest to call the introspection tool and to report certain details about its operation. For example, a classic antimalware remediation tool can report what it has found during a scan. Signed-off-by: Mihai Donțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/hypercalls.rst | 35 ++++++++++++++++ Documentation/virt/kvm/kvmi.rst | 40 +++++++++++++++++- arch/x86/include/uapi/asm/kvmi.h | 4 ++ arch/x86/kvm/kvmi.c | 20 +++++++++ arch/x86/kvm/x86.c | 18 ++++++-- include/linux/kvmi_host.h | 2 + include/uapi/linux/kvm_para.h | 1 + include/uapi/linux/kvmi.h | 3 +- .../testing/selftests/kvm/x86_64/kvmi_test.c | 42 +++++++++++++++++++ virt/kvm/introspection/kvmi.c | 38 +++++++++++++++++ virt/kvm/introspection/kvmi_int.h | 8 ++++ virt/kvm/introspection/kvmi_msg.c | 13 ++++++ 12 files changed, 218 insertions(+), 6 deletions(-) diff --git a/Documentation/virt/kvm/hypercalls.rst b/Documentation/virt/kvm/hypercalls.rst index 70e77c66b64c..abfbff96b9e3 100644 --- a/Documentation/virt/kvm/hypercalls.rst +++ b/Documentation/virt/kvm/hypercalls.rst @@ -169,3 +169,38 @@ a0: destination APIC ID :Usage example: When sending a call-function IPI-many to vCPUs, yield if any of the IPI target vCPUs was preempted. + +9. KVM_HC_XEN_HVM_OP +-------------------- + +:Architecture: x86 +:Status: active +:Purpose: To enable communication between a guest agent and a VMI application + +Usage: + +An event will be sent to the VMI application (see kvmi.rst) if the following +registers, which differ between 32bit and 64bit, have the following values: + + ======== ===== ===== + 32bit 64bit value + ======== ===== ===== + ebx (a0) rdi KVM_HC_XEN_HVM_OP_GUEST_REQUEST_VM_EVENT + ecx (a1) rsi 0 + ======== ===== ===== + +This specification copies Xen's { __HYPERVISOR_hvm_op, +HVMOP_guest_request_vm_event } hypercall and can originate from kernel or +userspace. + +It returns 0 if successful, or a negative POSIX.1 error code if it fails. The +absence of an active VMI application is not signaled in any way. + +The following registers are clobbered: + + * 32bit: edx, esi, edi, ebp + * 64bit: rdx, r10, r8, r9 + +In particular, for KVM_HC_XEN_HVM_OP_GUEST_REQUEST_VM_EVENT, the last two +registers can be poisoned deliberately and cannot be used for passing +information. diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 10966430621c..023c885638af 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -535,7 +535,10 @@ command) before returning to guest. struct kvmi_error_code -Enables/disables vCPU introspection events. +Enables/disables vCPU introspection events. This command can be used with +the following events:: + + KVMI_VCPU_EVENT_HYPERCALL When an event is enabled, the introspection tool is notified and must reply with: continue, retry, crash, etc. (see **Events** below). @@ -779,3 +782,38 @@ cannot be controlled with *KVMI_VCPU_CONTROL_EVENTS*. Because it has a low priority, it will be sent after any other vCPU introspection event and when no other vCPU introspection command is queued. + +3. KVMI_VCPU_EVENT_HYPERCALL +---------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Actions: CONTINUE, CRASH +:Parameters: + +:: + + struct kvmi_event_hdr; + struct kvmi_vcpu_event; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + +This event is sent on a specific user hypercall when the introspection has +been enabled for this event (see *KVMI_VCPU_CONTROL_EVENTS*). + +The hypercall number must be ``KVM_HC_XEN_HVM_OP`` with the +``KVM_HC_XEN_HVM_OP_GUEST_REQUEST_VM_EVENT`` sub-function +(see hypercalls.rst). + +It is used by the code residing inside the introspected guest to call the +introspection tool and to report certain details about its operation. For +example, a classic antimalware remediation tool can report what it has +found during a scan. + +The most useful registers describing the vCPU state can be read from +``kvmi_vcpu_event.arch.regs``. diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 3631da9eef8c..a442ba4d2190 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -8,6 +8,10 @@ #include +enum { + KVM_HC_XEN_HVM_OP_GUEST_REQUEST_VM_EVENT = 24, +}; + struct kvmi_vcpu_get_info_reply { __u64 tsc_speed; }; diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 39638af7757e..5f08cf0d19bc 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -11,6 +11,7 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) { + set_bit(KVMI_VCPU_EVENT_HYPERCALL, supported); } static unsigned int kvmi_vcpu_mode(const struct kvm_vcpu *vcpu, @@ -140,3 +141,22 @@ void kvmi_arch_post_reply(struct kvm_vcpu *vcpu) kvm_arch_vcpu_set_regs(vcpu, &vcpui->arch.delayed_regs, false); vcpui->arch.have_delayed_regs = false; } + +bool kvmi_arch_is_agent_hypercall(struct kvm_vcpu *vcpu) +{ + unsigned long subfunc1, subfunc2; + bool longmode = is_64_bit_mode(vcpu); + + if (longmode) { + subfunc1 = kvm_rdi_read(vcpu); + subfunc2 = kvm_rsi_read(vcpu); + } else { + subfunc1 = kvm_rbx_read(vcpu); + subfunc1 &= 0xFFFFFFFF; + subfunc2 = kvm_rcx_read(vcpu); + subfunc2 &= 0xFFFFFFFF; + } + + return (subfunc1 == KVM_HC_XEN_HVM_OP_GUEST_REQUEST_VM_EVENT + && subfunc2 == 0); +} diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4e91d6794b5e..aad12df742df 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -8102,11 +8102,14 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) { unsigned long nr, a0, a1, a2, a3, ret; int op_64_bit; - - if (kvm_hv_hypercall_enabled(vcpu->kvm)) - return kvm_hv_hypercall(vcpu); + bool kvmi_hc; nr = kvm_rax_read(vcpu); + kvmi_hc = (u32)nr == KVM_HC_XEN_HVM_OP; + + if (kvm_hv_hypercall_enabled(vcpu->kvm) && !kvmi_hc) + return kvm_hv_hypercall(vcpu); + a0 = kvm_rbx_read(vcpu); a1 = kvm_rcx_read(vcpu); a2 = kvm_rdx_read(vcpu); @@ -8123,7 +8126,7 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) a3 &= 0xFFFFFFFF; } - if (kvm_x86_ops.get_cpl(vcpu) != 0) { + if (kvm_x86_ops.get_cpl(vcpu) != 0 && !kvmi_hc) { ret = -KVM_EPERM; goto out; } @@ -8160,6 +8163,13 @@ int kvm_emulate_hypercall(struct kvm_vcpu *vcpu) kvm_sched_yield(vcpu->kvm, a0); ret = 0; break; +#ifdef CONFIG_KVM_INTROSPECTION + case KVM_HC_XEN_HVM_OP: + ret = 0; + if (!kvmi_hypercall_event(vcpu)) + ret = -KVM_ENOSYS; + break; +#endif /* CONFIG_KVM_INTROSPECTION */ default: ret = -KVM_ENOSYS; break; diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 5e5d255e5a2c..1fb775b0de33 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -69,6 +69,7 @@ int kvmi_ioctl_event(struct kvm *kvm, int kvmi_ioctl_preunhook(struct kvm *kvm); void kvmi_handle_requests(struct kvm_vcpu *vcpu); +bool kvmi_hypercall_event(struct kvm_vcpu *vcpu); #else @@ -80,6 +81,7 @@ static inline void kvmi_destroy_vm(struct kvm *kvm) { } static inline void kvmi_vcpu_uninit(struct kvm_vcpu *vcpu) { } static inline void kvmi_handle_requests(struct kvm_vcpu *vcpu) { } +static inline bool kvmi_hypercall_event(struct kvm_vcpu *vcpu) { return false; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/include/uapi/linux/kvm_para.h b/include/uapi/linux/kvm_para.h index 3ce388249682..53cebbe22099 100644 --- a/include/uapi/linux/kvm_para.h +++ b/include/uapi/linux/kvm_para.h @@ -33,6 +33,7 @@ #define KVM_HC_CLOCK_PAIRING 9 #define KVM_HC_SEND_IPI 10 #define KVM_HC_SCHED_YIELD 11 +#define KVM_HC_XEN_HVM_OP 34 /* Xen's __HYPERVISOR_hvm_op */ /* * hypercalls use architecture specific diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 2c93a36bfa43..3dfc3486cc46 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -54,7 +54,8 @@ enum { }; enum { - KVMI_VCPU_EVENT_PAUSE = KVMI_VCPU_EVENT_ID(0), + KVMI_VCPU_EVENT_PAUSE = KVMI_VCPU_EVENT_ID(0), + KVMI_VCPU_EVENT_HYPERCALL = KVMI_VCPU_EVENT_ID(1), KVMI_NEXT_VCPU_EVENT }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 542b59466d12..8c772b2bff2a 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -53,6 +53,7 @@ struct vcpu_worker_data { enum { GUEST_TEST_NOOP = 0, + GUEST_TEST_HYPERCALL, }; #define GUEST_REQUEST_TEST() GUEST_SYNC(0) @@ -67,12 +68,23 @@ static int guest_test_id(void) return READ_ONCE(test_id); } +static void guest_hypercall_test(void) +{ + asm volatile("mov $34, %rax"); + asm volatile("mov $24, %rdi"); + asm volatile("mov $0, %rsi"); + asm volatile(".byte 0x0f,0x01,0xc1"); +} + static void guest_code(void) { while (true) { switch (guest_test_id()) { case GUEST_TEST_NOOP: break; + case GUEST_TEST_HYPERCALL: + guest_hypercall_test(); + break; } GUEST_SIGNAL_TEST_DONE(); } @@ -977,6 +989,35 @@ static void test_cmd_vcpu_get_cpuid(struct kvm_vm *vm) function, index, rpl.eax, rpl.ebx, rpl.ecx, rpl.edx); } +static void test_event_hypercall(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = { + .vm = vm, + .vcpu_id = VCPU_ID, + .test_id = GUEST_TEST_HYPERCALL, + }; + struct kvmi_msg_hdr hdr; + struct vcpu_event ev; + struct vcpu_reply rpl = {}; + __u16 event_id = KVMI_VCPU_EVENT_HYPERCALL; + pthread_t vcpu_thread; + + enable_vcpu_event(vm, event_id); + + vcpu_thread = start_vcpu_worker(&data); + + receive_vcpu_event(&hdr, &ev, sizeof(ev), event_id); + + pr_debug("Hypercall event, rip 0x%llx\n", ev.common.arch.regs.rip); + + reply_to_event(&hdr, &ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl, sizeof(rpl)); + + wait_vcpu_worker(vcpu_thread); + + disable_vcpu_event(vm, event_id); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -997,6 +1038,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_get_registers(vm); test_cmd_vcpu_set_registers(vm); test_cmd_vcpu_get_cpuid(vm); + test_event_hypercall(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 457d1b9122ad..e1e0cd17138c 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -836,3 +836,41 @@ int kvmi_cmd_vcpu_pause(struct kvm_vcpu *vcpu, bool wait) return 0; } + +static bool __kvmi_hypercall_event(struct kvm_vcpu *vcpu) +{ + u32 action; + bool ret; + + action = kvmi_msg_send_vcpu_hypercall(vcpu); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + ret = true; + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + ret = false; + } + + return ret; +} + +bool kvmi_hypercall_event(struct kvm_vcpu *vcpu) +{ + struct kvm_introspection *kvmi; + bool ret = false; + + if (!kvmi_arch_is_agent_hypercall(vcpu)) + return ret; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return ret; + + if (is_vcpu_event_enabled(vcpu, KVMI_VCPU_EVENT_HYPERCALL)) + ret = __kvmi_hypercall_event(vcpu); + + kvmi_put(vcpu->kvm); + + return ret; +} diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 018764ca1b71..72f0b75d2cf5 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -26,6 +26,11 @@ typedef int (*kvmi_vcpu_msg_job_fct)(const struct kvmi_vcpu_msg_job *job, const struct kvmi_msg_hdr *msg, const void *req); +static inline bool is_vcpu_event_enabled(struct kvm_vcpu *vcpu, u16 event_id) +{ + return test_bit(event_id, VCPUI(vcpu)->ev_enable_mask); +} + /* kvmi_msg.c */ bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd); void kvmi_sock_shutdown(struct kvm_introspection *kvmi); @@ -39,6 +44,7 @@ int kvmi_msg_vcpu_reply(const struct kvmi_vcpu_msg_job *job, const struct kvmi_msg_hdr *msg, int err, const void *rpl, size_t rpl_size); u32 kvmi_msg_send_vcpu_pause(struct kvm_vcpu *vcpu); +u32 kvmi_msg_send_vcpu_hypercall(struct kvm_vcpu *vcpu); /* kvmi.c */ void *kvmi_msg_alloc(void); @@ -52,6 +58,7 @@ int kvmi_add_job(struct kvm_vcpu *vcpu, void (*fct)(struct kvm_vcpu *vcpu, void *ctx), void *ctx, void (*free_fct)(void *ctx)); void kvmi_run_jobs(struct kvm_vcpu *vcpu); +void kvmi_handle_common_event_actions(struct kvm_vcpu *vcpu, u32 action); int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, u16 event_id, bool enable); int kvmi_cmd_vcpu_control_events(struct kvm_vcpu *vcpu, @@ -71,5 +78,6 @@ kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id); void kvmi_arch_setup_vcpu_event(struct kvm_vcpu *vcpu, struct kvmi_vcpu_event *ev); void kvmi_arch_post_reply(struct kvm_vcpu *vcpu); +bool kvmi_arch_is_agent_hypercall(struct kvm_vcpu *vcpu); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index b7de39d4afa6..0c3f044723fc 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -739,3 +739,16 @@ u32 kvmi_msg_send_vcpu_pause(struct kvm_vcpu *vcpu) return action; } + +u32 kvmi_msg_send_vcpu_hypercall(struct kvm_vcpu *vcpu) +{ + u32 action; + int err; + + err = kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_HYPERCALL, NULL, 0, + NULL, 0, &action); + if (err) + return KVMI_EVENT_ACTION_CONTINUE; + + return action; +} From patchwork Wed Nov 25 09:35:36 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930855 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B08DDC8300E for ; Wed, 25 Nov 2020 09:42:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6D63B2145D for ; Wed, 25 Nov 2020 09:42:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728701AbgKYJmY (ORCPT ); Wed, 25 Nov 2020 04:42:24 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57198 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728531AbgKYJmG (ORCPT ); Wed, 25 Nov 2020 04:42:06 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 1A838305D3C5; Wed, 25 Nov 2020 11:35:52 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id E24593072785; Wed, 25 Nov 2020 11:35:51 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?b?TmljdciZ?= =?utf-8?b?b3IgQ8OuyJt1?= , =?utf-8?q?Adalbert_Laz?= =?utf-8?q?=C4=83r?= Subject: [PATCH v10 57/81] KVM: introspection: add KVMI_VCPU_EVENT_BREAKPOINT Date: Wed, 25 Nov 2020 11:35:36 +0200 Message-Id: <20201125093600.2766-58-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This event is sent when a breakpoint was reached. The introspection tool can place breakpoints and use them as notification for when the OS or an application has reached a certain state or is trying to perform a certain operation (eg. create a process). Signed-off-by: Mihai Donțu Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 48 ++++++++++++++++++ arch/x86/kvm/kvmi.c | 50 +++++++++++++++++++ arch/x86/kvm/svm/svm.c | 34 +++++++++++++ arch/x86/kvm/vmx/vmx.c | 17 +++++-- include/linux/kvmi_host.h | 3 ++ include/uapi/linux/kvmi.h | 11 +++- .../testing/selftests/kvm/x86_64/kvmi_test.c | 46 +++++++++++++++++ virt/kvm/introspection/kvmi.c | 25 ++++++++++ virt/kvm/introspection/kvmi_int.h | 4 ++ virt/kvm/introspection/kvmi_msg.c | 18 +++++++ 10 files changed, 250 insertions(+), 6 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 023c885638af..c89f383e48f9 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -538,6 +538,7 @@ command) before returning to guest. Enables/disables vCPU introspection events. This command can be used with the following events:: + KVMI_VCPU_EVENT_BREAKPOINT KVMI_VCPU_EVENT_HYPERCALL When an event is enabled, the introspection tool is notified and @@ -559,6 +560,9 @@ the *KVMI_VM_CONTROL_EVENTS* command. * -KVM_EINVAL - the event ID is unknown (use *KVMI_VM_CHECK_EVENT* first) * -KVM_EPERM - the access is disallowed (use *KVMI_VM_CHECK_EVENT* first) * -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_EBUSY - the event can't be intercepted right now + (e.g. KVMI_VCPU_EVENT_BREAKPOINT if the #BP event + is already intercepted by userspace) 11. KVMI_VCPU_GET_REGISTERS --------------------------- @@ -817,3 +821,47 @@ found during a scan. The most useful registers describing the vCPU state can be read from ``kvmi_vcpu_event.arch.regs``. + +4. KVMI_VCPU_EVENT_BREAKPOINT +----------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Actions: CONTINUE, CRASH, RETRY +:Parameters: + +:: + + struct kvmi_event_hdr; + struct kvmi_vcpu_event; + struct kvmi_vcpu_event_breakpoint { + __u64 gpa; + __u8 insn_len; + __u8 padding[7]; + }; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + +This event is sent when a breakpoint was reached and the introspection has +been enabled for this event (see *KVMI_VCPU_CONTROL_EVENTS*). + +Some of these breakpoints could have been injected by the introspection tool, +placed in the slack space of various functions and used as notification +for when the OS or an application has reached a certain state or is +trying to perform a certain operation (like creating a process). + +``kvmi_vcpu_event`` (with the vCPU state), the guest physical address +(``gpa``) where the breakpoint instruction is placed and the breakpoint +instruction length (``insn_len``) are sent to the introspection tool. + +The *RETRY* action is used by the introspection tool for its own +breakpoints. In most cases, the tool will change the instruction pointer +before returning this action. + +The *CONTINUE* action will cause the breakpoint exception to be reinjected +(the OS will handle it). diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 5f08cf0d19bc..0bb6f38f1213 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -11,6 +11,7 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) { + set_bit(KVMI_VCPU_EVENT_BREAKPOINT, supported); set_bit(KVMI_VCPU_EVENT_HYPERCALL, supported); } @@ -160,3 +161,52 @@ bool kvmi_arch_is_agent_hypercall(struct kvm_vcpu *vcpu) return (subfunc1 == KVM_HC_XEN_HVM_OP_GUEST_REQUEST_VM_EVENT && subfunc2 == 0); } + +static int kvmi_control_bp_intercept(struct kvm_vcpu *vcpu, bool enable) +{ + struct kvm_guest_debug dbg = {}; + int err = 0; + + if (enable) + dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; + + err = kvm_arch_vcpu_set_guest_debug(vcpu, &dbg); + + return err; +} + +int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, + unsigned int event_id, bool enable) +{ + int err = 0; + + switch (event_id) { + case KVMI_VCPU_EVENT_BREAKPOINT: + err = kvmi_control_bp_intercept(vcpu, enable); + break; + default: + break; + } + + return err; +} + +void kvmi_arch_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len) +{ + u32 action; + u64 gpa; + + gpa = kvm_mmu_gva_to_gpa_system(vcpu, gva, 0, NULL); + + action = kvmi_msg_send_vcpu_bp(vcpu, gpa, insn_len); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + kvm_queue_exception(vcpu, BP_VECTOR); + break; + case KVMI_EVENT_ACTION_RETRY: + /* rip was most likely adjusted past the INT 3 instruction */ + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + } +} diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index c6730ec39c58..baca455212f9 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1958,10 +1959,43 @@ static int db_interception(struct vcpu_svm *svm) return 1; } +static unsigned svm_get_instruction_len(struct kvm_vcpu *vcpu) +{ + struct vcpu_svm *svm = to_svm(vcpu); + unsigned long rip = kvm_rip_read(vcpu); + unsigned long next_rip = 0; + unsigned insn_len; + + if (static_cpu_has(X86_FEATURE_NRIPS)) + next_rip = svm->vmcb->control.next_rip; + + if (!next_rip) { + if (!kvm_emulate_instruction(vcpu, EMULTYPE_SKIP)) + return 0; + + next_rip = kvm_rip_read(vcpu); + kvm_rip_write(vcpu, rip); + } + + insn_len = next_rip - rip; + if (insn_len > MAX_INST_SIZE) { + pr_err("%s: ip 0x%lx next 0x%lx\n", + __func__, rip, next_rip); + return 0; + } + + return insn_len; +} + static int bp_interception(struct vcpu_svm *svm) { struct kvm_run *kvm_run = svm->vcpu.run; + if (!kvmi_breakpoint_event(&svm->vcpu, svm->vmcb->save.cs.base + + svm->vmcb->save.rip, + svm_get_instruction_len(&svm->vcpu))) + return 1; + kvm_run->exit_reason = KVM_EXIT_DEBUG; kvm_run->debug.arch.pc = svm->vmcb->save.cs.base + svm->vmcb->save.rip; kvm_run->debug.arch.exception = BP_VECTOR; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 1c8fbd6209ce..9bfa2e9f8161 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -4814,7 +4815,7 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) struct vcpu_vmx *vmx = to_vmx(vcpu); struct kvm_run *kvm_run = vcpu->run; u32 intr_info, ex_no, error_code; - unsigned long cr2, rip, dr6; + unsigned long cr2, dr6; u32 vect_info; vect_info = vmx->idt_vectoring_info; @@ -4895,7 +4896,10 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) kvm_run->debug.arch.dr6 = dr6 | DR6_FIXED_1 | DR6_RTM; kvm_run->debug.arch.dr7 = vmcs_readl(GUEST_DR7); fallthrough; - case BP_VECTOR: + case BP_VECTOR: { + unsigned long gva = vmcs_readl(GUEST_CS_BASE) + + kvm_rip_read(vcpu); + /* * Update instruction length as we may reinject #BP from * user space while in guest debugging mode. Reading it for @@ -4903,11 +4907,16 @@ static int handle_exception_nmi(struct kvm_vcpu *vcpu) */ vmx->vcpu.arch.event_exit_inst_len = vmcs_read32(VM_EXIT_INSTRUCTION_LEN); + + if (!kvmi_breakpoint_event(vcpu, gva, + vmx->vcpu.arch.event_exit_inst_len)) + return 1; + kvm_run->exit_reason = KVM_EXIT_DEBUG; - rip = kvm_rip_read(vcpu); - kvm_run->debug.arch.pc = vmcs_readl(GUEST_CS_BASE) + rip; + kvm_run->debug.arch.pc = gva; kvm_run->debug.arch.exception = ex_no; break; + } case AC_VECTOR: if (guest_inject_ac(vcpu)) { kvm_queue_exception_e(vcpu, AC_VECTOR, error_code); diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 1fb775b0de33..30b7269468dd 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -70,6 +70,7 @@ int kvmi_ioctl_preunhook(struct kvm *kvm); void kvmi_handle_requests(struct kvm_vcpu *vcpu); bool kvmi_hypercall_event(struct kvm_vcpu *vcpu); +bool kvmi_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len); #else @@ -82,6 +83,8 @@ static inline void kvmi_vcpu_uninit(struct kvm_vcpu *vcpu) { } static inline void kvmi_handle_requests(struct kvm_vcpu *vcpu) { } static inline bool kvmi_hypercall_event(struct kvm_vcpu *vcpu) { return false; } +static inline bool kvmi_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, + u8 insn_len) { return true; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 3dfc3486cc46..ea66f3f803e7 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -54,8 +54,9 @@ enum { }; enum { - KVMI_VCPU_EVENT_PAUSE = KVMI_VCPU_EVENT_ID(0), - KVMI_VCPU_EVENT_HYPERCALL = KVMI_VCPU_EVENT_ID(1), + KVMI_VCPU_EVENT_PAUSE = KVMI_VCPU_EVENT_ID(0), + KVMI_VCPU_EVENT_HYPERCALL = KVMI_VCPU_EVENT_ID(1), + KVMI_VCPU_EVENT_BREAKPOINT = KVMI_VCPU_EVENT_ID(2), KVMI_NEXT_VCPU_EVENT }; @@ -160,4 +161,10 @@ struct kvmi_vcpu_control_events { __u32 padding2; }; +struct kvmi_vcpu_event_breakpoint { + __u64 gpa; + __u8 insn_len; + __u8 padding[7]; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 8c772b2bff2a..ccb9a3a997d8 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -53,6 +53,7 @@ struct vcpu_worker_data { enum { GUEST_TEST_NOOP = 0, + GUEST_TEST_BP, GUEST_TEST_HYPERCALL, }; @@ -68,6 +69,11 @@ static int guest_test_id(void) return READ_ONCE(test_id); } +static void guest_bp_test(void) +{ + asm volatile("int3"); +} + static void guest_hypercall_test(void) { asm volatile("mov $34, %rax"); @@ -82,6 +88,9 @@ static void guest_code(void) switch (guest_test_id()) { case GUEST_TEST_NOOP: break; + case GUEST_TEST_BP: + guest_bp_test(); + break; case GUEST_TEST_HYPERCALL: guest_hypercall_test(); break; @@ -1018,6 +1027,42 @@ static void test_event_hypercall(struct kvm_vm *vm) disable_vcpu_event(vm, event_id); } +static void test_event_breakpoint(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = { + .vm = vm, + .vcpu_id = VCPU_ID, + .test_id = GUEST_TEST_BP, + }; + struct kvmi_msg_hdr hdr; + struct { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_breakpoint bp; + } ev; + struct vcpu_reply rpl = {}; + __u16 event_id = KVMI_VCPU_EVENT_BREAKPOINT; + pthread_t vcpu_thread; + + enable_vcpu_event(vm, event_id); + + vcpu_thread = start_vcpu_worker(&data); + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), event_id); + + pr_debug("Breakpoint event, rip 0x%llx, len %u\n", + ev.vcpu_ev.common.arch.regs.rip, ev.bp.insn_len); + + ev.vcpu_ev.common.arch.regs.rip += ev.bp.insn_len; + __set_registers(vm, &ev.vcpu_ev.common.arch.regs); + + reply_to_event(&hdr, &ev.vcpu_ev, KVMI_EVENT_ACTION_RETRY, + &rpl, sizeof(rpl)); + + wait_vcpu_worker(vcpu_thread); + + disable_vcpu_event(vm, event_id); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1039,6 +1084,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_set_registers(vm); test_cmd_vcpu_get_cpuid(vm); test_event_hypercall(vm); + test_event_breakpoint(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index e1e0cd17138c..476af6dd8bf1 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -638,6 +638,11 @@ int kvmi_cmd_vcpu_control_events(struct kvm_vcpu *vcpu, u16 event_id, bool enable) { struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + int err; + + err = kvmi_arch_cmd_control_intercept(vcpu, event_id, enable); + if (err) + return err; if (enable) set_bit(event_id, vcpui->ev_enable_mask); @@ -874,3 +879,23 @@ bool kvmi_hypercall_event(struct kvm_vcpu *vcpu) return ret; } + +bool kvmi_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len) +{ + struct kvm_introspection *kvmi; + bool ret = true; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return ret; + + if (is_vcpu_event_enabled(vcpu, KVMI_VCPU_EVENT_BREAKPOINT)) { + kvmi_arch_breakpoint_event(vcpu, gva, insn_len); + ret = false; + } + + kvmi_put(vcpu->kvm); + + return ret; +} +EXPORT_SYMBOL(kvmi_breakpoint_event); diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 72f0b75d2cf5..ff745e3cebaf 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -45,6 +45,7 @@ int kvmi_msg_vcpu_reply(const struct kvmi_vcpu_msg_job *job, const void *rpl, size_t rpl_size); u32 kvmi_msg_send_vcpu_pause(struct kvm_vcpu *vcpu); u32 kvmi_msg_send_vcpu_hypercall(struct kvm_vcpu *vcpu); +u32 kvmi_msg_send_vcpu_bp(struct kvm_vcpu *vcpu, u64 gpa, u8 insn_len); /* kvmi.c */ void *kvmi_msg_alloc(void); @@ -79,5 +80,8 @@ void kvmi_arch_setup_vcpu_event(struct kvm_vcpu *vcpu, struct kvmi_vcpu_event *ev); void kvmi_arch_post_reply(struct kvm_vcpu *vcpu); bool kvmi_arch_is_agent_hypercall(struct kvm_vcpu *vcpu); +void kvmi_arch_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len); +int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, + unsigned int event_id, bool enable); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 0c3f044723fc..5c5f14bbcb40 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -752,3 +752,21 @@ u32 kvmi_msg_send_vcpu_hypercall(struct kvm_vcpu *vcpu) return action; } + +u32 kvmi_msg_send_vcpu_bp(struct kvm_vcpu *vcpu, u64 gpa, u8 insn_len) +{ + struct kvmi_vcpu_event_breakpoint e; + u32 action; + int err; + + memset(&e, 0, sizeof(e)); + e.gpa = gpa; + e.insn_len = insn_len; + + err = kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_BREAKPOINT, + &e, sizeof(e), NULL, 0, &action); + if (err) + return KVMI_EVENT_ACTION_CONTINUE; + + return action; +} From patchwork Wed Nov 25 09:35:37 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930843 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id E2B25C8300F for ; Wed, 25 Nov 2020 09:42:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 9F8A320B1F for ; Wed, 25 Nov 2020 09:42:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728620AbgKYJmK (ORCPT ); Wed, 25 Nov 2020 04:42:10 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57134 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728596AbgKYJmJ (ORCPT ); Wed, 25 Nov 2020 04:42:09 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 41BE4305D3C6; Wed, 25 Nov 2020 11:35:52 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 1D2E13072784; Wed, 25 Nov 2020 11:35:52 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 58/81] KVM: introspection: add cleanup support for vCPUs Date: Wed, 25 Nov 2020 11:35:37 +0200 Message-Id: <20201125093600.2766-59-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu On unhook the introspection channel is closed. This will signal the receiving thread to call kvmi_put() and exit. There might be vCPU threads handling introspection commands or waiting for event replies. These will also call kvmi_put() and re-enter in guest. Once the reference counter reaches zero, the structures keeping the introspection data (kvm_introspection and kvm_vcpu_introspection) will be freed. In order to restore the interception of CRs, MSRs, BP, descriptor-table registers, from all vCPUs (some of which might run from userspace), we keep the needed information in another structure (kvmi_interception) which will be used and freed by each of them before re-entering in guest. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvm_host.h | 3 ++ arch/x86/include/asm/kvmi_host.h | 4 +++ arch/x86/kvm/kvmi.c | 49 +++++++++++++++++++++++++++++++ virt/kvm/introspection/kvmi.c | 32 ++++++++++++++++++-- virt/kvm/introspection/kvmi_int.h | 5 ++++ 5 files changed, 90 insertions(+), 3 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 7d1e865193a9..d4e2fe493419 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -816,6 +816,9 @@ struct kvm_vcpu_arch { /* #PF translated error code from EPT/NPT exit reason */ u64 error_code; + + /* Control the interception of MSRs/CRs/BP... */ + struct kvmi_interception *kvmi; }; struct kvm_lpage_info { diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index cc945151cb36..b776be4bb49f 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -4,6 +4,10 @@ #include +struct kvmi_interception { + bool restore_interception; +}; + struct kvm_vcpu_arch_introspection { struct kvm_regs delayed_regs; bool have_delayed_regs; diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 0bb6f38f1213..b4a7d581f68c 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -210,3 +210,52 @@ void kvmi_arch_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len) kvmi_handle_common_event_actions(vcpu, action); } } + +static void kvmi_arch_restore_interception(struct kvm_vcpu *vcpu) +{ +} + +bool kvmi_arch_clean_up_interception(struct kvm_vcpu *vcpu) +{ + struct kvmi_interception *arch_vcpui = vcpu->arch.kvmi; + + if (!arch_vcpui) + return false; + + if (!arch_vcpui->restore_interception) + return false; + + kvmi_arch_restore_interception(vcpu); + + return true; +} + +bool kvmi_arch_vcpu_alloc_interception(struct kvm_vcpu *vcpu) +{ + struct kvmi_interception *arch_vcpui; + + arch_vcpui = kzalloc(sizeof(*arch_vcpui), GFP_KERNEL); + if (!arch_vcpui) + return false; + + return true; +} + +void kvmi_arch_vcpu_free_interception(struct kvm_vcpu *vcpu) +{ + kfree(vcpu->arch.kvmi); + WRITE_ONCE(vcpu->arch.kvmi, NULL); +} + +bool kvmi_arch_vcpu_introspected(struct kvm_vcpu *vcpu) +{ + return !!READ_ONCE(vcpu->arch.kvmi); +} + +void kvmi_arch_request_interception_cleanup(struct kvm_vcpu *vcpu) +{ + struct kvmi_interception *arch_vcpui = READ_ONCE(vcpu->arch.kvmi); + + if (arch_vcpui) + arch_vcpui->restore_interception = true; +} diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 476af6dd8bf1..a0cd98839944 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -206,7 +206,7 @@ static bool kvmi_alloc_vcpui(struct kvm_vcpu *vcpu) vcpu->kvmi = vcpui; - return true; + return kvmi_arch_vcpu_alloc_interception(vcpu); } static int kvmi_create_vcpui(struct kvm_vcpu *vcpu) @@ -240,6 +240,9 @@ static void kvmi_free_vcpui(struct kvm_vcpu *vcpu) kfree(vcpui); vcpu->kvmi = NULL; + + kvmi_arch_request_interception_cleanup(vcpu); + kvmi_make_request(vcpu, false); } static void kvmi_free(struct kvm *kvm) @@ -262,6 +265,7 @@ void kvmi_vcpu_uninit(struct kvm_vcpu *vcpu) { mutex_lock(&vcpu->kvm->kvmi_lock); kvmi_free_vcpui(vcpu); + kvmi_arch_vcpu_free_interception(vcpu); mutex_unlock(&vcpu->kvm->kvmi_lock); } @@ -410,6 +414,21 @@ static int kvmi_recv_thread(void *arg) return 0; } +static bool ready_to_hook(struct kvm *kvm) +{ + struct kvm_vcpu *vcpu; + int i; + + if (kvm->kvmi) + return false; + + kvm_for_each_vcpu(i, vcpu, kvm) + if (kvmi_arch_vcpu_introspected(vcpu)) + return false; + + return true; +} + int kvmi_hook(struct kvm *kvm, const struct kvm_introspection_hook *hook) { struct kvm_introspection *kvmi; @@ -417,7 +436,7 @@ int kvmi_hook(struct kvm *kvm, const struct kvm_introspection_hook *hook) mutex_lock(&kvm->kvmi_lock); - if (kvm->kvmi) { + if (!ready_to_hook(kvm)) { err = -EEXIST; goto out; } @@ -814,7 +833,7 @@ void kvmi_handle_requests(struct kvm_vcpu *vcpu) kvmi = kvmi_get(vcpu->kvm); if (!kvmi) - return; + goto out; for (;;) { kvmi_run_jobs(vcpu); @@ -826,6 +845,13 @@ void kvmi_handle_requests(struct kvm_vcpu *vcpu) } kvmi_put(vcpu->kvm); + +out: + if (kvmi_arch_clean_up_interception(vcpu)) { + mutex_lock(&vcpu->kvm->kvmi_lock); + kvmi_arch_vcpu_free_interception(vcpu); + mutex_unlock(&vcpu->kvm->kvmi_lock); + } } int kvmi_cmd_vcpu_pause(struct kvm_vcpu *vcpu, bool wait) diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index ff745e3cebaf..94de54d7ebb9 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -78,6 +78,11 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id); void kvmi_arch_setup_vcpu_event(struct kvm_vcpu *vcpu, struct kvmi_vcpu_event *ev); +bool kvmi_arch_vcpu_alloc_interception(struct kvm_vcpu *vcpu); +void kvmi_arch_vcpu_free_interception(struct kvm_vcpu *vcpu); +bool kvmi_arch_vcpu_introspected(struct kvm_vcpu *vcpu); +void kvmi_arch_request_interception_cleanup(struct kvm_vcpu *vcpu); +bool kvmi_arch_clean_up_interception(struct kvm_vcpu *vcpu); void kvmi_arch_post_reply(struct kvm_vcpu *vcpu); bool kvmi_arch_is_agent_hypercall(struct kvm_vcpu *vcpu); void kvmi_arch_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len); From patchwork Wed Nov 25 09:35:38 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930921 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 05653C64E75 for ; Wed, 25 Nov 2020 09:42:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id AC26A20B1F for ; Wed, 25 Nov 2020 09:42:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728780AbgKYJmw (ORCPT ); Wed, 25 Nov 2020 04:42:52 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57190 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728074AbgKYJmA (ORCPT ); Wed, 25 Nov 2020 04:42:00 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 6B6B5305D3C7; Wed, 25 Nov 2020 11:35:52 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 4597B3072785; Wed, 25 Nov 2020 11:35:52 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 59/81] KVM: introspection: restore the state of #BP interception on unhook Date: Wed, 25 Nov 2020 11:35:38 +0200 Message-Id: <20201125093600.2766-60-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This commit also ensures that only the userspace or the introspection tool can control the #BP interception exclusively at one time. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvmi_host.h | 18 ++++++ arch/x86/kvm/kvmi.c | 60 +++++++++++++++++++ arch/x86/kvm/x86.c | 5 ++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 14 +++++ 4 files changed, 97 insertions(+) diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index b776be4bb49f..e008662f91a5 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -4,8 +4,15 @@ #include +struct kvmi_monitor_interception { + bool kvmi_intercepted; + bool kvm_intercepted; + bool (*monitor_fct)(struct kvm_vcpu *vcpu, bool enable); +}; + struct kvmi_interception { bool restore_interception; + struct kvmi_monitor_interception breakpoint; }; struct kvm_vcpu_arch_introspection { @@ -16,4 +23,15 @@ struct kvm_vcpu_arch_introspection { struct kvm_arch_introspection { }; +#ifdef CONFIG_KVM_INTROSPECTION + +bool kvmi_monitor_bp_intercept(struct kvm_vcpu *vcpu, u32 dbg); + +#else /* CONFIG_KVM_INTROSPECTION */ + +static inline bool kvmi_monitor_bp_intercept(struct kvm_vcpu *vcpu, u32 dbg) + { return false; } + +#endif /* CONFIG_KVM_INTROSPECTION */ + #endif /* _ASM_X86_KVMI_HOST_H */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index b4a7d581f68c..3fd73087276e 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -162,19 +162,72 @@ bool kvmi_arch_is_agent_hypercall(struct kvm_vcpu *vcpu) && subfunc2 == 0); } +/* + * Returns true if one side (kvm or kvmi) tries to enable/disable the breakpoint + * interception while the other side is still tracking it. + */ +bool kvmi_monitor_bp_intercept(struct kvm_vcpu *vcpu, u32 dbg) +{ + struct kvmi_interception *arch_vcpui = READ_ONCE(vcpu->arch.kvmi); + u32 bp_mask = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; + bool enable = false; + + if ((dbg & bp_mask) == bp_mask) + enable = true; + + return (arch_vcpui && arch_vcpui->breakpoint.monitor_fct(vcpu, enable)); +} +EXPORT_SYMBOL(kvmi_monitor_bp_intercept); + +static bool monitor_bp_fct_kvmi(struct kvm_vcpu *vcpu, bool enable) +{ + if (enable) { + if (kvm_x86_ops.bp_intercepted(vcpu)) + return true; + } else if (!vcpu->arch.kvmi->breakpoint.kvmi_intercepted) + return true; + + vcpu->arch.kvmi->breakpoint.kvmi_intercepted = enable; + + return false; +} + +static bool monitor_bp_fct_kvm(struct kvm_vcpu *vcpu, bool enable) +{ + if (enable) { + if (kvm_x86_ops.bp_intercepted(vcpu)) + return true; + } else if (!vcpu->arch.kvmi->breakpoint.kvm_intercepted) + return true; + + vcpu->arch.kvmi->breakpoint.kvm_intercepted = enable; + + return false; +} + static int kvmi_control_bp_intercept(struct kvm_vcpu *vcpu, bool enable) { struct kvm_guest_debug dbg = {}; int err = 0; + vcpu->arch.kvmi->breakpoint.monitor_fct = monitor_bp_fct_kvmi; if (enable) dbg.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; err = kvm_arch_vcpu_set_guest_debug(vcpu, &dbg); + vcpu->arch.kvmi->breakpoint.monitor_fct = monitor_bp_fct_kvm; return err; } +static void kvmi_arch_disable_bp_intercept(struct kvm_vcpu *vcpu) +{ + kvmi_control_bp_intercept(vcpu, false); + + vcpu->arch.kvmi->breakpoint.kvmi_intercepted = false; + vcpu->arch.kvmi->breakpoint.kvm_intercepted = false; +} + int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, unsigned int event_id, bool enable) { @@ -213,6 +266,7 @@ void kvmi_arch_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len) static void kvmi_arch_restore_interception(struct kvm_vcpu *vcpu) { + kvmi_arch_disable_bp_intercept(vcpu); } bool kvmi_arch_clean_up_interception(struct kvm_vcpu *vcpu) @@ -238,6 +292,12 @@ bool kvmi_arch_vcpu_alloc_interception(struct kvm_vcpu *vcpu) if (!arch_vcpui) return false; + arch_vcpui->breakpoint.monitor_fct = monitor_bp_fct_kvm; + + /* pair with kvmi_monitor_bp_intercept() */ + smp_wmb(); + WRITE_ONCE(vcpu->arch.kvmi, arch_vcpui); + return true; } diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index aad12df742df..824d9d20a6ea 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9756,6 +9756,11 @@ int kvm_arch_vcpu_set_guest_debug(struct kvm_vcpu *vcpu, kvm_queue_exception(vcpu, BP_VECTOR); } + if (kvmi_monitor_bp_intercept(vcpu, dbg->control)) { + r = -EBUSY; + goto out; + } + /* * Read rflags as long as potentially injected trace flags are still * filtered out. diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index ccb9a3a997d8..143ee1a8f618 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -63,6 +63,9 @@ enum { #define HOST_SEND_TEST(uc) (uc.cmd == UCALL_SYNC && uc.args[1] == 0) #define HOST_TEST_DONE(uc) (uc.cmd == UCALL_SYNC && uc.args[1] == 1) +static pthread_t start_vcpu_worker(struct vcpu_worker_data *data); +static void wait_vcpu_worker(pthread_t vcpu_thread); + static int guest_test_id(void) { GUEST_REQUEST_TEST(); @@ -172,13 +175,24 @@ static void allow_command(struct kvm_vm *vm, __s32 id) static void hook_introspection(struct kvm_vm *vm) { + struct vcpu_worker_data data = {.vm = vm, .vcpu_id = VCPU_ID }; __u32 allow = 1, disallow = 0, allow_inval = 2; + pthread_t vcpu_thread; __s32 all_IDs = -1; set_command_perm(vm, all_IDs, allow, EFAULT); set_event_perm(vm, all_IDs, allow, EFAULT); do_hook_ioctl(vm, -1, EINVAL); + + /* + * The last call failed "too late". + * We have to let the vCPU run and clean up its structures, + * otherwise the next call will fail with EEXIST. + */ + vcpu_thread = start_vcpu_worker(&data); + wait_vcpu_worker(vcpu_thread); + do_hook_ioctl(vm, Kvm_socket, 0); do_hook_ioctl(vm, Kvm_socket, EEXIST); From patchwork Wed Nov 25 09:35:39 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930895 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EBC3BC8300F for ; Wed, 25 Nov 2020 09:42:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A09CA20B1F for ; Wed, 25 Nov 2020 09:42:47 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728760AbgKYJmr (ORCPT ); Wed, 25 Nov 2020 04:42:47 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57138 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728174AbgKYJmB (ORCPT ); Wed, 25 Nov 2020 04:42:01 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 94BCD305D3C8; Wed, 25 Nov 2020 11:35:52 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 708EE3072784; Wed, 25 Nov 2020 11:35:52 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 60/81] KVM: introspection: add KVMI_VM_CONTROL_CLEANUP Date: Wed, 25 Nov 2020 11:35:39 +0200 Message-Id: <20201125093600.2766-61-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This command will allow more control over the guest state on unhook. However, the memory restrictions (e.g. those set with KVMI_VM_SET_PAGE_ACCESS) will be removed on unhook. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 28 +++++++++++++++ arch/x86/include/asm/kvmi_host.h | 1 + arch/x86/kvm/kvmi.c | 17 +++++----- include/linux/kvmi_host.h | 2 ++ include/uapi/linux/kvmi.h | 22 +++++++----- .../testing/selftests/kvm/x86_64/kvmi_test.c | 24 +++++++++++++ virt/kvm/introspection/kvmi.c | 18 +++++++--- virt/kvm/introspection/kvmi_int.h | 12 ++++++- virt/kvm/introspection/kvmi_msg.c | 34 ++++++++++++++----- 9 files changed, 129 insertions(+), 29 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index c89f383e48f9..f9c10d27ce14 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -673,6 +673,34 @@ Returns a CPUID leaf (as seen by the guest OS). * -KVM_EAGAIN - the selected vCPU can't be introspected yet * -KVM_ENOENT - the selected leaf is not present or is invalid +14. KVMI_VM_CONTROL_CLEANUP +--------------------------- +:Architectures: all +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vm_control_cleanup { + __u8 enable; + __u8 padding[7]; + }; + +:Returns: + +:: + + struct kvmi_error_code + +Enables/disables the automatic cleanup of the changes made by +the introspection tool at the hypervisor level (e.g. CR/MSR/BP +interceptions). By default it is enabled. + +:Errors: + +* -KVM_EINVAL - the padding is not zero +* -KVM_EINVAL - ``enable`` is not 1 or 0 + Events ====== diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index e008662f91a5..161d1ae5a7cf 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -11,6 +11,7 @@ struct kvmi_monitor_interception { }; struct kvmi_interception { + bool cleanup; bool restore_interception; struct kvmi_monitor_interception breakpoint; }; diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 3fd73087276e..e7a4ef48ed61 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -273,13 +273,11 @@ bool kvmi_arch_clean_up_interception(struct kvm_vcpu *vcpu) { struct kvmi_interception *arch_vcpui = vcpu->arch.kvmi; - if (!arch_vcpui) + if (!arch_vcpui || !arch_vcpui->cleanup) return false; - if (!arch_vcpui->restore_interception) - return false; - - kvmi_arch_restore_interception(vcpu); + if (arch_vcpui->restore_interception) + kvmi_arch_restore_interception(vcpu); return true; } @@ -312,10 +310,13 @@ bool kvmi_arch_vcpu_introspected(struct kvm_vcpu *vcpu) return !!READ_ONCE(vcpu->arch.kvmi); } -void kvmi_arch_request_interception_cleanup(struct kvm_vcpu *vcpu) +void kvmi_arch_request_interception_cleanup(struct kvm_vcpu *vcpu, + bool restore_interception) { struct kvmi_interception *arch_vcpui = READ_ONCE(vcpu->arch.kvmi); - if (arch_vcpui) - arch_vcpui->restore_interception = true; + if (arch_vcpui) { + arch_vcpui->restore_interception = restore_interception; + arch_vcpui->cleanup = true; + } } diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 30b7269468dd..7a7360306812 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -50,6 +50,8 @@ struct kvm_introspection { unsigned long *vm_event_enable_mask; atomic_t ev_seq; + + bool restore_on_unhook; }; int kvmi_version(void); diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index ea66f3f803e7..9e28961a8387 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -20,14 +20,15 @@ enum { enum { KVMI_VM_EVENT = KVMI_VM_MESSAGE_ID(0), - KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1), - KVMI_VM_CHECK_COMMAND = KVMI_VM_MESSAGE_ID(2), - KVMI_VM_CHECK_EVENT = KVMI_VM_MESSAGE_ID(3), - KVMI_VM_GET_INFO = KVMI_VM_MESSAGE_ID(4), - KVMI_VM_CONTROL_EVENTS = KVMI_VM_MESSAGE_ID(5), - KVMI_VM_READ_PHYSICAL = KVMI_VM_MESSAGE_ID(6), - KVMI_VM_WRITE_PHYSICAL = KVMI_VM_MESSAGE_ID(7), - KVMI_VM_PAUSE_VCPU = KVMI_VM_MESSAGE_ID(8), + KVMI_GET_VERSION = KVMI_VM_MESSAGE_ID(1), + KVMI_VM_CHECK_COMMAND = KVMI_VM_MESSAGE_ID(2), + KVMI_VM_CHECK_EVENT = KVMI_VM_MESSAGE_ID(3), + KVMI_VM_GET_INFO = KVMI_VM_MESSAGE_ID(4), + KVMI_VM_CONTROL_EVENTS = KVMI_VM_MESSAGE_ID(5), + KVMI_VM_READ_PHYSICAL = KVMI_VM_MESSAGE_ID(6), + KVMI_VM_WRITE_PHYSICAL = KVMI_VM_MESSAGE_ID(7), + KVMI_VM_PAUSE_VCPU = KVMI_VM_MESSAGE_ID(8), + KVMI_VM_CONTROL_CLEANUP = KVMI_VM_MESSAGE_ID(9), KVMI_NEXT_VM_MESSAGE }; @@ -167,4 +168,9 @@ struct kvmi_vcpu_event_breakpoint { __u8 padding[7]; }; +struct kvmi_vm_control_cleanup { + __u8 enable; + __u8 padding[7]; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 143ee1a8f618..f95f2771a123 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1077,6 +1077,29 @@ static void test_event_breakpoint(struct kvm_vm *vm) disable_vcpu_event(vm, event_id); } +static void cmd_vm_control_cleanup(__u8 enable, int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vm_control_cleanup cmd; + } req = {}; + + req.cmd.enable = enable; + + test_vm_command(KVMI_VM_CONTROL_CLEANUP, &req.hdr, sizeof(req), + NULL, 0, expected_err); +} + +static void test_cmd_vm_control_cleanup(struct kvm_vm *vm) +{ + __u8 disable = 0, enable = 1, enable_inval = 2; + + cmd_vm_control_cleanup(enable_inval, -KVM_EINVAL); + + cmd_vm_control_cleanup(enable, 0); + cmd_vm_control_cleanup(disable, 0); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1099,6 +1122,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_get_cpuid(vm); test_event_hypercall(vm); test_event_breakpoint(vm); + test_cmd_vm_control_cleanup(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index a0cd98839944..e6333708c584 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -227,7 +227,7 @@ static void kvmi_free_vcpu_jobs(struct kvm_vcpu_introspection *vcpui) } } -static void kvmi_free_vcpui(struct kvm_vcpu *vcpu) +static void kvmi_free_vcpui(struct kvm_vcpu *vcpu, bool restore_interception) { struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); @@ -241,17 +241,18 @@ static void kvmi_free_vcpui(struct kvm_vcpu *vcpu) kfree(vcpui); vcpu->kvmi = NULL; - kvmi_arch_request_interception_cleanup(vcpu); + kvmi_arch_request_interception_cleanup(vcpu, restore_interception); kvmi_make_request(vcpu, false); } static void kvmi_free(struct kvm *kvm) { + bool restore_interception = KVMI(kvm)->restore_on_unhook; struct kvm_vcpu *vcpu; int i; kvm_for_each_vcpu(i, vcpu, kvm) - kvmi_free_vcpui(vcpu); + kvmi_free_vcpui(vcpu, restore_interception); bitmap_free(kvm->kvmi->cmd_allow_mask); bitmap_free(kvm->kvmi->event_allow_mask); @@ -263,8 +264,10 @@ static void kvmi_free(struct kvm *kvm) void kvmi_vcpu_uninit(struct kvm_vcpu *vcpu) { + bool restore_interception = false; + mutex_lock(&vcpu->kvm->kvmi_lock); - kvmi_free_vcpui(vcpu); + kvmi_free_vcpui(vcpu, restore_interception); kvmi_arch_vcpu_free_interception(vcpu); mutex_unlock(&vcpu->kvm->kvmi_lock); } @@ -295,6 +298,8 @@ kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook) BUILD_BUG_ON(sizeof(hook->uuid) != sizeof(kvmi->uuid)); memcpy(&kvmi->uuid, &hook->uuid, sizeof(kvmi->uuid)); + kvmi->restore_on_unhook = true; + bitmap_copy(kvmi->cmd_allow_mask, Kvmi_always_allowed_commands, KVMI_NUM_COMMANDS); @@ -671,6 +676,11 @@ int kvmi_cmd_vcpu_control_events(struct kvm_vcpu *vcpu, return 0; } +void kvmi_cmd_vm_control_cleanup(struct kvm_introspection *kvmi, bool enable) +{ + kvmi->restore_on_unhook = enable; +} + static long get_user_pages_remote_unlocked(struct mm_struct *mm, unsigned long start, unsigned long nr_pages, unsigned int gup_flags, diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 94de54d7ebb9..8a266b058155 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -31,6 +31,14 @@ static inline bool is_vcpu_event_enabled(struct kvm_vcpu *vcpu, u16 event_id) return test_bit(event_id, VCPUI(vcpu)->ev_enable_mask); } +static inline bool non_zero_padding(const u8 *addr, size_t len) +{ + while (len--) + if (*addr++) + return true; + return false; +} + /* kvmi_msg.c */ bool kvmi_sock_get(struct kvm_introspection *kvmi, int fd); void kvmi_sock_shutdown(struct kvm_introspection *kvmi); @@ -60,6 +68,7 @@ int kvmi_add_job(struct kvm_vcpu *vcpu, void *ctx, void (*free_fct)(void *ctx)); void kvmi_run_jobs(struct kvm_vcpu *vcpu); void kvmi_handle_common_event_actions(struct kvm_vcpu *vcpu, u32 action); +void kvmi_cmd_vm_control_cleanup(struct kvm_introspection *kvmi, bool enable); int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, u16 event_id, bool enable); int kvmi_cmd_vcpu_control_events(struct kvm_vcpu *vcpu, @@ -81,7 +90,8 @@ void kvmi_arch_setup_vcpu_event(struct kvm_vcpu *vcpu, bool kvmi_arch_vcpu_alloc_interception(struct kvm_vcpu *vcpu); void kvmi_arch_vcpu_free_interception(struct kvm_vcpu *vcpu); bool kvmi_arch_vcpu_introspected(struct kvm_vcpu *vcpu); -void kvmi_arch_request_interception_cleanup(struct kvm_vcpu *vcpu); +void kvmi_arch_request_interception_cleanup(struct kvm_vcpu *vcpu, + bool restore_interception); bool kvmi_arch_clean_up_interception(struct kvm_vcpu *vcpu); void kvmi_arch_post_reply(struct kvm_vcpu *vcpu); bool kvmi_arch_is_agent_hypercall(struct kvm_vcpu *vcpu); diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 5c5f14bbcb40..9c1d69caaf11 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -273,18 +273,36 @@ static int handle_vm_pause_vcpu(struct kvm_introspection *kvmi, return kvmi_msg_vm_reply(kvmi, msg, err, NULL, 0); } +static int handle_vm_control_cleanup(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vm_control_cleanup *req = _req; + int ec = 0; + + if (non_zero_padding(req->padding, ARRAY_SIZE(req->padding))) + ec = -KVM_EINVAL; + else if (req->enable > 1) + ec = -KVM_EINVAL; + else + kvmi_cmd_vm_control_cleanup(kvmi, req->enable == 1); + + return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); +} + /* * These commands are executed by the receiving thread. */ static kvmi_vm_msg_fct const msg_vm[] = { - [KVMI_GET_VERSION] = handle_get_version, - [KVMI_VM_CHECK_COMMAND] = handle_vm_check_command, - [KVMI_VM_CHECK_EVENT] = handle_vm_check_event, - [KVMI_VM_CONTROL_EVENTS] = handle_vm_control_events, - [KVMI_VM_GET_INFO] = handle_vm_get_info, - [KVMI_VM_PAUSE_VCPU] = handle_vm_pause_vcpu, - [KVMI_VM_READ_PHYSICAL] = handle_vm_read_physical, - [KVMI_VM_WRITE_PHYSICAL] = handle_vm_write_physical, + [KVMI_GET_VERSION] = handle_get_version, + [KVMI_VM_CHECK_COMMAND] = handle_vm_check_command, + [KVMI_VM_CHECK_EVENT] = handle_vm_check_event, + [KVMI_VM_CONTROL_CLEANUP] = handle_vm_control_cleanup, + [KVMI_VM_CONTROL_EVENTS] = handle_vm_control_events, + [KVMI_VM_GET_INFO] = handle_vm_get_info, + [KVMI_VM_PAUSE_VCPU] = handle_vm_pause_vcpu, + [KVMI_VM_READ_PHYSICAL] = handle_vm_read_physical, + [KVMI_VM_WRITE_PHYSICAL] = handle_vm_write_physical, }; static kvmi_vm_msg_fct get_vm_msg_handler(u16 id) From patchwork Wed Nov 25 09:35:40 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930919 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id C8E3FC83014 for ; Wed, 25 Nov 2020 09:42:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8287E2083E for ; Wed, 25 Nov 2020 09:42:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728766AbgKYJmt (ORCPT ); Wed, 25 Nov 2020 04:42:49 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57194 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727718AbgKYJmB (ORCPT ); Wed, 25 Nov 2020 04:42:01 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id C1C69305D3C9; Wed, 25 Nov 2020 11:35:52 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 982663072785; Wed, 25 Nov 2020 11:35:52 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 61/81] KVM: introspection: add KVMI_VCPU_CONTROL_CR and KVMI_VCPU_EVENT_CR Date: Wed, 25 Nov 2020 11:35:40 +0200 Message-Id: <20201125093600.2766-62-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu Using the KVMI_VCPU_CONTROL_CR command, the introspection tool subscribes to KVMI_VCPU_EVENT_CR events that will be sent when a control register (CR0, CR3 or CR4) is going to be changed. Signed-off-by: Mihai Donțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 73 +++++++++++++ arch/x86/include/asm/kvmi_host.h | 12 +++ arch/x86/include/uapi/asm/kvmi.h | 18 ++++ arch/x86/kvm/kvmi.c | 78 ++++++++++++++ arch/x86/kvm/kvmi.h | 4 + arch/x86/kvm/kvmi_msg.c | 44 ++++++++ arch/x86/kvm/vmx/vmx.c | 6 +- arch/x86/kvm/x86.c | 12 ++- include/uapi/linux/kvmi.h | 2 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 100 ++++++++++++++++++ virt/kvm/introspection/kvmi_int.h | 2 + 11 files changed, 348 insertions(+), 3 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index f9c10d27ce14..85e14b82aa2f 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -539,6 +539,7 @@ Enables/disables vCPU introspection events. This command can be used with the following events:: KVMI_VCPU_EVENT_BREAKPOINT + KVMI_VCPU_EVENT_CR KVMI_VCPU_EVENT_HYPERCALL When an event is enabled, the introspection tool is notified and @@ -701,6 +702,40 @@ interceptions). By default it is enabled. * -KVM_EINVAL - the padding is not zero * -KVM_EINVAL - ``enable`` is not 1 or 0 +15. KVMI_VCPU_CONTROL_CR +------------------------ + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_control_cr { + __u8 cr; + __u8 enable; + __u16 padding1; + __u32 padding2; + }; + +:Returns: + +:: + + struct kvmi_error_code + +Enables/disables introspection for a specific control register and must +be used in addition to *KVMI_VCPU_CONTROL_EVENTS* with the *KVMI_VCPU_EVENT_CR* +ID set. + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the specified control register is not CR0, CR3 or CR4 +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet + Events ====== @@ -893,3 +928,41 @@ before returning this action. The *CONTINUE* action will cause the breakpoint exception to be reinjected (the OS will handle it). + +5. KVMI_VCPU_EVENT_CR +--------------------- + +:Architectures: x86 +:Versions: >= 1 +:Actions: CONTINUE, CRASH +:Parameters: + +:: + + struct kvmi_event_hdr; + struct kvmi_vcpu_event; + struct kvmi_vcpu_event_cr { + __u8 cr; + __u8 padding[7]; + __u64 old_value; + __u64 new_value; + }; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + struct kvmi_vcpu_event_cr_reply { + __u64 new_val; + }; + +This event is sent when a control register is going to be changed and the +introspection has been enabled for this event and for this specific +register (see **KVMI_VCPU_CONTROL_EVENTS**). + +``kvmi_vcpu_event`` (with the vCPU state), the control register number +(``cr``), the old value (``old_value``) and the new value (``new_value``) +are sent to the introspection tool. The *CONTINUE* action will set the +``new_val``. diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 161d1ae5a7cf..7613088d0ae2 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -4,6 +4,8 @@ #include +#define KVMI_NUM_CR 5 + struct kvmi_monitor_interception { bool kvmi_intercepted; bool kvm_intercepted; @@ -19,6 +21,8 @@ struct kvmi_interception { struct kvm_vcpu_arch_introspection { struct kvm_regs delayed_regs; bool have_delayed_regs; + + DECLARE_BITMAP(cr_mask, KVMI_NUM_CR); }; struct kvm_arch_introspection { @@ -27,11 +31,19 @@ struct kvm_arch_introspection { #ifdef CONFIG_KVM_INTROSPECTION bool kvmi_monitor_bp_intercept(struct kvm_vcpu *vcpu, u32 dbg); +bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, + unsigned long old_value, unsigned long *new_value); +bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu); #else /* CONFIG_KVM_INTROSPECTION */ static inline bool kvmi_monitor_bp_intercept(struct kvm_vcpu *vcpu, u32 dbg) { return false; } +static inline bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, + unsigned long old_value, + unsigned long *new_value) + { return true; } +static inline bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu) { return false; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index a442ba4d2190..32cd17488058 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -61,4 +61,22 @@ struct kvmi_vcpu_get_cpuid_reply { __u32 edx; }; +struct kvmi_vcpu_control_cr { + __u8 cr; + __u8 enable; + __u16 padding1; + __u32 padding2; +}; + +struct kvmi_vcpu_event_cr { + __u8 cr; + __u8 padding[7]; + __u64 old_value; + __u64 new_value; +}; + +struct kvmi_vcpu_event_cr_reply { + __u64 new_val; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index e7a4ef48ed61..2bb6b4bb932b 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -8,10 +8,12 @@ #include "linux/kvm_host.h" #include "x86.h" #include "../../../virt/kvm/introspection/kvmi_int.h" +#include "kvmi.h" void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) { set_bit(KVMI_VCPU_EVENT_BREAKPOINT, supported); + set_bit(KVMI_VCPU_EVENT_CR, supported); set_bit(KVMI_VCPU_EVENT_HYPERCALL, supported); } @@ -320,3 +322,79 @@ void kvmi_arch_request_interception_cleanup(struct kvm_vcpu *vcpu, arch_vcpui->cleanup = true; } } + +int kvmi_arch_cmd_vcpu_control_cr(struct kvm_vcpu *vcpu, int cr, bool enable) +{ + if (cr == 3) + kvm_x86_ops.control_cr3_intercept(vcpu, CR_TYPE_W, enable); + + if (enable) + set_bit(cr, VCPUI(vcpu)->arch.cr_mask); + else + clear_bit(cr, VCPUI(vcpu)->arch.cr_mask); + + return 0; +} + +static bool __kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, + u64 old_value, unsigned long *new_value) +{ + u64 reply_value; + u32 action; + bool ret; + + if (!test_bit(cr, VCPUI(vcpu)->arch.cr_mask)) + return true; + + action = kvmi_msg_send_cr(vcpu, cr, old_value, *new_value, + &reply_value); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + *new_value = reply_value; + ret = true; + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + ret = false; + } + + return ret; +} + +bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, + unsigned long old_value, unsigned long *new_value) +{ + struct kvm_introspection *kvmi; + bool ret = true; + + if (old_value == *new_value) + return true; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return true; + + if (is_vcpu_event_enabled(vcpu, KVMI_VCPU_EVENT_CR)) + ret = __kvmi_cr_event(vcpu, cr, old_value, new_value); + + kvmi_put(vcpu->kvm); + + return ret; +} + +bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu) +{ + struct kvm_introspection *kvmi; + bool ret; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return false; + + ret = test_bit(3, VCPUI(vcpu)->arch.cr_mask); + + kvmi_put(vcpu->kvm); + + return ret; +} +EXPORT_SYMBOL(kvmi_cr3_intercepted); diff --git a/arch/x86/kvm/kvmi.h b/arch/x86/kvm/kvmi.h index 4eeb0c900083..6f4aaebb67f9 100644 --- a/arch/x86/kvm/kvmi.h +++ b/arch/x86/kvm/kvmi.h @@ -7,5 +7,9 @@ int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu, struct kvmi_vcpu_get_registers_reply *rpl); void kvmi_arch_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu, const struct kvm_regs *regs); +int kvmi_arch_cmd_vcpu_control_cr(struct kvm_vcpu *vcpu, int cr, bool enable); + +u32 kvmi_msg_send_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, + u64 new_value, u64 *ret_value); #endif diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 1651ef877e3e..1682bb27105f 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -132,7 +132,26 @@ static int handle_vcpu_get_cpuid(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, ec, &rpl, sizeof(rpl)); } +static int handle_vcpu_control_cr(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vcpu_control_cr *req = _req; + int ec; + + if (req->padding1 || req->padding2 || req->enable > 1) + ec = -KVM_EINVAL; + else if (req->cr != 0 && req->cr != 3 && req->cr != 4) + ec = -KVM_EINVAL; + else + ec = kvmi_arch_cmd_vcpu_control_cr(job->vcpu, req->cr, + req->enable == 1); + + return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { + [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, @@ -143,3 +162,28 @@ kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) { return id < ARRAY_SIZE(msg_vcpu) ? msg_vcpu[id] : NULL; } + +u32 kvmi_msg_send_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, + u64 new_value, u64 *ret_value) +{ + struct kvmi_vcpu_event_cr e; + struct kvmi_vcpu_event_cr_reply r; + u32 action; + int err; + + memset(&e, 0, sizeof(e)); + e.cr = cr; + e.old_value = old_value; + e.new_value = new_value; + + err = kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_CR, &e, sizeof(e), + &r, sizeof(r), &action); + if (err) { + action = KVMI_EVENT_ACTION_CONTINUE; + *ret_value = new_value; + } else { + *ret_value = r.new_val; + } + + return action; +} diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 9bfa2e9f8161..723e78529146 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5062,7 +5062,8 @@ static int handle_cr(struct kvm_vcpu *vcpu) err = handle_set_cr0(vcpu, val); return kvm_complete_insn_gp(vcpu, err); case 3: - WARN_ON_ONCE(enable_unrestricted_guest); + WARN_ON_ONCE(enable_unrestricted_guest && + !kvmi_cr3_intercepted(vcpu)); err = kvm_set_cr3(vcpu, val); return kvm_complete_insn_gp(vcpu, err); case 4: @@ -5095,7 +5096,8 @@ static int handle_cr(struct kvm_vcpu *vcpu) case 1: /*mov from cr*/ switch (cr) { case 3: - WARN_ON_ONCE(enable_unrestricted_guest); + WARN_ON_ONCE(enable_unrestricted_guest && + !kvmi_cr3_intercepted(vcpu)); val = kvm_read_cr3(vcpu); kvm_register_write(vcpu, reg, val); trace_kvm_cr_read(cr, val); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 824d9d20a6ea..9a4ec0b4714c 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -849,6 +849,9 @@ int kvm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) if (!(cr0 & X86_CR0_PG) && kvm_read_cr4_bits(vcpu, X86_CR4_PCIDE)) return 1; + if (!kvmi_cr_event(vcpu, 0, old_cr0, &cr0)) + return 1; + kvm_x86_ops.set_cr0(vcpu, cr0); if ((cr0 ^ old_cr0) & X86_CR0_PG) { @@ -1010,6 +1013,9 @@ int kvm_set_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) return 1; } + if (!kvmi_cr_event(vcpu, 4, old_cr4, &cr4)) + return 1; + kvm_x86_ops.set_cr4(vcpu, cr4); if (((cr4 ^ old_cr4) & mmu_role_bits) || @@ -1022,6 +1028,7 @@ EXPORT_SYMBOL_GPL(kvm_set_cr4); int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) { + unsigned long old_cr3 = kvm_read_cr3(vcpu); bool skip_tlb_flush = false; #ifdef CONFIG_X86_64 bool pcid_enabled = kvm_read_cr4_bits(vcpu, X86_CR4_PCIDE); @@ -1032,7 +1039,7 @@ int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) } #endif - if (cr3 == kvm_read_cr3(vcpu) && !pdptrs_changed(vcpu)) { + if (cr3 == old_cr3 && !pdptrs_changed(vcpu)) { if (!skip_tlb_flush) { kvm_mmu_sync_roots(vcpu); kvm_make_request(KVM_REQ_TLB_FLUSH_CURRENT, vcpu); @@ -1047,6 +1054,9 @@ int kvm_set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) !load_pdptrs(vcpu, vcpu->arch.walk_mmu, cr3)) return 1; + if (!kvmi_cr_event(vcpu, 3, old_cr3, &cr3)) + return 1; + kvm_mmu_new_pgd(vcpu, cr3, skip_tlb_flush, skip_tlb_flush); vcpu->arch.cr3 = cr3; kvm_register_mark_available(vcpu, VCPU_EXREG_CR3); diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 9e28961a8387..c1d8cf02018b 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -41,6 +41,7 @@ enum { KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3), KVMI_VCPU_SET_REGISTERS = KVMI_VCPU_MESSAGE_ID(4), KVMI_VCPU_GET_CPUID = KVMI_VCPU_MESSAGE_ID(5), + KVMI_VCPU_CONTROL_CR = KVMI_VCPU_MESSAGE_ID(6), KVMI_NEXT_VCPU_MESSAGE }; @@ -58,6 +59,7 @@ enum { KVMI_VCPU_EVENT_PAUSE = KVMI_VCPU_EVENT_ID(0), KVMI_VCPU_EVENT_HYPERCALL = KVMI_VCPU_EVENT_ID(1), KVMI_VCPU_EVENT_BREAKPOINT = KVMI_VCPU_EVENT_ID(2), + KVMI_VCPU_EVENT_CR = KVMI_VCPU_EVENT_ID(3), KVMI_NEXT_VCPU_EVENT }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index f95f2771a123..6a1103eab77a 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -54,6 +54,7 @@ struct vcpu_worker_data { enum { GUEST_TEST_NOOP = 0, GUEST_TEST_BP, + GUEST_TEST_CR, GUEST_TEST_HYPERCALL, }; @@ -77,6 +78,11 @@ static void guest_bp_test(void) asm volatile("int3"); } +static void guest_cr_test(void) +{ + set_cr4(get_cr4() | X86_CR4_OSXSAVE); +} + static void guest_hypercall_test(void) { asm volatile("mov $34, %rax"); @@ -94,6 +100,9 @@ static void guest_code(void) case GUEST_TEST_BP: guest_bp_test(); break; + case GUEST_TEST_CR: + guest_cr_test(); + break; case GUEST_TEST_HYPERCALL: guest_hypercall_test(); break; @@ -1100,6 +1109,96 @@ static void test_cmd_vm_control_cleanup(struct kvm_vm *vm) cmd_vm_control_cleanup(disable, 0); } +static void cmd_vcpu_control_cr(struct kvm_vm *vm, __u8 cr, __u8 enable, + int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_control_cr cmd; + } req = {}; + + req.cmd.cr = cr; + req.cmd.enable = enable; + + test_vcpu0_command(vm, KVMI_VCPU_CONTROL_CR, &req.hdr, sizeof(req), + NULL, 0, expected_err); +} + +static void enable_cr_events(struct kvm_vm *vm, __u8 cr) +{ + enable_vcpu_event(vm, KVMI_VCPU_EVENT_CR); + + cmd_vcpu_control_cr(vm, cr, 1, 0); +} + +static void disable_cr_events(struct kvm_vm *vm, __u8 cr) +{ + cmd_vcpu_control_cr(vm, cr, 0, 0); + + disable_vcpu_event(vm, KVMI_VCPU_EVENT_CR); +} + +static void test_invalid_vcpu_control_cr(struct kvm_vm *vm) +{ + __u8 enable = 1, enable_inval = 2; + __u8 cr_inval = 99, cr = 0; + + cmd_vcpu_control_cr(vm, cr, enable_inval, -KVM_EINVAL); + cmd_vcpu_control_cr(vm, cr_inval, enable, -KVM_EINVAL); +} + +static void test_cmd_vcpu_control_cr(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = { + .vm = vm, + .vcpu_id = VCPU_ID, + .test_id = GUEST_TEST_CR, + }; + struct kvmi_msg_hdr hdr; + struct { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_cr cr; + } ev; + struct { + struct vcpu_reply common; + struct kvmi_vcpu_event_cr_reply cr; + } rpl = {}; + __u16 event_id = KVMI_VCPU_EVENT_CR; + __u8 cr_no = 4; + struct kvm_sregs sregs; + pthread_t vcpu_thread; + + enable_cr_events(vm, cr_no); + + vcpu_thread = start_vcpu_worker(&data); + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), event_id); + + pr_debug("CR%u, old 0x%llx, new 0x%llx\n", + ev.cr.cr, ev.cr.old_value, ev.cr.new_value); + + TEST_ASSERT(ev.cr.cr == cr_no, + "Unexpected CR event, received CR%u, expected CR%u", + ev.cr.cr, cr_no); + + rpl.cr.new_val = ev.cr.old_value; + + reply_to_event(&hdr, &ev.vcpu_ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl.common, sizeof(rpl)); + + wait_vcpu_worker(vcpu_thread); + + disable_cr_events(vm, cr_no); + + vcpu_sregs_get(vm, VCPU_ID, &sregs); + TEST_ASSERT(sregs.cr4 == ev.cr.old_value, + "Failed to block CR4 update, CR4 0x%llx, expected 0x%llx", + sregs.cr4, ev.cr.old_value); + + test_invalid_vcpu_control_cr(vm); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1123,6 +1222,7 @@ static void test_introspection(struct kvm_vm *vm) test_event_hypercall(vm); test_event_breakpoint(vm); test_cmd_vm_control_cleanup(vm); + test_cmd_vcpu_control_cr(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 8a266b058155..b1877a770fcb 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -69,6 +69,8 @@ int kvmi_add_job(struct kvm_vcpu *vcpu, void kvmi_run_jobs(struct kvm_vcpu *vcpu); void kvmi_handle_common_event_actions(struct kvm_vcpu *vcpu, u32 action); void kvmi_cmd_vm_control_cleanup(struct kvm_introspection *kvmi, bool enable); +struct kvm_introspection * __must_check kvmi_get(struct kvm *kvm); +void kvmi_put(struct kvm *kvm); int kvmi_cmd_vm_control_events(struct kvm_introspection *kvmi, u16 event_id, bool enable); int kvmi_cmd_vcpu_control_events(struct kvm_vcpu *vcpu, From patchwork Wed Nov 25 09:35:41 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930947 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 5CC06C64E7A for ; Wed, 25 Nov 2020 09:43:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 134202083E for ; Wed, 25 Nov 2020 09:43:13 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728828AbgKYJnL (ORCPT ); Wed, 25 Nov 2020 04:43:11 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57100 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727126AbgKYJlz (ORCPT ); Wed, 25 Nov 2020 04:41:55 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E6BA8305D3CA; Wed, 25 Nov 2020 11:35:52 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C4A833072784; Wed, 25 Nov 2020 11:35:52 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 62/81] KVM: introspection: restore the state of CR3 interception on unhook Date: Wed, 25 Nov 2020 11:35:41 +0200 Message-Id: <20201125093600.2766-63-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This commit also ensures that the introspection tool and the userspace do not disable each other the CR3-write VM-exit. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvmi_host.h | 4 ++ arch/x86/kvm/kvmi.c | 67 ++++++++++++++++++++++++++++++-- arch/x86/kvm/kvmi.h | 4 +- arch/x86/kvm/kvmi_msg.c | 4 +- arch/x86/kvm/svm/svm.c | 5 +++ arch/x86/kvm/vmx/vmx.c | 5 +++ 6 files changed, 81 insertions(+), 8 deletions(-) diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 7613088d0ae2..edbedf031467 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -16,6 +16,7 @@ struct kvmi_interception { bool cleanup; bool restore_interception; struct kvmi_monitor_interception breakpoint; + struct kvmi_monitor_interception cr3w; }; struct kvm_vcpu_arch_introspection { @@ -34,6 +35,7 @@ bool kvmi_monitor_bp_intercept(struct kvm_vcpu *vcpu, u32 dbg); bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, unsigned long old_value, unsigned long *new_value); bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu); +bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable); #else /* CONFIG_KVM_INTROSPECTION */ @@ -44,6 +46,8 @@ static inline bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, unsigned long *new_value) { return true; } static inline bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu) { return false; } +static inline bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, + bool enable) { return false; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 2bb6b4bb932b..8ad3698e5988 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -230,6 +230,59 @@ static void kvmi_arch_disable_bp_intercept(struct kvm_vcpu *vcpu) vcpu->arch.kvmi->breakpoint.kvm_intercepted = false; } +static bool monitor_cr3w_fct_kvmi(struct kvm_vcpu *vcpu, bool enable) +{ + vcpu->arch.kvmi->cr3w.kvmi_intercepted = enable; + + if (enable) + vcpu->arch.kvmi->cr3w.kvm_intercepted = + kvm_x86_ops.cr3_write_intercepted(vcpu); + else if (vcpu->arch.kvmi->cr3w.kvm_intercepted) + return true; + + return false; +} + +static bool monitor_cr3w_fct_kvm(struct kvm_vcpu *vcpu, bool enable) +{ + if (!vcpu->arch.kvmi->cr3w.kvmi_intercepted) + return false; + + vcpu->arch.kvmi->cr3w.kvm_intercepted = enable; + + if (!enable) + return true; + + return false; +} + +/* + * Returns true if one side (kvm or kvmi) tries to disable the CR3 write + * interception while the other side is still tracking it. + */ +bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable) +{ + struct kvmi_interception *arch_vcpui = READ_ONCE(vcpu->arch.kvmi); + + return (arch_vcpui && arch_vcpui->cr3w.monitor_fct(vcpu, enable)); +} +EXPORT_SYMBOL(kvmi_monitor_cr3w_intercept); + +static void kvmi_control_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable) +{ + vcpu->arch.kvmi->cr3w.monitor_fct = monitor_cr3w_fct_kvmi; + kvm_x86_ops.control_cr3_intercept(vcpu, CR_TYPE_W, enable); + vcpu->arch.kvmi->cr3w.monitor_fct = monitor_cr3w_fct_kvm; +} + +static void kvmi_arch_disable_cr3w_intercept(struct kvm_vcpu *vcpu) +{ + kvmi_control_cr3w_intercept(vcpu, false); + + vcpu->arch.kvmi->cr3w.kvmi_intercepted = false; + vcpu->arch.kvmi->cr3w.kvm_intercepted = false; +} + int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, unsigned int event_id, bool enable) { @@ -269,6 +322,7 @@ void kvmi_arch_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len) static void kvmi_arch_restore_interception(struct kvm_vcpu *vcpu) { kvmi_arch_disable_bp_intercept(vcpu); + kvmi_arch_disable_cr3w_intercept(vcpu); } bool kvmi_arch_clean_up_interception(struct kvm_vcpu *vcpu) @@ -293,8 +347,13 @@ bool kvmi_arch_vcpu_alloc_interception(struct kvm_vcpu *vcpu) return false; arch_vcpui->breakpoint.monitor_fct = monitor_bp_fct_kvm; + arch_vcpui->cr3w.monitor_fct = monitor_cr3w_fct_kvm; - /* pair with kvmi_monitor_bp_intercept() */ + /* + * paired with: + * - kvmi_monitor_bp_intercept() + * - kvmi_monitor_cr3w_intercept() + */ smp_wmb(); WRITE_ONCE(vcpu->arch.kvmi, arch_vcpui); @@ -326,7 +385,7 @@ void kvmi_arch_request_interception_cleanup(struct kvm_vcpu *vcpu, int kvmi_arch_cmd_vcpu_control_cr(struct kvm_vcpu *vcpu, int cr, bool enable) { if (cr == 3) - kvm_x86_ops.control_cr3_intercept(vcpu, CR_TYPE_W, enable); + kvmi_control_cr3w_intercept(vcpu, enable); if (enable) set_bit(cr, VCPUI(vcpu)->arch.cr_mask); @@ -346,8 +405,8 @@ static bool __kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, if (!test_bit(cr, VCPUI(vcpu)->arch.cr_mask)) return true; - action = kvmi_msg_send_cr(vcpu, cr, old_value, *new_value, - &reply_value); + action = kvmi_msg_send_vcpu_cr(vcpu, cr, old_value, *new_value, + &reply_value); switch (action) { case KVMI_EVENT_ACTION_CONTINUE: *new_value = reply_value; diff --git a/arch/x86/kvm/kvmi.h b/arch/x86/kvm/kvmi.h index 6f4aaebb67f9..6a444428b831 100644 --- a/arch/x86/kvm/kvmi.h +++ b/arch/x86/kvm/kvmi.h @@ -9,7 +9,7 @@ void kvmi_arch_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu, const struct kvm_regs *regs); int kvmi_arch_cmd_vcpu_control_cr(struct kvm_vcpu *vcpu, int cr, bool enable); -u32 kvmi_msg_send_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, - u64 new_value, u64 *ret_value); +u32 kvmi_msg_send_vcpu_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, + u64 new_value, u64 *ret_value); #endif diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 1682bb27105f..34aedb4785e4 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -163,8 +163,8 @@ kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) return id < ARRAY_SIZE(msg_vcpu) ? msg_vcpu[id] : NULL; } -u32 kvmi_msg_send_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, - u64 new_value, u64 *ret_value) +u32 kvmi_msg_send_vcpu_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, + u64 new_value, u64 *ret_value) { struct kvmi_vcpu_event_cr e; struct kvmi_vcpu_event_cr_reply r; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index baca455212f9..f3ec79e6c39a 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1787,6 +1787,11 @@ static void svm_control_cr3_intercept(struct kvm_vcpu *vcpu, int type, { struct vcpu_svm *svm = to_svm(vcpu); +#ifdef CONFIG_KVM_INTROSPECTION + if ((type & CR_TYPE_W) && kvmi_monitor_cr3w_intercept(vcpu, enable)) + type &= ~CR_TYPE_W; +#endif /* CONFIG_KVM_INTROSPECTION */ + if (type & CR_TYPE_R) enable ? svm_set_intercept(svm, INTERCEPT_CR3_READ) : svm_clr_intercept(svm, INTERCEPT_CR3_READ); diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 723e78529146..837447541710 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -2985,6 +2985,11 @@ static void vmx_control_cr3_intercept(struct kvm_vcpu *vcpu, int type, struct vcpu_vmx *vmx = to_vmx(vcpu); u32 cr3_exec_control = 0; +#ifdef CONFIG_KVM_INTROSPECTION + if ((type & CR_TYPE_W) && kvmi_monitor_cr3w_intercept(vcpu, enable)) + type &= ~CR_TYPE_W; +#endif /* CONFIG_KVM_INTROSPECTION */ + if (type & CR_TYPE_R) cr3_exec_control |= CPU_BASED_CR3_STORE_EXITING; if (type & CR_TYPE_W) From patchwork Wed Nov 25 09:35:42 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930911 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3681AC64E7A for ; Wed, 25 Nov 2020 09:42:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E19252083E for ; Wed, 25 Nov 2020 09:42:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728528AbgKYJmg (ORCPT ); Wed, 25 Nov 2020 04:42:36 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57224 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728077AbgKYJmE (ORCPT ); Wed, 25 Nov 2020 04:42:04 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 272B9305D3CB; Wed, 25 Nov 2020 11:35:53 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id EC2873072785; Wed, 25 Nov 2020 11:35:52 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?b?TmljdciZ?= =?utf-8?b?b3IgQ8OuyJt1?= , =?utf-8?q?Adalbert_Laz?= =?utf-8?q?=C4=83r?= Subject: [PATCH v10 63/81] KVM: introspection: add KVMI_VCPU_INJECT_EXCEPTION + KVMI_VCPU_EVENT_TRAP Date: Wed, 25 Nov 2020 11:35:42 +0200 Message-Id: <20201125093600.2766-64-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu The KVMI_VCPU_INJECT_EXCEPTION command is used by the introspection tool to inject exceptions, for example, to get a page from swap. The exception is injected right before entering in guest unless there is already an exception pending. The introspection tool is notified with an KVMI_VCPU_EVENT_TRAP event about the success of the injection. In case of failure, the introspection tool is expected to try again later. Signed-off-by: Mihai Donțu Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 76 +++++++++++ arch/x86/include/asm/kvmi_host.h | 11 ++ arch/x86/include/uapi/asm/kvmi.h | 16 +++ arch/x86/kvm/kvmi.c | 110 ++++++++++++++++ arch/x86/kvm/kvmi.h | 3 + arch/x86/kvm/kvmi_msg.c | 55 +++++++- arch/x86/kvm/x86.c | 2 + include/uapi/linux/kvmi.h | 14 +- .../testing/selftests/kvm/x86_64/kvmi_test.c | 124 ++++++++++++++++++ virt/kvm/introspection/kvmi.c | 2 + virt/kvm/introspection/kvmi_int.h | 4 + virt/kvm/introspection/kvmi_msg.c | 16 ++- 12 files changed, 419 insertions(+), 14 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 85e14b82aa2f..e688ac387faf 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -550,6 +550,7 @@ because these are sent as a result of certain commands (but they can be disallowed by the device manager) :: KVMI_VCPU_EVENT_PAUSE + KVMI_VCPU_EVENT_TRAP The VM events (e.g. *KVMI_VM_EVENT_UNHOOK*) are controlled with the *KVMI_VM_CONTROL_EVENTS* command. @@ -736,6 +737,46 @@ ID set. * -KVM_EINVAL - the padding is not zero * -KVM_EAGAIN - the selected vCPU can't be introspected yet +16. KVMI_VCPU_INJECT_EXCEPTION +------------------------------ + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_inject_exception { + __u8 nr; + __u8 padding1; + __u16 padding2; + __u32 error_code; + __u64 address; + }; + +:Returns: + +:: + + struct kvmi_error_code + +Injects a vCPU exception (``nr``) with or without an error code (``error_code``). +For page fault exceptions, the guest virtual address (``address``) +has to be specified too. + +The *KVMI_VCPU_EVENT_TRAP* event will be sent with the effective injected +exception. + +:Errors: + +* -KVM_EPERM - the *KVMI_VCPU_EVENT_TRAP* event is disallowed +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_EBUSY - another *KVMI_VCPU_INJECT_EXCEPTION*-*KVMI_VCPU_EVENT_TRAP* + pair is in progress + Events ====== @@ -966,3 +1007,38 @@ register (see **KVMI_VCPU_CONTROL_EVENTS**). (``cr``), the old value (``old_value``) and the new value (``new_value``) are sent to the introspection tool. The *CONTINUE* action will set the ``new_val``. + +6. KVMI_VCPU_EVENT_TRAP +----------------------- + +:Architectures: x86 +:Versions: >= 1 +:Actions: CONTINUE, CRASH +:Parameters: + +:: + + struct kvmi_vcpu_event; + struct kvmi_vcpu_event_trap { + __u8 nr; + __u8 padding1; + __u16 padding2; + __u32 error_code; + __u64 address; + }; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + +This event is sent if a previous *KVMI_VCPU_INJECT_EXCEPTION* command +took place. Because it has a high priority, it will be sent before any +other vCPU introspection event. + +``kvmi_vcpu_event`` (with the vCPU state), exception/interrupt number +(``nr``), exception code (``error_code``) and ``address`` are sent to +the introspection tool, which should check if its exception has been +injected or overridden. diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index edbedf031467..97f5b1a01c9e 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -24,6 +24,15 @@ struct kvm_vcpu_arch_introspection { bool have_delayed_regs; DECLARE_BITMAP(cr_mask, KVMI_NUM_CR); + + struct { + u8 nr; + u32 error_code; + bool error_code_valid; + u64 address; + bool pending; + bool send_event; + } exception; }; struct kvm_arch_introspection { @@ -36,6 +45,7 @@ bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, unsigned long old_value, unsigned long *new_value); bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu); bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable); +void kvmi_enter_guest(struct kvm_vcpu *vcpu); #else /* CONFIG_KVM_INTROSPECTION */ @@ -48,6 +58,7 @@ static inline bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, static inline bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu) { return false; } static inline bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable) { return false; } +static inline void kvmi_enter_guest(struct kvm_vcpu *vcpu) { } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 32cd17488058..aa991fbab473 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -79,4 +79,20 @@ struct kvmi_vcpu_event_cr_reply { __u64 new_val; }; +struct kvmi_vcpu_event_trap { + __u8 nr; + __u8 padding1; + __u16 padding2; + __u32 error_code; + __u64 address; +}; + +struct kvmi_vcpu_inject_exception { + __u8 nr; + __u8 padding1; + __u16 padding2; + __u32 error_code; + __u64 address; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 8ad3698e5988..52b46d56ebb5 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -15,6 +15,7 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) set_bit(KVMI_VCPU_EVENT_BREAKPOINT, supported); set_bit(KVMI_VCPU_EVENT_CR, supported); set_bit(KVMI_VCPU_EVENT_HYPERCALL, supported); + set_bit(KVMI_VCPU_EVENT_TRAP, supported); } static unsigned int kvmi_vcpu_mode(const struct kvm_vcpu *vcpu, @@ -457,3 +458,112 @@ bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu) return ret; } EXPORT_SYMBOL(kvmi_cr3_intercepted); + +int kvmi_arch_cmd_vcpu_inject_exception(struct kvm_vcpu *vcpu, + const struct kvmi_vcpu_inject_exception *req) +{ + struct kvm_vcpu_arch_introspection *arch = &VCPUI(vcpu)->arch; + bool has_error; + + arch->exception.pending = true; + + has_error = x86_exception_has_error_code(req->nr); + + arch->exception.nr = req->nr; + arch->exception.error_code = has_error ? req->error_code : 0; + arch->exception.error_code_valid = has_error; + arch->exception.address = req->address; + + return 0; +} + +static void kvmi_queue_exception(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_arch_introspection *arch = &VCPUI(vcpu)->arch; + struct x86_exception e = { + .vector = arch->exception.nr, + .error_code_valid = arch->exception.error_code_valid, + .error_code = arch->exception.error_code, + .address = arch->exception.address, + }; + + if (e.vector == PF_VECTOR) + kvm_inject_page_fault(vcpu, &e); + else if (e.error_code_valid) + kvm_queue_exception_e(vcpu, e.vector, e.error_code); + else + kvm_queue_exception(vcpu, e.vector); +} + +static void kvmi_save_injected_event(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + + vcpui->arch.exception.error_code = 0; + vcpui->arch.exception.error_code_valid = false; + + vcpui->arch.exception.address = vcpu->arch.cr2; + if (vcpu->arch.exception.injected) { + vcpui->arch.exception.nr = vcpu->arch.exception.nr; + vcpui->arch.exception.error_code_valid = + x86_exception_has_error_code(vcpu->arch.exception.nr); + vcpui->arch.exception.error_code = vcpu->arch.exception.error_code; + } else if (vcpu->arch.interrupt.injected) { + vcpui->arch.exception.nr = vcpu->arch.interrupt.nr; + } +} + +static void kvmi_inject_pending_exception(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + + if (!kvm_event_needs_reinjection(vcpu)) { + kvmi_queue_exception(vcpu); + kvm_inject_pending_exception(vcpu); + } + + kvmi_save_injected_event(vcpu); + + vcpui->arch.exception.pending = false; + vcpui->arch.exception.send_event = true; + kvm_make_request(KVM_REQ_INTROSPECTION, vcpu); +} + +void kvmi_enter_guest(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_introspection *vcpui; + struct kvm_introspection *kvmi; + + kvmi = kvmi_get(vcpu->kvm); + if (kvmi) { + vcpui = VCPUI(vcpu); + + if (vcpui->arch.exception.pending) + kvmi_inject_pending_exception(vcpu); + + kvmi_put(vcpu->kvm); + } +} + +static void kvmi_send_trap_event(struct kvm_vcpu *vcpu) +{ + u32 action; + + action = kvmi_msg_send_vcpu_trap(vcpu); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + } +} + +void kvmi_arch_send_pending_event(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + + if (vcpui->arch.exception.send_event) { + vcpui->arch.exception.send_event = false; + kvmi_send_trap_event(vcpu); + } +} diff --git a/arch/x86/kvm/kvmi.h b/arch/x86/kvm/kvmi.h index 6a444428b831..265fece148d2 100644 --- a/arch/x86/kvm/kvmi.h +++ b/arch/x86/kvm/kvmi.h @@ -8,8 +8,11 @@ int kvmi_arch_cmd_vcpu_get_registers(struct kvm_vcpu *vcpu, void kvmi_arch_cmd_vcpu_set_registers(struct kvm_vcpu *vcpu, const struct kvm_regs *regs); int kvmi_arch_cmd_vcpu_control_cr(struct kvm_vcpu *vcpu, int cr, bool enable); +int kvmi_arch_cmd_vcpu_inject_exception(struct kvm_vcpu *vcpu, + const struct kvmi_vcpu_inject_exception *req); u32 kvmi_msg_send_vcpu_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, u64 new_value, u64 *ret_value); +u32 kvmi_msg_send_vcpu_trap(struct kvm_vcpu *vcpu); #endif diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 34aedb4785e4..0b73142ab73a 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -150,12 +150,37 @@ static int handle_vcpu_control_cr(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); } +static int handle_vcpu_inject_exception(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vcpu_inject_exception *req = _req; + struct kvm_vcpu_arch_introspection *arch; + struct kvm_vcpu *vcpu = job->vcpu; + int ec; + + arch = &VCPUI(vcpu)->arch; + + if (!kvmi_is_event_allowed(KVMI(vcpu->kvm), KVMI_VCPU_EVENT_TRAP)) + ec = -KVM_EPERM; + else if (req->padding1 || req->padding2) + ec = -KVM_EINVAL; + else if (VCPUI(vcpu)->arch.exception.pending || + VCPUI(vcpu)->arch.exception.send_event) + ec = -KVM_EBUSY; + else + ec = kvmi_arch_cmd_vcpu_inject_exception(vcpu, req); + + return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { - [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, - [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, - [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, - [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, - [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, + [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, + [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, + [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, + [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, + [KVMI_VCPU_INJECT_EXCEPTION] = handle_vcpu_inject_exception, + [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, }; kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) @@ -187,3 +212,23 @@ u32 kvmi_msg_send_vcpu_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, return action; } + +u32 kvmi_msg_send_vcpu_trap(struct kvm_vcpu *vcpu) +{ + struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); + struct kvmi_vcpu_event_trap e; + u32 action; + int err; + + memset(&e, 0, sizeof(e)); + e.nr = vcpui->arch.exception.nr; + e.error_code = vcpui->arch.exception.error_code; + e.address = vcpui->arch.exception.address; + + err = __kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_TRAP, + &e, sizeof(e), NULL, 0, &action); + if (err) + action = KVMI_EVENT_ACTION_CONTINUE; + + return action; +} diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 9a4ec0b4714c..beb183b9f979 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -9008,6 +9008,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) goto cancel_injection; } + kvmi_enter_guest(vcpu); + if (req_immediate_exit) { kvm_make_request(KVM_REQ_EVENT, vcpu); kvm_x86_ops.request_immediate_exit(vcpu); diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index c1d8cf02018b..263d98a5903e 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -36,12 +36,13 @@ enum { enum { KVMI_VCPU_EVENT = KVMI_VCPU_MESSAGE_ID(0), - KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), - KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2), - KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3), - KVMI_VCPU_SET_REGISTERS = KVMI_VCPU_MESSAGE_ID(4), - KVMI_VCPU_GET_CPUID = KVMI_VCPU_MESSAGE_ID(5), - KVMI_VCPU_CONTROL_CR = KVMI_VCPU_MESSAGE_ID(6), + KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), + KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2), + KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3), + KVMI_VCPU_SET_REGISTERS = KVMI_VCPU_MESSAGE_ID(4), + KVMI_VCPU_GET_CPUID = KVMI_VCPU_MESSAGE_ID(5), + KVMI_VCPU_CONTROL_CR = KVMI_VCPU_MESSAGE_ID(6), + KVMI_VCPU_INJECT_EXCEPTION = KVMI_VCPU_MESSAGE_ID(7), KVMI_NEXT_VCPU_MESSAGE }; @@ -60,6 +61,7 @@ enum { KVMI_VCPU_EVENT_HYPERCALL = KVMI_VCPU_EVENT_ID(1), KVMI_VCPU_EVENT_BREAKPOINT = KVMI_VCPU_EVENT_ID(2), KVMI_VCPU_EVENT_CR = KVMI_VCPU_EVENT_ID(3), + KVMI_VCPU_EVENT_TRAP = KVMI_VCPU_EVENT_ID(4), KVMI_NEXT_VCPU_EVENT }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 6a1103eab77a..dc9f2f0d99e8 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -49,6 +49,7 @@ struct vcpu_worker_data { struct kvm_vm *vm; int vcpu_id; int test_id; + bool restart_on_shutdown; }; enum { @@ -633,6 +634,10 @@ static void *vcpu_worker(void *data) vcpu_run(ctx->vm, ctx->vcpu_id); + if (run->exit_reason == KVM_EXIT_SHUTDOWN && + ctx->restart_on_shutdown) + continue; + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "vcpu_run() failed, test_id %d, exit reason %u (%s)\n", ctx->test_id, run->exit_reason, @@ -1199,6 +1204,124 @@ static void test_cmd_vcpu_control_cr(struct kvm_vm *vm) test_invalid_vcpu_control_cr(vm); } +static void __inject_exception(int nr) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_inject_exception cmd; + } req = {}; + int r; + + req.cmd.nr = nr; + + r = __do_vcpu0_command(KVMI_VCPU_INJECT_EXCEPTION, + &req.hdr, sizeof(req), NULL, 0); + TEST_ASSERT(r == 0, + "KVMI_VCPU_INJECT_EXCEPTION failed, error %d(%s)\n", + -r, kvm_strerror(-r)); +} + +static void test_disallowed_trap_event(struct kvm_vm *vm) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_inject_exception cmd; + } req = {}; + + disallow_event(vm, KVMI_VCPU_EVENT_TRAP); + test_vcpu0_command(vm, KVMI_VCPU_INJECT_EXCEPTION, + &req.hdr, sizeof(req), NULL, 0, -KVM_EPERM); + allow_event(vm, KVMI_VCPU_EVENT_TRAP); +} + +static void receive_exception_event(int nr) +{ + struct kvmi_msg_hdr hdr; + struct { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_trap trap; + } ev; + struct vcpu_reply rpl = {}; + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), KVMI_VCPU_EVENT_TRAP); + + pr_debug("Exception event: vector %u, error_code 0x%x, address 0x%llx\n", + ev.trap.nr, ev.trap.error_code, ev.trap.address); + + TEST_ASSERT(ev.trap.nr == nr, + "Injected exception %u instead of %u\n", + ev.trap.nr, nr); + + reply_to_event(&hdr, &ev.vcpu_ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl, sizeof(rpl)); +} + +static void test_succeded_ud_injection(void) +{ + __u8 ud_vector = 6; + + __inject_exception(ud_vector); + + receive_exception_event(ud_vector); +} + +static void test_failed_ud_injection(struct kvm_vm *vm, + struct vcpu_worker_data *data) +{ + struct kvmi_msg_hdr hdr; + struct { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_breakpoint bp; + } ev; + struct vcpu_reply rpl = {}; + __u8 ud_vector = 6, bp_vector = 3; + + WRITE_ONCE(data->test_id, GUEST_TEST_BP); + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), + KVMI_VCPU_EVENT_BREAKPOINT); + + /* skip the breakpoint instruction, next time guest_bp_test() runs */ + ev.vcpu_ev.common.arch.regs.rip += ev.bp.insn_len; + __set_registers(vm, &ev.vcpu_ev.common.arch.regs); + + __inject_exception(ud_vector); + + /* reinject the #BP exception because of the continue action */ + reply_to_event(&hdr, &ev.vcpu_ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl, sizeof(rpl)); + + receive_exception_event(bp_vector); +} + +static void test_cmd_vcpu_inject_exception(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = { + .vm = vm, + .vcpu_id = VCPU_ID, + .restart_on_shutdown = true, + }; + pthread_t vcpu_thread; + + if (!is_intel_cpu()) { + print_skip("TODO: %s() - make it work with AMD", __func__); + return; + } + + test_disallowed_trap_event(vm); + + enable_vcpu_event(vm, KVMI_VCPU_EVENT_BREAKPOINT); + vcpu_thread = start_vcpu_worker(&data); + + test_succeded_ud_injection(); + test_failed_ud_injection(vm, &data); + + wait_vcpu_worker(vcpu_thread); + disable_vcpu_event(vm, KVMI_VCPU_EVENT_BREAKPOINT); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1223,6 +1346,7 @@ static void test_introspection(struct kvm_vm *vm) test_event_breakpoint(vm); test_cmd_vm_control_cleanup(vm); test_cmd_vcpu_control_cr(vm); + test_cmd_vcpu_inject_exception(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index e6333708c584..b366ba6820d6 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -845,6 +845,8 @@ void kvmi_handle_requests(struct kvm_vcpu *vcpu) if (!kvmi) goto out; + kvmi_arch_send_pending_event(vcpu); + for (;;) { kvmi_run_jobs(vcpu); diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index b1877a770fcb..0a7a8285b981 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -48,6 +48,9 @@ int kvmi_msg_send_unhook(struct kvm_introspection *kvmi); int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, void *ev, size_t ev_size, void *rpl, size_t rpl_size, u32 *action); +int __kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, + void *ev, size_t ev_size, + void *rpl, size_t rpl_size, u32 *action); int kvmi_msg_vcpu_reply(const struct kvmi_vcpu_msg_job *job, const struct kvmi_msg_hdr *msg, int err, const void *rpl, size_t rpl_size); @@ -100,5 +103,6 @@ bool kvmi_arch_is_agent_hypercall(struct kvm_vcpu *vcpu); void kvmi_arch_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len); int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, unsigned int event_id, bool enable); +void kvmi_arch_send_pending_event(struct kvm_vcpu *vcpu); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 9c1d69caaf11..762fb5227dd9 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -715,9 +715,9 @@ static int kvmi_fill_and_sent_vcpu_event(struct kvm_vcpu *vcpu, return kvmi_sock_write(kvmi, vec, n, msg_size); } -int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, - void *ev, size_t ev_size, - void *rpl, size_t rpl_size, u32 *action) +int __kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, + void *ev, size_t ev_size, + void *rpl, size_t rpl_size, u32 *action) { struct kvm_vcpu_introspection *vcpui = VCPUI(vcpu); struct kvm_introspection *kvmi = KVMI(vcpu->kvm); @@ -745,6 +745,16 @@ int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, u32 ev_id, return err; } +int kvmi_send_vcpu_event(struct kvm_vcpu *vcpu, + u32 ev_id, void *ev, size_t ev_size, + void *rpl, size_t rpl_size, u32 *action) +{ + kvmi_arch_send_pending_event(vcpu); + + return __kvmi_send_vcpu_event(vcpu, ev_id, ev, ev_size, + rpl, rpl_size, action); +} + u32 kvmi_msg_send_vcpu_pause(struct kvm_vcpu *vcpu) { u32 action; From patchwork Wed Nov 25 09:35:43 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930837 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 44746C8301B for ; Wed, 25 Nov 2020 09:42:13 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EF34621527 for ; Wed, 25 Nov 2020 09:42:12 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728634AbgKYJmL (ORCPT ); Wed, 25 Nov 2020 04:42:11 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57330 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728595AbgKYJmJ (ORCPT ); Wed, 25 Nov 2020 04:42:09 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 4D698305D3E2; Wed, 25 Nov 2020 11:35:53 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 296423072784; Wed, 25 Nov 2020 11:35:53 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?=C8=98tefan_=C8=98icleru?= , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 64/81] KVM: introspection: add KVMI_VM_GET_MAX_GFN Date: Wed, 25 Nov 2020 11:35:43 +0200 Message-Id: <20201125093600.2766-65-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Ștefan Șicleru The introspection tool will use this command to get the memory address range for which it can set access restrictions. Signed-off-by: Ștefan Șicleru Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 19 +++++++++++++++++++ include/uapi/linux/kvmi.h | 5 +++++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 12 ++++++++++++ virt/kvm/introspection/kvmi_msg.c | 13 +++++++++++++ 4 files changed, 49 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index e688ac387faf..ecf4207b42d0 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -777,6 +777,25 @@ exception. * -KVM_EBUSY - another *KVMI_VCPU_INJECT_EXCEPTION*-*KVMI_VCPU_EVENT_TRAP* pair is in progress +17. KVMI_VM_GET_MAX_GFN +----------------------- + +:Architectures: all +:Versions: >= 1 +:Parameters: none +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_vm_get_max_gfn_reply { + __u64 gfn; + }; + +Provides the maximum GFN allocated to the VM by walking through all +memory slots. Stricly speaking, the returned value refers to the first +inaccessible GFN, next to the maximum accessible GFN. + Events ====== diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 263d98a5903e..d0e06363c407 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -29,6 +29,7 @@ enum { KVMI_VM_WRITE_PHYSICAL = KVMI_VM_MESSAGE_ID(7), KVMI_VM_PAUSE_VCPU = KVMI_VM_MESSAGE_ID(8), KVMI_VM_CONTROL_CLEANUP = KVMI_VM_MESSAGE_ID(9), + KVMI_VM_GET_MAX_GFN = KVMI_VM_MESSAGE_ID(10), KVMI_NEXT_VM_MESSAGE }; @@ -177,4 +178,8 @@ struct kvmi_vm_control_cleanup { __u8 padding[7]; }; +struct kvmi_vm_get_max_gfn_reply { + __u64 gfn; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index dc9f2f0d99e8..b4565802db22 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1322,6 +1322,17 @@ static void test_cmd_vcpu_inject_exception(struct kvm_vm *vm) disable_vcpu_event(vm, KVMI_VCPU_EVENT_BREAKPOINT); } +static void test_cmd_vm_get_max_gfn(void) +{ + struct kvmi_vm_get_max_gfn_reply rpl; + struct kvmi_msg_hdr req; + + test_vm_command(KVMI_VM_GET_MAX_GFN, &req, sizeof(req), + &rpl, sizeof(rpl), 0); + + pr_debug("max_gfn: 0x%llx\n", rpl.gfn); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1347,6 +1358,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vm_control_cleanup(vm); test_cmd_vcpu_control_cr(vm); test_cmd_vcpu_inject_exception(vm); + test_cmd_vm_get_max_gfn(); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 762fb5227dd9..42d066e92ba2 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -290,6 +290,18 @@ static int handle_vm_control_cleanup(struct kvm_introspection *kvmi, return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); } +static int handle_vm_get_max_gfn(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *req) +{ + struct kvmi_vm_get_max_gfn_reply rpl; + + memset(&rpl, 0, sizeof(rpl)); + rpl.gfn = kvm_get_max_gfn(kvmi->kvm); + + return kvmi_msg_vm_reply(kvmi, msg, 0, &rpl, sizeof(rpl)); +} + /* * These commands are executed by the receiving thread. */ @@ -300,6 +312,7 @@ static kvmi_vm_msg_fct const msg_vm[] = { [KVMI_VM_CONTROL_CLEANUP] = handle_vm_control_cleanup, [KVMI_VM_CONTROL_EVENTS] = handle_vm_control_events, [KVMI_VM_GET_INFO] = handle_vm_get_info, + [KVMI_VM_GET_MAX_GFN] = handle_vm_get_max_gfn, [KVMI_VM_PAUSE_VCPU] = handle_vm_pause_vcpu, [KVMI_VM_READ_PHYSICAL] = handle_vm_read_physical, [KVMI_VM_WRITE_PHYSICAL] = handle_vm_write_physical, From patchwork Wed Nov 25 09:35:44 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930891 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4E916C8301D for ; Wed, 25 Nov 2020 09:42:44 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 01FE22083E for ; Wed, 25 Nov 2020 09:42:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728703AbgKYJmn (ORCPT ); Wed, 25 Nov 2020 04:42:43 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57212 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728338AbgKYJmD (ORCPT ); Wed, 25 Nov 2020 04:42:03 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 776C6305D3E3; Wed, 25 Nov 2020 11:35:53 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 51CE93072785; Wed, 25 Nov 2020 11:35:53 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?b?TmljdciZ?= =?utf-8?b?b3IgQ8OuyJt1?= , =?utf-8?q?Adalbert_Laz?= =?utf-8?q?=C4=83r?= Subject: [PATCH v10 65/81] KVM: introspection: add KVMI_VCPU_EVENT_XSETBV Date: Wed, 25 Nov 2020 11:35:44 +0200 Message-Id: <20201125093600.2766-66-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This event is sent when an extended control register XCR is going to be changed. Signed-off-by: Mihai Donțu Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 34 ++++++++ arch/x86/include/asm/kvmi_host.h | 4 + arch/x86/include/uapi/asm/kvmi.h | 7 ++ arch/x86/kvm/kvmi.c | 30 +++++++ arch/x86/kvm/kvmi.h | 2 + arch/x86/kvm/kvmi_msg.c | 20 +++++ arch/x86/kvm/x86.c | 6 ++ include/uapi/linux/kvmi.h | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 84 +++++++++++++++++++ 9 files changed, 188 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index ecf4207b42d0..24dc1867c1f1 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -541,6 +541,7 @@ the following events:: KVMI_VCPU_EVENT_BREAKPOINT KVMI_VCPU_EVENT_CR KVMI_VCPU_EVENT_HYPERCALL + KVMI_VCPU_EVENT_XSETBV When an event is enabled, the introspection tool is notified and must reply with: continue, retry, crash, etc. (see **Events** below). @@ -1061,3 +1062,36 @@ other vCPU introspection event. (``nr``), exception code (``error_code``) and ``address`` are sent to the introspection tool, which should check if its exception has been injected or overridden. + +7. KVMI_VCPU_EVENT_XSETBV +------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Actions: CONTINUE, CRASH +:Parameters: + +:: + + struct kvmi_vcpu_event; + struct kvmi_vcpu_event_xsetbv { + __u8 xcr; + __u8 padding[7]; + __u64 old_value; + __u64 new_value; + }; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + +This event is sent when an extended control register XCR is going +to be changed and the introspection has been enabled for this event +(see *KVMI_VCPU_CONTROL_EVENTS*). + +``kvmi_vcpu_event`` (with the vCPU state), the extended control register +number (``xcr``), the old value (``old_value``) and the new value +(``new_value``) are sent to the introspection tool. diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 97f5b1a01c9e..d66349208a6b 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -46,6 +46,8 @@ bool kvmi_cr_event(struct kvm_vcpu *vcpu, unsigned int cr, bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu); bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable); void kvmi_enter_guest(struct kvm_vcpu *vcpu); +void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, + u64 old_value, u64 new_value); #else /* CONFIG_KVM_INTROSPECTION */ @@ -59,6 +61,8 @@ static inline bool kvmi_cr3_intercepted(struct kvm_vcpu *vcpu) { return false; } static inline bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable) { return false; } static inline void kvmi_enter_guest(struct kvm_vcpu *vcpu) { } +static inline void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, + u64 old_value, u64 new_value) { } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index aa991fbab473..604a8b3d4ac2 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -95,4 +95,11 @@ struct kvmi_vcpu_inject_exception { __u64 address; }; +struct kvmi_vcpu_event_xsetbv { + __u8 xcr; + __u8 padding[7]; + __u64 old_value; + __u64 new_value; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 52b46d56ebb5..5219b6faf4b5 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -16,6 +16,7 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) set_bit(KVMI_VCPU_EVENT_CR, supported); set_bit(KVMI_VCPU_EVENT_HYPERCALL, supported); set_bit(KVMI_VCPU_EVENT_TRAP, supported); + set_bit(KVMI_VCPU_EVENT_XSETBV, supported); } static unsigned int kvmi_vcpu_mode(const struct kvm_vcpu *vcpu, @@ -567,3 +568,32 @@ void kvmi_arch_send_pending_event(struct kvm_vcpu *vcpu) kvmi_send_trap_event(vcpu); } } + +static void __kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, + u64 old_value, u64 new_value) +{ + u32 action; + + action = kvmi_msg_send_vcpu_xsetbv(vcpu, xcr, old_value, new_value); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + } +} + +void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, + u64 old_value, u64 new_value) +{ + struct kvm_introspection *kvmi; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return; + + if (is_vcpu_event_enabled(vcpu, KVMI_VCPU_EVENT_XSETBV)) + __kvmi_xsetbv_event(vcpu, xcr, old_value, new_value); + + kvmi_put(vcpu->kvm); +} diff --git a/arch/x86/kvm/kvmi.h b/arch/x86/kvm/kvmi.h index 265fece148d2..43bc956d740c 100644 --- a/arch/x86/kvm/kvmi.h +++ b/arch/x86/kvm/kvmi.h @@ -14,5 +14,7 @@ int kvmi_arch_cmd_vcpu_inject_exception(struct kvm_vcpu *vcpu, u32 kvmi_msg_send_vcpu_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, u64 new_value, u64 *ret_value); u32 kvmi_msg_send_vcpu_trap(struct kvm_vcpu *vcpu); +u32 kvmi_msg_send_vcpu_xsetbv(struct kvm_vcpu *vcpu, u8 xcr, + u64 old_value, u64 new_value); #endif diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 0b73142ab73a..d0dc917118b5 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -232,3 +232,23 @@ u32 kvmi_msg_send_vcpu_trap(struct kvm_vcpu *vcpu) return action; } + +u32 kvmi_msg_send_vcpu_xsetbv(struct kvm_vcpu *vcpu, u8 xcr, + u64 old_value, u64 new_value) +{ + struct kvmi_vcpu_event_xsetbv e; + u32 action; + int err; + + memset(&e, 0, sizeof(e)); + e.xcr = xcr; + e.old_value = old_value; + e.new_value = new_value; + + err = kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_XSETBV, + &e, sizeof(e), NULL, 0, &action); + if (err) + action = KVMI_EVENT_ACTION_CONTINUE; + + return action; +} diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index beb183b9f979..d62294fee424 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -955,6 +955,12 @@ static int __kvm_set_xcr(struct kvm_vcpu *vcpu, u32 index, u64 xcr) } vcpu->arch.xcr0 = xcr0; +#ifdef CONFIG_KVM_INTROSPECTION + if (index == 0 && xcr0 != old_xcr0) + kvmi_xsetbv_event(vcpu, 0, old_xcr0, xcr0); +#endif /* CONFIG_KVM_INTROSPECTION */ + + if ((xcr0 ^ old_xcr0) & XFEATURE_MASK_EXTEND) kvm_update_cpuid_runtime(vcpu); return 0; diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index d0e06363c407..d503e15baf60 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -63,6 +63,7 @@ enum { KVMI_VCPU_EVENT_BREAKPOINT = KVMI_VCPU_EVENT_ID(2), KVMI_VCPU_EVENT_CR = KVMI_VCPU_EVENT_ID(3), KVMI_VCPU_EVENT_TRAP = KVMI_VCPU_EVENT_ID(4), + KVMI_VCPU_EVENT_XSETBV = KVMI_VCPU_EVENT_ID(5), KVMI_NEXT_VCPU_EVENT }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index b4565802db22..f73dbfe1407d 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -23,6 +23,8 @@ #define VCPU_ID 1 +#define X86_FEATURE_XSAVE (1<<26) + static int socket_pair[2]; #define Kvm_socket socket_pair[0] #define Userspace_socket socket_pair[1] @@ -57,6 +59,7 @@ enum { GUEST_TEST_BP, GUEST_TEST_CR, GUEST_TEST_HYPERCALL, + GUEST_TEST_XSETBV, }; #define GUEST_REQUEST_TEST() GUEST_SYNC(0) @@ -92,6 +95,45 @@ static void guest_hypercall_test(void) asm volatile(".byte 0x0f,0x01,0xc1"); } +/* from fpu/internal.h */ +static u64 xgetbv(u32 index) +{ + u32 eax, edx; + + asm volatile(".byte 0x0f,0x01,0xd0" /* xgetbv */ + : "=a" (eax), "=d" (edx) + : "c" (index)); + return eax + ((u64)edx << 32); +} + +/* from fpu/internal.h */ +static void xsetbv(u32 index, u64 value) +{ + u32 eax = value; + u32 edx = value >> 32; + + asm volatile(".byte 0x0f,0x01,0xd1" /* xsetbv */ + : : "a" (eax), "d" (edx), "c" (index)); +} + +static void guest_xsetbv_test(void) +{ + const int SSE_BIT = 1 << 1; + const int AVX_BIT = 1 << 2; + u64 xcr0; + + /* avoid #UD */ + set_cr4(get_cr4() | X86_CR4_OSXSAVE); + + xcr0 = xgetbv(0); + if (xcr0 & AVX_BIT) + xcr0 &= ~AVX_BIT; + else + xcr0 |= (AVX_BIT | SSE_BIT); + + xsetbv(0, xcr0); +} + static void guest_code(void) { while (true) { @@ -107,6 +149,9 @@ static void guest_code(void) case GUEST_TEST_HYPERCALL: guest_hypercall_test(); break; + case GUEST_TEST_XSETBV: + guest_xsetbv_test(); + break; } GUEST_SIGNAL_TEST_DONE(); } @@ -1333,6 +1378,44 @@ static void test_cmd_vm_get_max_gfn(void) pr_debug("max_gfn: 0x%llx\n", rpl.gfn); } +static void test_event_xsetbv(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = { + .vm = vm, + .vcpu_id = VCPU_ID, + .test_id = GUEST_TEST_XSETBV, + }; + __u16 event_id = KVMI_VCPU_EVENT_XSETBV; + struct kvm_cpuid_entry2 *entry; + struct vcpu_reply rpl = {}; + struct kvmi_msg_hdr hdr; + pthread_t vcpu_thread; + struct { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_xsetbv xsetbv; + } ev; + + entry = kvm_get_supported_cpuid_entry(1); + if (!(entry->ecx & X86_FEATURE_XSAVE)) { + print_skip("XSAVE not supported, ecx 0x%x", entry->ecx); + return; + } + + enable_vcpu_event(vm, event_id); + vcpu_thread = start_vcpu_worker(&data); + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), event_id); + + pr_debug("XSETBV event, XCR%u, old 0x%llx, new 0x%llx\n", + ev.xsetbv.xcr, ev.xsetbv.old_value, ev.xsetbv.new_value); + + reply_to_event(&hdr, &ev.vcpu_ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl, sizeof(rpl)); + + wait_vcpu_worker(vcpu_thread); + disable_vcpu_event(vm, event_id); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1359,6 +1442,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_control_cr(vm); test_cmd_vcpu_inject_exception(vm); test_cmd_vm_get_max_gfn(); + test_event_xsetbv(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:45 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930879 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2424AC64E7C for ; Wed, 25 Nov 2020 09:42:47 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D31DD2083E for ; Wed, 25 Nov 2020 09:42:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728742AbgKYJmq (ORCPT ); Wed, 25 Nov 2020 04:42:46 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57198 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728268AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 9774830462D0; Wed, 25 Nov 2020 11:35:53 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 795CA3072784; Wed, 25 Nov 2020 11:35:53 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 66/81] KVM: introspection: add KVMI_VCPU_GET_XCR Date: Wed, 25 Nov 2020 11:35:45 +0200 Message-Id: <20201125093600.2766-67-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This can be used by the introspection tool to emulate SSE instructions. Signed-off-by: Adalbert Lazăr Reported-by: kernel test robot --- Documentation/virt/kvm/kvmi.rst | 33 +++++++++++++++++++ arch/x86/include/uapi/asm/kvmi.h | 9 +++++ arch/x86/kvm/kvmi_msg.c | 21 ++++++++++++ include/uapi/linux/kvmi.h | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 33 +++++++++++++++++++ 5 files changed, 97 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 24dc1867c1f1..008c7c73a46f 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -797,6 +797,39 @@ Provides the maximum GFN allocated to the VM by walking through all memory slots. Stricly speaking, the returned value refers to the first inaccessible GFN, next to the maximum accessible GFN. +18. KVMI_VCPU_GET_XCR +--------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_get_xcr { + __u8 xcr; + __u8 padding[7]; + }; + +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_vcpu_get_xcr_reply { + u64 value; + }; + +Returns the value of an extended control register XCR. + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the specified control register is not XCR0 +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet + Events ====== diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 604a8b3d4ac2..5ca6190d85ec 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -102,4 +102,13 @@ struct kvmi_vcpu_event_xsetbv { __u64 new_value; }; +struct kvmi_vcpu_get_xcr { + __u8 xcr; + __u8 padding[7]; +}; + +struct kvmi_vcpu_get_xcr_reply { + u64 value; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index d0dc917118b5..596f607296b5 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -174,11 +174,32 @@ static int handle_vcpu_inject_exception(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); } +static int handle_vcpu_get_xcr(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vcpu_get_xcr *req = _req; + struct kvmi_vcpu_get_xcr_reply rpl; + int ec = 0; + + memset(&rpl, 0, sizeof(rpl)); + + if (non_zero_padding(req->padding, ARRAY_SIZE(req->padding))) + ec = -KVM_EINVAL; + else if (req->xcr != 0) + ec = -KVM_EINVAL; + else + rpl.value = job->vcpu->arch.xcr0; + + return kvmi_msg_vcpu_reply(job, msg, ec, &rpl, sizeof(rpl)); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, + [KVMI_VCPU_GET_XCR] = handle_vcpu_get_xcr, [KVMI_VCPU_INJECT_EXCEPTION] = handle_vcpu_inject_exception, [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, }; diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index d503e15baf60..07b6d383641a 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -44,6 +44,7 @@ enum { KVMI_VCPU_GET_CPUID = KVMI_VCPU_MESSAGE_ID(5), KVMI_VCPU_CONTROL_CR = KVMI_VCPU_MESSAGE_ID(6), KVMI_VCPU_INJECT_EXCEPTION = KVMI_VCPU_MESSAGE_ID(7), + KVMI_VCPU_GET_XCR = KVMI_VCPU_MESSAGE_ID(8), KVMI_NEXT_VCPU_MESSAGE }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index f73dbfe1407d..da90c6a8d535 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1416,6 +1416,38 @@ static void test_event_xsetbv(struct kvm_vm *vm) disable_vcpu_event(vm, event_id); } +static void cmd_vcpu_get_xcr(struct kvm_vm *vm, u8 xcr, u64 *value, + int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_get_xcr cmd; + } req = { 0 }; + struct kvmi_vcpu_get_xcr_reply rpl = { 0 }; + int r; + + req.cmd.xcr = xcr; + + r = do_vcpu0_command(vm, KVMI_VCPU_GET_XCR, &req.hdr, sizeof(req), + &rpl, sizeof(rpl)); + TEST_ASSERT(r == expected_err, + "KVMI_VCPU_GET_XCR failed, error %d (%s), expected %d\n", + -r, kvm_strerror(-r), expected_err); + + *value = r == 0 ? rpl.value : 0; +} + +static void test_cmd_vcpu_get_xcr(struct kvm_vm *vm) +{ + u8 xcr0 = 0, xcr1 = 1; + u64 value; + + cmd_vcpu_get_xcr(vm, xcr0, &value, 0); + pr_debug("XCR0 0x%lx\n", value); + cmd_vcpu_get_xcr(vm, xcr1, &value, -KVM_EINVAL); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1443,6 +1475,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_inject_exception(vm); test_cmd_vm_get_max_gfn(); test_event_xsetbv(vm); + test_cmd_vcpu_get_xcr(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:46 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930829 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 57475C83018 for ; Wed, 25 Nov 2020 09:42:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 146232083E for ; Wed, 25 Nov 2020 09:42:21 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728660AbgKYJmT (ORCPT ); Wed, 25 Nov 2020 04:42:19 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57144 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728582AbgKYJmH (ORCPT ); Wed, 25 Nov 2020 04:42:07 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id BF6DB30462D2; Wed, 25 Nov 2020 11:35:53 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 9CB353072785; Wed, 25 Nov 2020 11:35:53 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 67/81] KVM: introspection: add KVMI_VCPU_GET_XSAVE Date: Wed, 25 Nov 2020 11:35:46 +0200 Message-Id: <20201125093600.2766-68-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This vCPU command is used to get the XSAVE area. Signed-off-by: Mihai Donțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 29 +++++++++++++++++++ arch/x86/include/uapi/asm/kvmi.h | 4 +++ arch/x86/kvm/kvmi_msg.c | 20 +++++++++++++ include/uapi/linux/kvmi.h | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 26 +++++++++++++++++ 5 files changed, 80 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 008c7c73a46f..c1ac47def4e9 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -830,6 +830,35 @@ Returns the value of an extended control register XCR. * -KVM_EINVAL - the padding is not zero * -KVM_EAGAIN - the selected vCPU can't be introspected yet +19. KVMI_VCPU_GET_XSAVE +----------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_vcpu_get_xsave_reply { + struct kvm_xsave xsave; + }; + +Returns a buffer containing the XSAVE area. + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_ENOMEM - there is not enough memory to allocate the reply + Events ====== diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 5ca6190d85ec..0d3696c52d88 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -111,4 +111,8 @@ struct kvmi_vcpu_get_xcr_reply { u64 value; }; +struct kvmi_vcpu_get_xsave_reply { + struct kvm_xsave xsave; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 596f607296b5..77c753cd9705 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -194,12 +194,32 @@ static int handle_vcpu_get_xcr(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, ec, &rpl, sizeof(rpl)); } +static int handle_vcpu_get_xsave(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *req) +{ + struct kvmi_vcpu_get_xsave_reply *rpl; + int err, ec = 0; + + rpl = kvmi_msg_alloc(); + if (!rpl) + ec = -KVM_ENOMEM; + else + kvm_vcpu_ioctl_x86_get_xsave(job->vcpu, &rpl->xsave); + + err = kvmi_msg_vcpu_reply(job, msg, 0, rpl, sizeof(*rpl)); + + kvmi_msg_free(rpl); + return err; +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, [KVMI_VCPU_GET_XCR] = handle_vcpu_get_xcr, + [KVMI_VCPU_GET_XSAVE] = handle_vcpu_get_xsave, [KVMI_VCPU_INJECT_EXCEPTION] = handle_vcpu_inject_exception, [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, }; diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 07b6d383641a..e47c4ce0f8ed 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -45,6 +45,7 @@ enum { KVMI_VCPU_CONTROL_CR = KVMI_VCPU_MESSAGE_ID(6), KVMI_VCPU_INJECT_EXCEPTION = KVMI_VCPU_MESSAGE_ID(7), KVMI_VCPU_GET_XCR = KVMI_VCPU_MESSAGE_ID(8), + KVMI_VCPU_GET_XSAVE = KVMI_VCPU_MESSAGE_ID(9), KVMI_NEXT_VCPU_MESSAGE }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index da90c6a8d535..277b1061410b 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1448,6 +1448,31 @@ static void test_cmd_vcpu_get_xcr(struct kvm_vm *vm) cmd_vcpu_get_xcr(vm, xcr1, &value, -KVM_EINVAL); } +static void cmd_vcpu_get_xsave(struct kvm_vm *vm) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + } req = {}; + struct kvm_xsave rpl; + + test_vcpu0_command(vm, KVMI_VCPU_GET_XSAVE, &req.hdr, sizeof(req), + &rpl, sizeof(rpl), 0); +} + +static void test_cmd_vcpu_get_xsave(struct kvm_vm *vm) +{ + struct kvm_cpuid_entry2 *entry; + + entry = kvm_get_supported_cpuid_entry(1); + if (!(entry->ecx & X86_FEATURE_XSAVE)) { + print_skip("XSAVE not supported, ecx 0x%x", entry->ecx); + return; + } + + cmd_vcpu_get_xsave(vm); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1476,6 +1501,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vm_get_max_gfn(); test_event_xsetbv(vm); test_cmd_vcpu_get_xcr(vm); + test_cmd_vcpu_get_xsave(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:47 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930935 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1A9BFC83010 for ; Wed, 25 Nov 2020 09:43:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D94802083E for ; Wed, 25 Nov 2020 09:43:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728826AbgKYJnG (ORCPT ); Wed, 25 Nov 2020 04:43:06 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57136 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727338AbgKYJl4 (ORCPT ); Wed, 25 Nov 2020 04:41:56 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E0AAF30462CE; Wed, 25 Nov 2020 11:35:53 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id C2C243072784; Wed, 25 Nov 2020 11:35:53 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 68/81] KVM: introspection: add KVMI_VCPU_SET_XSAVE Date: Wed, 25 Nov 2020 11:35:47 +0200 Message-Id: <20201125093600.2766-69-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This can be used by the introspection tool to emulate SSE instructions. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 28 +++++++++++++++++++ arch/x86/include/uapi/asm/kvmi.h | 4 +++ arch/x86/kvm/kvmi_msg.c | 21 ++++++++++++++ include/uapi/linux/kvmi.h | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 27 ++++++++++++++---- 5 files changed, 75 insertions(+), 6 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index c1ac47def4e9..56efeeb38980 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -859,6 +859,34 @@ Returns a buffer containing the XSAVE area. * -KVM_EAGAIN - the selected vCPU can't be introspected yet * -KVM_ENOMEM - there is not enough memory to allocate the reply +20. KVMI_VCPU_SET_XSAVE +----------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_set_xsave { + struct kvm_xsave xsave; + }; + +:Returns: + +:: + + struct kvmi_error_code; + +Modifies the XSAVE area. + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet + Events ====== diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 0d3696c52d88..6ec290b69b46 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -115,4 +115,8 @@ struct kvmi_vcpu_get_xsave_reply { struct kvm_xsave xsave; }; +struct kvmi_vcpu_set_xsave { + struct kvm_xsave xsave; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 77c753cd9705..c1b3bd56a42c 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -213,6 +213,26 @@ static int handle_vcpu_get_xsave(const struct kvmi_vcpu_msg_job *job, return err; } +static int handle_vcpu_set_xsave(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *req) +{ + size_t req_size, msg_size = msg->size; + int ec = 0; + + if (check_sub_overflow(msg_size, sizeof(struct kvmi_vcpu_hdr), + &req_size)) + return -EINVAL; + + if (req_size < sizeof(struct kvm_xsave)) + ec = -KVM_EINVAL; + else if (kvm_vcpu_ioctl_x86_set_xsave(job->vcpu, + (struct kvm_xsave *) req)) + ec = -KVM_EINVAL; + + return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, @@ -222,6 +242,7 @@ static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { [KVMI_VCPU_GET_XSAVE] = handle_vcpu_get_xsave, [KVMI_VCPU_INJECT_EXCEPTION] = handle_vcpu_inject_exception, [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, + [KVMI_VCPU_SET_XSAVE] = handle_vcpu_set_xsave, }; kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index e47c4ce0f8ed..3baf5c7842bb 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -46,6 +46,7 @@ enum { KVMI_VCPU_INJECT_EXCEPTION = KVMI_VCPU_MESSAGE_ID(7), KVMI_VCPU_GET_XCR = KVMI_VCPU_MESSAGE_ID(8), KVMI_VCPU_GET_XSAVE = KVMI_VCPU_MESSAGE_ID(9), + KVMI_VCPU_SET_XSAVE = KVMI_VCPU_MESSAGE_ID(10), KVMI_NEXT_VCPU_MESSAGE }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 277b1061410b..45c1f3132a3c 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1448,21 +1448,35 @@ static void test_cmd_vcpu_get_xcr(struct kvm_vm *vm) cmd_vcpu_get_xcr(vm, xcr1, &value, -KVM_EINVAL); } -static void cmd_vcpu_get_xsave(struct kvm_vm *vm) +static void cmd_vcpu_get_xsave(struct kvm_vm *vm, struct kvm_xsave *rpl) { struct { struct kvmi_msg_hdr hdr; struct kvmi_vcpu_hdr vcpu_hdr; } req = {}; - struct kvm_xsave rpl; test_vcpu0_command(vm, KVMI_VCPU_GET_XSAVE, &req.hdr, sizeof(req), - &rpl, sizeof(rpl), 0); + rpl, sizeof(*rpl), 0); } -static void test_cmd_vcpu_get_xsave(struct kvm_vm *vm) +static void cmd_vcpu_set_xsave(struct kvm_vm *vm, struct kvm_xsave *rpl) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvm_xsave xsave; + } req = {}; + + memcpy(&req.xsave, rpl, sizeof(*rpl)); + + test_vcpu0_command(vm, KVMI_VCPU_SET_XSAVE, &req.hdr, sizeof(req), + NULL, 0, 0); +} + +static void test_cmd_vcpu_xsave(struct kvm_vm *vm) { struct kvm_cpuid_entry2 *entry; + struct kvm_xsave xsave; entry = kvm_get_supported_cpuid_entry(1); if (!(entry->ecx & X86_FEATURE_XSAVE)) { @@ -1470,7 +1484,8 @@ static void test_cmd_vcpu_get_xsave(struct kvm_vm *vm) return; } - cmd_vcpu_get_xsave(vm); + cmd_vcpu_get_xsave(vm, &xsave); + cmd_vcpu_set_xsave(vm, &xsave); } static void test_introspection(struct kvm_vm *vm) @@ -1501,7 +1516,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vm_get_max_gfn(); test_event_xsetbv(vm); test_cmd_vcpu_get_xcr(vm); - test_cmd_vcpu_get_xsave(vm); + test_cmd_vcpu_xsave(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:48 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930857 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4B2C1C64E90 for ; Wed, 25 Nov 2020 09:42:33 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 013A02083E for ; Wed, 25 Nov 2020 09:42:32 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728730AbgKYJmc (ORCPT ); Wed, 25 Nov 2020 04:42:32 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57188 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728532AbgKYJmF (ORCPT ); Wed, 25 Nov 2020 04:42:05 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 1396230462CF; Wed, 25 Nov 2020 11:35:54 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id E45EE3072785; Wed, 25 Nov 2020 11:35:53 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?b?TmljdciZ?= =?utf-8?b?b3IgQ8OuyJt1?= , =?utf-8?q?Adalbert_Laz?= =?utf-8?q?=C4=83r?= Subject: [PATCH v10 69/81] KVM: introspection: add KVMI_VCPU_GET_MTRR_TYPE Date: Wed, 25 Nov 2020 11:35:48 +0200 Message-Id: <20201125093600.2766-70-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This command returns the memory type for a guest physical address. Signed-off-by: Mihai Donțu Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 32 +++++++++++++++++++ arch/x86/include/uapi/asm/kvmi.h | 9 ++++++ arch/x86/kvm/kvmi_msg.c | 17 ++++++++++ include/uapi/linux/kvmi.h | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 18 +++++++++++ 5 files changed, 77 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 56efeeb38980..58b50464b5f6 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -887,6 +887,38 @@ Modifies the XSAVE area. * -KVM_EINVAL - the padding is not zero * -KVM_EAGAIN - the selected vCPU can't be introspected yet +21. KVMI_VCPU_GET_MTRR_TYPE +--------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_get_mtrr_type { + __u64 gpa; + }; + +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_vcpu_get_mtrr_type_reply { + __u8 type; + __u8 padding[7]; + }; + +Returns the guest memory type for a specific guest physical address (``gpa``). + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet + Events ====== diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 6ec290b69b46..998878215078 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -119,4 +119,13 @@ struct kvmi_vcpu_set_xsave { struct kvm_xsave xsave; }; +struct kvmi_vcpu_get_mtrr_type { + __u64 gpa; +}; + +struct kvmi_vcpu_get_mtrr_type_reply { + __u8 type; + __u8 padding[7]; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index c1b3bd56a42c..fc4ee6acce4a 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -233,10 +233,27 @@ static int handle_vcpu_set_xsave(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); } +static int handle_vcpu_get_mtrr_type(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vcpu_get_mtrr_type *req = _req; + struct kvmi_vcpu_get_mtrr_type_reply rpl; + gfn_t gfn; + + gfn = gpa_to_gfn(req->gpa); + + memset(&rpl, 0, sizeof(rpl)); + rpl.type = kvm_mtrr_get_guest_memory_type(job->vcpu, gfn); + + return kvmi_msg_vcpu_reply(job, msg, 0, &rpl, sizeof(rpl)); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, + [KVMI_VCPU_GET_MTRR_TYPE] = handle_vcpu_get_mtrr_type, [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, [KVMI_VCPU_GET_XCR] = handle_vcpu_get_xcr, [KVMI_VCPU_GET_XSAVE] = handle_vcpu_get_xsave, diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 3baf5c7842bb..8d7c6027f12c 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -47,6 +47,7 @@ enum { KVMI_VCPU_GET_XCR = KVMI_VCPU_MESSAGE_ID(8), KVMI_VCPU_GET_XSAVE = KVMI_VCPU_MESSAGE_ID(9), KVMI_VCPU_SET_XSAVE = KVMI_VCPU_MESSAGE_ID(10), + KVMI_VCPU_GET_MTRR_TYPE = KVMI_VCPU_MESSAGE_ID(11), KVMI_NEXT_VCPU_MESSAGE }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 45c1f3132a3c..b0906c7fb954 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1488,6 +1488,23 @@ static void test_cmd_vcpu_xsave(struct kvm_vm *vm) cmd_vcpu_set_xsave(vm, &xsave); } +static void test_cmd_vcpu_get_mtrr_type(struct kvm_vm *vm) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_get_mtrr_type cmd; + } req = {}; + struct kvmi_vcpu_get_mtrr_type_reply rpl; + + req.cmd.gpa = test_gpa; + + test_vcpu0_command(vm, KVMI_VCPU_GET_MTRR_TYPE, + &req.hdr, sizeof(req), &rpl, sizeof(rpl), 0); + + pr_debug("mtrr_type: gpa 0x%lx type 0x%x\n", test_gpa, rpl.type); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1517,6 +1534,7 @@ static void test_introspection(struct kvm_vm *vm) test_event_xsetbv(vm); test_cmd_vcpu_get_xcr(vm); test_cmd_vcpu_xsave(vm); + test_cmd_vcpu_get_mtrr_type(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:49 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930853 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 405BDC83011 for ; Wed, 25 Nov 2020 09:42:16 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id EF9EC2083E for ; Wed, 25 Nov 2020 09:42:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728641AbgKYJmP (ORCPT ); Wed, 25 Nov 2020 04:42:15 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57138 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728604AbgKYJmK (ORCPT ); Wed, 25 Nov 2020 04:42:10 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 4127230462D4; Wed, 25 Nov 2020 11:35:54 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 1B1CE3072784; Wed, 25 Nov 2020 11:35:54 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 70/81] KVM: introspection: add KVMI_VCPU_EVENT_DESCRIPTOR Date: Wed, 25 Nov 2020 11:35:49 +0200 Message-Id: <20201125093600.2766-71-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This event is sent when IDTR, GDTR, LDTR or TR are accessed. These could be used to implement a tiny agent which runs in the context of an introspected guest and uses virtualized exceptions (#VE) and alternate EPT views (VMFUNC #0) to filter converted VMEXITS. The events of interested will be suppressed (after some appropriate guest-side handling) while the rest will be sent to the introspector via a VMCALL. Signed-off-by: Nicușor Cîțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 43 +++++++++++ arch/x86/include/asm/kvmi_host.h | 3 + arch/x86/include/uapi/asm/kvmi.h | 13 ++++ arch/x86/kvm/kvmi.c | 58 ++++++++++++++ arch/x86/kvm/kvmi.h | 1 + arch/x86/kvm/kvmi_msg.c | 19 +++++ arch/x86/kvm/svm/svm.c | 33 ++++++++ arch/x86/kvm/vmx/vmx.c | 23 ++++++ include/uapi/linux/kvmi.h | 1 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 75 +++++++++++++++++++ 10 files changed, 269 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 58b50464b5f6..649a679a485b 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -540,6 +540,7 @@ the following events:: KVMI_VCPU_EVENT_BREAKPOINT KVMI_VCPU_EVENT_CR + KVMI_VCPU_EVENT_DESCRIPTOR KVMI_VCPU_EVENT_HYPERCALL KVMI_VCPU_EVENT_XSETBV @@ -563,6 +564,8 @@ the *KVMI_VM_CONTROL_EVENTS* command. * -KVM_EINVAL - the event ID is unknown (use *KVMI_VM_CHECK_EVENT* first) * -KVM_EPERM - the access is disallowed (use *KVMI_VM_CHECK_EVENT* first) * -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_EOPNOTSUPP - the event can't be intercepted in the current setup + (e.g. KVMI_VCPU_EVENT_DESCRIPTOR with AMD) * -KVM_EBUSY - the event can't be intercepted right now (e.g. KVMI_VCPU_EVENT_BREAKPOINT if the #BP event is already intercepted by userspace) @@ -1217,3 +1220,43 @@ to be changed and the introspection has been enabled for this event ``kvmi_vcpu_event`` (with the vCPU state), the extended control register number (``xcr``), the old value (``old_value``) and the new value (``new_value``) are sent to the introspection tool. + +8. KVMI_VCPU_EVENT_DESCRIPTOR +----------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Actions: CONTINUE, RETRY, CRASH +:Parameters: + +:: + + struct kvmi_vcpu_event; + struct kvmi_vcpu_event_descriptor { + __u8 descriptor; + __u8 write; + __u8 padding[6]; + }; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + +This event is sent when a descriptor table register is accessed and the +introspection has been enabled for this event (see **KVMI_VCPU_CONTROL_EVENTS**). + +``kvmi_vcpu_event`` (with the vCPU state), the descriptor-table register +(``descriptor``) and the access type (``write``) are sent to the +introspection tool. + +``descriptor`` can be one of:: + + KVMI_DESC_IDTR + KVMI_DESC_GDTR + KVMI_DESC_LDTR + KVMI_DESC_TR + +``write`` is 1 if the descriptor was written, 0 otherwise. diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index d66349208a6b..a24ba87036f7 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -48,6 +48,7 @@ bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable); void kvmi_enter_guest(struct kvm_vcpu *vcpu); void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, u64 old_value, u64 new_value); +bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write); #else /* CONFIG_KVM_INTROSPECTION */ @@ -63,6 +64,8 @@ static inline bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, static inline void kvmi_enter_guest(struct kvm_vcpu *vcpu) { } static inline void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, u64 old_value, u64 new_value) { } +static inline bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, + bool write) { return true; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 998878215078..db01c56a95ff 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -128,4 +128,17 @@ struct kvmi_vcpu_get_mtrr_type_reply { __u8 padding[7]; }; +enum { + KVMI_DESC_IDTR = 1, + KVMI_DESC_GDTR = 2, + KVMI_DESC_LDTR = 3, + KVMI_DESC_TR = 4, +}; + +struct kvmi_vcpu_event_descriptor { + __u8 descriptor; + __u8 write; + __u8 padding[6]; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 5219b6faf4b5..3d5b041de634 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -15,6 +15,7 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) set_bit(KVMI_VCPU_EVENT_BREAKPOINT, supported); set_bit(KVMI_VCPU_EVENT_CR, supported); set_bit(KVMI_VCPU_EVENT_HYPERCALL, supported); + set_bit(KVMI_VCPU_EVENT_DESCRIPTOR, supported); set_bit(KVMI_VCPU_EVENT_TRAP, supported); set_bit(KVMI_VCPU_EVENT_XSETBV, supported); } @@ -285,6 +286,21 @@ static void kvmi_arch_disable_cr3w_intercept(struct kvm_vcpu *vcpu) vcpu->arch.kvmi->cr3w.kvm_intercepted = false; } +static int kvmi_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) +{ + if (!kvm_x86_ops.desc_ctrl_supported()) + return -KVM_EOPNOTSUPP; + + kvm_x86_ops.control_desc_intercept(vcpu, enable); + + return 0; +} + +static void kvmi_arch_disable_desc_intercept(struct kvm_vcpu *vcpu) +{ + kvmi_control_desc_intercept(vcpu, false); +} + int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, unsigned int event_id, bool enable) { @@ -294,6 +310,9 @@ int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, case KVMI_VCPU_EVENT_BREAKPOINT: err = kvmi_control_bp_intercept(vcpu, enable); break; + case KVMI_VCPU_EVENT_DESCRIPTOR: + err = kvmi_control_desc_intercept(vcpu, enable); + break; default: break; } @@ -325,6 +344,7 @@ static void kvmi_arch_restore_interception(struct kvm_vcpu *vcpu) { kvmi_arch_disable_bp_intercept(vcpu); kvmi_arch_disable_cr3w_intercept(vcpu); + kvmi_arch_disable_desc_intercept(vcpu); } bool kvmi_arch_clean_up_interception(struct kvm_vcpu *vcpu) @@ -597,3 +617,41 @@ void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, kvmi_put(vcpu->kvm); } + +static bool __kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, + bool write) +{ + bool ret = false; + u32 action; + + action = kvmi_msg_send_vcpu_descriptor(vcpu, descriptor, write); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + ret = true; + break; + case KVMI_EVENT_ACTION_RETRY: + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + } + + return ret; +} + +bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write) +{ + struct kvm_introspection *kvmi; + bool ret = true; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return true; + + if (is_vcpu_event_enabled(vcpu, KVMI_VCPU_EVENT_DESCRIPTOR)) + ret = __kvmi_descriptor_event(vcpu, descriptor, write); + + kvmi_put(vcpu->kvm); + + return ret; +} +EXPORT_SYMBOL(kvmi_descriptor_event); diff --git a/arch/x86/kvm/kvmi.h b/arch/x86/kvm/kvmi.h index 43bc956d740c..92422e2e57cf 100644 --- a/arch/x86/kvm/kvmi.h +++ b/arch/x86/kvm/kvmi.h @@ -16,5 +16,6 @@ u32 kvmi_msg_send_vcpu_cr(struct kvm_vcpu *vcpu, u32 cr, u64 old_value, u32 kvmi_msg_send_vcpu_trap(struct kvm_vcpu *vcpu); u32 kvmi_msg_send_vcpu_xsetbv(struct kvm_vcpu *vcpu, u8 xcr, u64 old_value, u64 new_value); +u32 kvmi_msg_send_vcpu_descriptor(struct kvm_vcpu *vcpu, u8 desc, bool write); #endif diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index fc4ee6acce4a..7a851b4d5983 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -331,3 +331,22 @@ u32 kvmi_msg_send_vcpu_xsetbv(struct kvm_vcpu *vcpu, u8 xcr, return action; } + +u32 kvmi_msg_send_vcpu_descriptor(struct kvm_vcpu *vcpu, u8 desc, bool write) +{ + struct kvmi_vcpu_event_descriptor e; + u32 action; + int err; + + memset(&e, 0, sizeof(e)); + e.descriptor = desc; + e.write = write ? 1 : 0; + + err = kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_DESCRIPTOR, + &e, sizeof(e), NULL, 0, &action); + if (err) + action = KVMI_EVENT_ACTION_CONTINUE; + + return action; + +} diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index f3ec79e6c39a..5b689d3fe3e4 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -2449,6 +2449,39 @@ static int descriptor_access_interception(struct vcpu_svm *svm) { struct kvm_vcpu *vcpu = &svm->vcpu; +#ifdef CONFIG_KVM_INTROSPECTION + struct vmcb_control_area *c = &svm->vmcb->control; + bool cont; + + switch (c->exit_code) { + case SVM_EXIT_IDTR_READ: + case SVM_EXIT_IDTR_WRITE: + cont = kvmi_descriptor_event(vcpu, KVMI_DESC_IDTR, + c->exit_code == SVM_EXIT_IDTR_WRITE); + break; + case SVM_EXIT_GDTR_READ: + case SVM_EXIT_GDTR_WRITE: + cont = kvmi_descriptor_event(vcpu, KVMI_DESC_GDTR, + c->exit_code == SVM_EXIT_GDTR_WRITE); + break; + case SVM_EXIT_LDTR_READ: + case SVM_EXIT_LDTR_WRITE: + cont = kvmi_descriptor_event(vcpu, KVMI_DESC_LDTR, + c->exit_code == SVM_EXIT_LDTR_WRITE); + break; + case SVM_EXIT_TR_READ: + case SVM_EXIT_TR_WRITE: + cont = kvmi_descriptor_event(vcpu, KVMI_DESC_TR, + c->exit_code == SVM_EXIT_TR_WRITE); + break; + default: + cont = true; + break; + } + if (!cont) + return 1; +#endif /* CONFIG_KVM_INTROSPECTION */ + return kvm_emulate_instruction(vcpu, 0); } diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 837447541710..3afdf532b030 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5043,7 +5043,30 @@ static int handle_set_cr4(struct kvm_vcpu *vcpu, unsigned long val) static int handle_desc(struct kvm_vcpu *vcpu) { +#ifdef CONFIG_KVM_INTROSPECTION + struct vcpu_vmx *vmx = to_vmx(vcpu); + u32 exit_reason = vmx->exit_reason; + u32 vmx_instruction_info = vmcs_read32(VMX_INSTRUCTION_INFO); + u8 store = (vmx_instruction_info >> 29) & 0x1; + u8 descriptor = 0; + + if (exit_reason == EXIT_REASON_GDTR_IDTR) { + if ((vmx_instruction_info >> 28) & 0x1) + descriptor = KVMI_DESC_IDTR; + else + descriptor = KVMI_DESC_GDTR; + } else { + if ((vmx_instruction_info >> 28) & 0x1) + descriptor = KVMI_DESC_TR; + else + descriptor = KVMI_DESC_LDTR; + } + + if (!kvmi_descriptor_event(vcpu, descriptor, store)) + return 1; +#else WARN_ON(!(vcpu->arch.cr4 & X86_CR4_UMIP)); +#endif /* CONFIG_KVM_INTROSPECTION */ return kvm_emulate_instruction(vcpu, 0); } diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 8d7c6027f12c..09f78c0efc4f 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -68,6 +68,7 @@ enum { KVMI_VCPU_EVENT_CR = KVMI_VCPU_EVENT_ID(3), KVMI_VCPU_EVENT_TRAP = KVMI_VCPU_EVENT_ID(4), KVMI_VCPU_EVENT_XSETBV = KVMI_VCPU_EVENT_ID(5), + KVMI_VCPU_EVENT_DESCRIPTOR = KVMI_VCPU_EVENT_ID(6), KVMI_NEXT_VCPU_EVENT }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index b0906c7fb954..d26df3a9ffff 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -58,6 +58,7 @@ enum { GUEST_TEST_NOOP = 0, GUEST_TEST_BP, GUEST_TEST_CR, + GUEST_TEST_DESCRIPTOR, GUEST_TEST_HYPERCALL, GUEST_TEST_XSETBV, }; @@ -87,6 +88,14 @@ static void guest_cr_test(void) set_cr4(get_cr4() | X86_CR4_OSXSAVE); } +static void guest_descriptor_test(void) +{ + void *ptr; + + asm volatile("sgdt %0" :: "m"(ptr)); + asm volatile("lgdt %0" :: "m"(ptr)); +} + static void guest_hypercall_test(void) { asm volatile("mov $34, %rax"); @@ -146,6 +155,9 @@ static void guest_code(void) case GUEST_TEST_CR: guest_cr_test(); break; + case GUEST_TEST_DESCRIPTOR: + guest_descriptor_test(); + break; case GUEST_TEST_HYPERCALL: guest_hypercall_test(); break; @@ -1505,6 +1517,68 @@ static void test_cmd_vcpu_get_mtrr_type(struct kvm_vm *vm) pr_debug("mtrr_type: gpa 0x%lx type 0x%x\n", test_gpa, rpl.type); } +static void test_desc_read_access(__u16 event_id) +{ + struct kvmi_msg_hdr hdr; + struct { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_descriptor desc; + } ev; + struct vcpu_reply rpl = {}; + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), event_id); + + pr_info("Descriptor event (read), descriptor %u, write %u\n", + ev.desc.descriptor, ev.desc.write); + + TEST_ASSERT(ev.desc.write == 0, + "Received a write descriptor access\n"); + + reply_to_event(&hdr, &ev.vcpu_ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl, sizeof(rpl)); +} + +static void test_desc_write_access(__u16 event_id) +{ + struct kvmi_msg_hdr hdr; + struct { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_descriptor desc; + } ev; + struct vcpu_reply rpl = {}; + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), event_id); + + pr_debug("Descriptor event (write), descriptor %u, write %u\n", + ev.desc.descriptor, ev.desc.write); + + TEST_ASSERT(ev.desc.write == 1, + "Received a read descriptor access\n"); + + reply_to_event(&hdr, &ev.vcpu_ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl, sizeof(rpl)); +} + +static void test_event_descriptor(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = { + .vm = vm, + .vcpu_id = VCPU_ID, + .test_id = GUEST_TEST_DESCRIPTOR, + }; + __u16 event_id = KVMI_VCPU_EVENT_DESCRIPTOR; + pthread_t vcpu_thread; + + enable_vcpu_event(vm, event_id); + vcpu_thread = start_vcpu_worker(&data); + + test_desc_read_access(event_id); + test_desc_write_access(event_id); + + wait_vcpu_worker(vcpu_thread); + disable_vcpu_event(vm, event_id); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1535,6 +1609,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_get_xcr(vm); test_cmd_vcpu_xsave(vm); test_cmd_vcpu_get_mtrr_type(vm); + test_event_descriptor(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:50 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930907 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4E306C64E7D for ; Wed, 25 Nov 2020 09:42:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 04C4021527 for ; Wed, 25 Nov 2020 09:42:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728777AbgKYJmw (ORCPT ); Wed, 25 Nov 2020 04:42:52 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57312 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728161AbgKYJmA (ORCPT ); Wed, 25 Nov 2020 04:42:00 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 61FC430462D5; Wed, 25 Nov 2020 11:35:54 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 42E7E3072785; Wed, 25 Nov 2020 11:35:54 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 71/81] KVM: introspection: restore the state of descriptor-table register interception on unhook Date: Wed, 25 Nov 2020 11:35:50 +0200 Message-Id: <20201125093600.2766-72-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This commit also ensures that the introspection tool and the userspace do not disable each other the descriptor-table access VM-exit. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvmi_host.h | 4 +++ arch/x86/kvm/kvmi.c | 45 ++++++++++++++++++++++++++++++++ arch/x86/kvm/svm/svm.c | 3 +++ arch/x86/kvm/vmx/vmx.c | 3 +++ 4 files changed, 55 insertions(+) diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index a24ba87036f7..a872277eba67 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -17,6 +17,7 @@ struct kvmi_interception { bool restore_interception; struct kvmi_monitor_interception breakpoint; struct kvmi_monitor_interception cr3w; + struct kvmi_monitor_interception descriptor; }; struct kvm_vcpu_arch_introspection { @@ -48,6 +49,7 @@ bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, bool enable); void kvmi_enter_guest(struct kvm_vcpu *vcpu); void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, u64 old_value, u64 new_value); +bool kvmi_monitor_desc_intercept(struct kvm_vcpu *vcpu, bool enable); bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write); #else /* CONFIG_KVM_INTROSPECTION */ @@ -64,6 +66,8 @@ static inline bool kvmi_monitor_cr3w_intercept(struct kvm_vcpu *vcpu, static inline void kvmi_enter_guest(struct kvm_vcpu *vcpu) { } static inline void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, u64 old_value, u64 new_value) { } +static inline bool kvmi_monitor_desc_intercept(struct kvm_vcpu *vcpu, + bool enable) { return false; } static inline bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write) { return true; } diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 3d5b041de634..4106ae63a115 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -286,12 +286,52 @@ static void kvmi_arch_disable_cr3w_intercept(struct kvm_vcpu *vcpu) vcpu->arch.kvmi->cr3w.kvm_intercepted = false; } +/* + * Returns true if one side (kvm or kvmi) tries to disable the descriptor + * interception while the other side is still tracking it. + */ +bool kvmi_monitor_desc_intercept(struct kvm_vcpu *vcpu, bool enable) +{ + struct kvmi_interception *arch_vcpui = READ_ONCE(vcpu->arch.kvmi); + + return (arch_vcpui && arch_vcpui->descriptor.monitor_fct(vcpu, enable)); +} +EXPORT_SYMBOL(kvmi_monitor_desc_intercept); + +static bool monitor_desc_fct_kvmi(struct kvm_vcpu *vcpu, bool enable) +{ + vcpu->arch.kvmi->descriptor.kvmi_intercepted = enable; + + if (enable) + vcpu->arch.kvmi->descriptor.kvm_intercepted = + kvm_x86_ops.desc_intercepted(vcpu); + else if (vcpu->arch.kvmi->descriptor.kvm_intercepted) + return true; + + return false; +} + +static bool monitor_desc_fct_kvm(struct kvm_vcpu *vcpu, bool enable) +{ + if (!vcpu->arch.kvmi->descriptor.kvmi_intercepted) + return false; + + vcpu->arch.kvmi->descriptor.kvm_intercepted = enable; + + if (!enable) + return true; + + return false; +} + static int kvmi_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) { if (!kvm_x86_ops.desc_ctrl_supported()) return -KVM_EOPNOTSUPP; + vcpu->arch.kvmi->descriptor.monitor_fct = monitor_desc_fct_kvmi; kvm_x86_ops.control_desc_intercept(vcpu, enable); + vcpu->arch.kvmi->descriptor.monitor_fct = monitor_desc_fct_kvm; return 0; } @@ -299,6 +339,9 @@ static int kvmi_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) static void kvmi_arch_disable_desc_intercept(struct kvm_vcpu *vcpu) { kvmi_control_desc_intercept(vcpu, false); + + vcpu->arch.kvmi->descriptor.kvmi_intercepted = false; + vcpu->arch.kvmi->descriptor.kvm_intercepted = false; } int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, @@ -370,11 +413,13 @@ bool kvmi_arch_vcpu_alloc_interception(struct kvm_vcpu *vcpu) arch_vcpui->breakpoint.monitor_fct = monitor_bp_fct_kvm; arch_vcpui->cr3w.monitor_fct = monitor_cr3w_fct_kvm; + arch_vcpui->descriptor.monitor_fct = monitor_desc_fct_kvm; /* * paired with: * - kvmi_monitor_bp_intercept() * - kvmi_monitor_cr3w_intercept() + * - kvmi_monitor_desc_intercept() */ smp_wmb(); WRITE_ONCE(vcpu->arch.kvmi, arch_vcpui); diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 5b689d3fe3e4..834e4b6c4112 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -1670,6 +1670,9 @@ static void svm_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) { struct vcpu_svm *svm = to_svm(vcpu); + if (kvmi_monitor_desc_intercept(vcpu, enable)) + return; + if (enable) { svm_set_intercept(svm, INTERCEPT_STORE_IDTR); svm_set_intercept(svm, INTERCEPT_STORE_GDTR); diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 3afdf532b030..245332ce91a5 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -3130,6 +3130,9 @@ static void vmx_control_desc_intercept(struct kvm_vcpu *vcpu, bool enable) { struct vcpu_vmx *vmx = to_vmx(vcpu); + if (kvmi_monitor_desc_intercept(vcpu, enable)) + return; + if (enable) secondary_exec_controls_setbit(vmx, SECONDARY_EXEC_DESC); else From patchwork Wed Nov 25 09:35:51 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930889 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4661DC83011 for ; Wed, 25 Nov 2020 09:42:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F1EA52083E for ; Wed, 25 Nov 2020 09:42:48 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728721AbgKYJmp (ORCPT ); Wed, 25 Nov 2020 04:42:45 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57142 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728192AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 968B6305D3E4; Wed, 25 Nov 2020 11:35:54 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 681043072784; Wed, 25 Nov 2020 11:35:54 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?b?TmljdciZ?= =?utf-8?b?b3IgQ8OuyJt1?= , =?utf-8?q?Adalbert_Laz?= =?utf-8?q?=C4=83r?= Subject: [PATCH v10 72/81] KVM: introspection: add KVMI_VCPU_CONTROL_MSR and KVMI_VCPU_EVENT_MSR Date: Wed, 25 Nov 2020 11:35:51 +0200 Message-Id: <20201125093600.2766-73-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This command is used to enable/disable introspection for a specific MSR. The KVMI_VCPU_EVENT_MSR event is sent when the tracked MSR is going to be changed. The introspection tool can respond by allowing the guest to continue with normal execution or by discarding the change. This is meant to prevent malicious changes to MSRs such as MSR_IA32_SYSENTER_EIP. Signed-off-by: Mihai Donțu Co-developed-by: Nicușor Cîțu Signed-off-by: Nicușor Cîțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 79 +++++++++++ arch/x86/include/asm/kvmi_host.h | 12 ++ arch/x86/include/uapi/asm/kvmi.h | 18 +++ arch/x86/kvm/kvmi.c | 125 ++++++++++++++++++ arch/x86/kvm/kvmi.h | 3 + arch/x86/kvm/kvmi_msg.c | 52 ++++++++ arch/x86/kvm/x86.c | 3 + include/uapi/linux/kvmi.h | 2 + .../testing/selftests/kvm/x86_64/kvmi_test.c | 111 ++++++++++++++++ 9 files changed, 405 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 649a679a485b..3466db72e5e8 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -542,6 +542,7 @@ the following events:: KVMI_VCPU_EVENT_CR KVMI_VCPU_EVENT_DESCRIPTOR KVMI_VCPU_EVENT_HYPERCALL + KVMI_VCPU_EVENT_MSR KVMI_VCPU_EVENT_XSETBV When an event is enabled, the introspection tool is notified and @@ -922,6 +923,48 @@ Returns the guest memory type for a specific guest physical address (``gpa``). * -KVM_EINVAL - the padding is not zero * -KVM_EAGAIN - the selected vCPU can't be introspected yet +22. KVMI_VCPU_CONTROL_MSR +------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_control_msr { + __u8 enable; + __u8 padding1; + __u16 padding2; + __u32 msr; + }; + +:Returns: + +:: + + struct kvmi_error_code + +Enables/disables introspection for a specific MSR and must be used +in addition to *KVMI_VCPU_CONTROL_EVENTS* with the *KVMI_VCPU_EVENT_MSR* +ID set. + +Currently, only MSRs within the following two ranges are supported. Trying +to control events for any other register will fail with -KVM_EINVAL:: + + 0 ... 0x00001fff + 0xc0000000 ... 0xc0001fff + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the specified MSR is invalid +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_EPERM - the interception of the selected MSR is disallowed + from userspace (KVM_X86_SET_MSR_FILTER) + Events ====== @@ -1260,3 +1303,39 @@ introspection tool. KVMI_DESC_TR ``write`` is 1 if the descriptor was written, 0 otherwise. + +9. KVMI_VCPU_EVENT_MSR +---------------------- + +:Architectures: x86 +:Versions: >= 1 +:Actions: CONTINUE, CRASH +:Parameters: + +:: + + struct kvmi_vcpu_event; + struct kvmi_vcpu_event_msr { + __u32 msr; + __u32 padding; + __u64 old_value; + __u64 new_value; + }; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + struct kvmi_vcpu_event_msr_reply { + __u64 new_val; + }; + +This event is sent when a model specific register is going to be changed +and the introspection has been enabled for this event and for this specific +register (see **KVMI_VCPU_CONTROL_EVENTS**). + +``kvmi_vcpu_event`` (with the vCPU state), the MSR number (``msr``), +the old value (``old_value``) and the new value (``new_value``) are sent +to the introspection tool. The *CONTINUE* action will set the ``new_val``. diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index a872277eba67..5a4fc5b80907 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -4,7 +4,10 @@ #include +struct msr_data; + #define KVMI_NUM_CR 5 +#define KVMI_NUM_MSR 0x2000 struct kvmi_monitor_interception { bool kvmi_intercepted; @@ -18,6 +21,12 @@ struct kvmi_interception { struct kvmi_monitor_interception breakpoint; struct kvmi_monitor_interception cr3w; struct kvmi_monitor_interception descriptor; + struct { + struct { + DECLARE_BITMAP(low, KVMI_NUM_MSR); + DECLARE_BITMAP(high, KVMI_NUM_MSR); + } kvmi_mask; + } msrw; }; struct kvm_vcpu_arch_introspection { @@ -51,6 +60,7 @@ void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, u64 old_value, u64 new_value); bool kvmi_monitor_desc_intercept(struct kvm_vcpu *vcpu, bool enable); bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write); +bool kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr); #else /* CONFIG_KVM_INTROSPECTION */ @@ -70,6 +80,8 @@ static inline bool kvmi_monitor_desc_intercept(struct kvm_vcpu *vcpu, bool enable) { return false; } static inline bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write) { return true; } +static inline bool kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr) + { return true; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index db01c56a95ff..705e90c2137a 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -141,4 +141,22 @@ struct kvmi_vcpu_event_descriptor { __u8 padding[6]; }; +struct kvmi_vcpu_control_msr { + __u8 enable; + __u8 padding1; + __u16 padding2; + __u32 msr; +}; + +struct kvmi_vcpu_event_msr { + __u32 msr; + __u32 padding; + __u64 old_value; + __u64 new_value; +}; + +struct kvmi_vcpu_event_msr_reply { + __u64 new_val; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 4106ae63a115..ce29e01ba7a6 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -16,6 +16,7 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) set_bit(KVMI_VCPU_EVENT_CR, supported); set_bit(KVMI_VCPU_EVENT_HYPERCALL, supported); set_bit(KVMI_VCPU_EVENT_DESCRIPTOR, supported); + set_bit(KVMI_VCPU_EVENT_MSR, supported); set_bit(KVMI_VCPU_EVENT_TRAP, supported); set_bit(KVMI_VCPU_EVENT_XSETBV, supported); } @@ -344,6 +345,77 @@ static void kvmi_arch_disable_desc_intercept(struct kvm_vcpu *vcpu) vcpu->arch.kvmi->descriptor.kvm_intercepted = false; } +static unsigned long *msr_mask(struct kvm_vcpu *vcpu, unsigned int *msr) +{ + switch (*msr) { + case 0 ... 0x1fff: + return vcpu->arch.kvmi->msrw.kvmi_mask.low; + case 0xc0000000 ... 0xc0001fff: + *msr &= 0x1fff; + return vcpu->arch.kvmi->msrw.kvmi_mask.high; + } + + return NULL; +} + +static bool test_msr_mask(struct kvm_vcpu *vcpu, unsigned int msr) +{ + unsigned long *mask = msr_mask(vcpu, &msr); + + if (!mask) + return false; + + return !!test_bit(msr, mask); +} + +static bool msr_control(struct kvm_vcpu *vcpu, unsigned int msr, bool enable) +{ + unsigned long *mask = msr_mask(vcpu, &msr); + + if (!mask) + return false; + + if (enable) + set_bit(msr, mask); + else + clear_bit(msr, mask); + + return true; +} + +static unsigned int msr_mask_to_base(struct kvm_vcpu *vcpu, unsigned long *mask) +{ + if (mask == vcpu->arch.kvmi->msrw.kvmi_mask.high) + return 0xc0000000; + + return 0; +} + +void kvmi_control_msrw_intercept(struct kvm_vcpu *vcpu, u32 msr, bool enable) +{ + kvm_x86_ops.control_msr_intercept(vcpu, msr, MSR_TYPE_W, enable); + msr_control(vcpu, msr, enable); +} + +static void kvmi_arch_disable_msr_intercept(struct kvm_vcpu *vcpu, + unsigned long *mask) +{ + unsigned int msr_base = msr_mask_to_base(vcpu, mask); + int offset = -1; + + for (;;) { + offset = find_next_bit(mask, KVMI_NUM_MSR, offset + 1); + + if (offset >= KVMI_NUM_MSR) + break; + + kvm_x86_ops.control_msr_intercept(vcpu, msr_base + offset, + MSR_TYPE_W, false); + } + + bitmap_zero(mask, KVMI_NUM_MSR); +} + int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, unsigned int event_id, bool enable) { @@ -385,9 +457,13 @@ void kvmi_arch_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len) static void kvmi_arch_restore_interception(struct kvm_vcpu *vcpu) { + struct kvmi_interception *arch_vcpui = vcpu->arch.kvmi; + kvmi_arch_disable_bp_intercept(vcpu); kvmi_arch_disable_cr3w_intercept(vcpu); kvmi_arch_disable_desc_intercept(vcpu); + kvmi_arch_disable_msr_intercept(vcpu, arch_vcpui->msrw.kvmi_mask.low); + kvmi_arch_disable_msr_intercept(vcpu, arch_vcpui->msrw.kvmi_mask.high); } bool kvmi_arch_clean_up_interception(struct kvm_vcpu *vcpu) @@ -700,3 +776,52 @@ bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write) return ret; } EXPORT_SYMBOL(kvmi_descriptor_event); + +static bool __kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr) +{ + struct msr_data old_msr = { + .host_initiated = true, + .index = msr->index, + }; + u64 reply_value; + u32 action; + bool ret; + + if (!test_msr_mask(vcpu, msr->index)) + return true; + if (kvm_x86_ops.get_msr(vcpu, &old_msr)) + return true; + if (old_msr.data == msr->data) + return true; + + action = kvmi_msg_send_vcpu_msr(vcpu, msr->index, old_msr.data, + msr->data, &reply_value); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + msr->data = reply_value; + ret = true; + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + ret = false; + } + + return ret; +} + +bool kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr) +{ + struct kvm_introspection *kvmi; + bool ret = true; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return true; + + if (is_vcpu_event_enabled(vcpu, KVMI_VCPU_EVENT_MSR)) + ret = __kvmi_msr_event(vcpu, msr); + + kvmi_put(vcpu->kvm); + + return ret; +} diff --git a/arch/x86/kvm/kvmi.h b/arch/x86/kvm/kvmi.h index 92422e2e57cf..6483edbb80c5 100644 --- a/arch/x86/kvm/kvmi.h +++ b/arch/x86/kvm/kvmi.h @@ -17,5 +17,8 @@ u32 kvmi_msg_send_vcpu_trap(struct kvm_vcpu *vcpu); u32 kvmi_msg_send_vcpu_xsetbv(struct kvm_vcpu *vcpu, u8 xcr, u64 old_value, u64 new_value); u32 kvmi_msg_send_vcpu_descriptor(struct kvm_vcpu *vcpu, u8 desc, bool write); +void kvmi_control_msrw_intercept(struct kvm_vcpu *vcpu, u32 msr, bool enable); +u32 kvmi_msg_send_vcpu_msr(struct kvm_vcpu *vcpu, u32 msr, u64 old_value, + u64 new_value, u64 *ret_value); #endif diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 7a851b4d5983..c961c5367a13 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -249,8 +249,36 @@ static int handle_vcpu_get_mtrr_type(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, 0, &rpl, sizeof(rpl)); } +static bool is_valid_msr(unsigned int msr) +{ + return msr <= 0x1fff || (msr >= 0xc0000000 && msr <= 0xc0001fff); +} + +static int handle_vcpu_control_msr(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vcpu_control_msr *req = _req; + int ec = 0; + + if (req->padding1 || req->padding2 || req->enable > 1) + ec = -KVM_EINVAL; + else if (!is_valid_msr(req->msr)) + ec = -KVM_EINVAL; + else if (req->enable && + !kvm_msr_allowed(job->vcpu, req->msr, + KVM_MSR_FILTER_WRITE)) + ec = -KVM_EPERM; + else + kvmi_control_msrw_intercept(job->vcpu, req->msr, + req->enable == 1); + + return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, + [KVMI_VCPU_CONTROL_MSR] = handle_vcpu_control_msr, [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, [KVMI_VCPU_GET_MTRR_TYPE] = handle_vcpu_get_mtrr_type, @@ -350,3 +378,27 @@ u32 kvmi_msg_send_vcpu_descriptor(struct kvm_vcpu *vcpu, u8 desc, bool write) return action; } + +u32 kvmi_msg_send_vcpu_msr(struct kvm_vcpu *vcpu, u32 msr, u64 old_value, + u64 new_value, u64 *ret_value) +{ + struct kvmi_vcpu_event_msr e; + struct kvmi_vcpu_event_msr_reply r; + int err, action; + + memset(&e, 0, sizeof(e)); + e.msr = msr; + e.old_value = old_value; + e.new_value = new_value; + + err = kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_MSR, &e, sizeof(e), + &r, sizeof(r), &action); + if (err) { + action = KVMI_EVENT_ACTION_CONTINUE; + *ret_value = new_value; + } else { + *ret_value = r.new_val; + } + + return action; +} diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index d62294fee424..0ffa2e73284e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1588,6 +1588,9 @@ static int __kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data, msr.index = index; msr.host_initiated = host_initiated; + if (!host_initiated && !kvmi_msr_event(vcpu, &msr)) + return 1; + return kvm_x86_ops.set_msr(vcpu, &msr); } diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 09f78c0efc4f..c8e7f4516379 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -48,6 +48,7 @@ enum { KVMI_VCPU_GET_XSAVE = KVMI_VCPU_MESSAGE_ID(9), KVMI_VCPU_SET_XSAVE = KVMI_VCPU_MESSAGE_ID(10), KVMI_VCPU_GET_MTRR_TYPE = KVMI_VCPU_MESSAGE_ID(11), + KVMI_VCPU_CONTROL_MSR = KVMI_VCPU_MESSAGE_ID(12), KVMI_NEXT_VCPU_MESSAGE }; @@ -69,6 +70,7 @@ enum { KVMI_VCPU_EVENT_TRAP = KVMI_VCPU_EVENT_ID(4), KVMI_VCPU_EVENT_XSETBV = KVMI_VCPU_EVENT_ID(5), KVMI_VCPU_EVENT_DESCRIPTOR = KVMI_VCPU_EVENT_ID(6), + KVMI_VCPU_EVENT_MSR = KVMI_VCPU_EVENT_ID(7), KVMI_NEXT_VCPU_EVENT }; diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index d26df3a9ffff..2e07b22bc8c0 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -60,6 +60,7 @@ enum { GUEST_TEST_CR, GUEST_TEST_DESCRIPTOR, GUEST_TEST_HYPERCALL, + GUEST_TEST_MSR, GUEST_TEST_XSETBV, }; @@ -104,6 +105,15 @@ static void guest_hypercall_test(void) asm volatile(".byte 0x0f,0x01,0xc1"); } +static void guest_msr_test(void) +{ + uint64_t msr; + + msr = rdmsr(MSR_MISC_FEATURES_ENABLES); + msr |= 1; /* MSR_MISC_FEATURES_ENABLES_CPUID_FAULT */ + wrmsr(MSR_MISC_FEATURES_ENABLES, msr); +} + /* from fpu/internal.h */ static u64 xgetbv(u32 index) { @@ -161,6 +171,9 @@ static void guest_code(void) case GUEST_TEST_HYPERCALL: guest_hypercall_test(); break; + case GUEST_TEST_MSR: + guest_msr_test(); + break; case GUEST_TEST_XSETBV: guest_xsetbv_test(); break; @@ -1579,6 +1592,103 @@ static void test_event_descriptor(struct kvm_vm *vm) disable_vcpu_event(vm, event_id); } +static void cmd_control_msr(struct kvm_vm *vm, __u32 msr, __u8 enable, + int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_control_msr cmd; + } req = {}; + + req.cmd.msr = msr; + req.cmd.enable = enable; + + test_vcpu0_command(vm, KVMI_VCPU_CONTROL_MSR, &req.hdr, sizeof(req), + NULL, 0, expected_err); +} + +static void enable_msr_events(struct kvm_vm *vm, __u32 msr) +{ + enable_vcpu_event(vm, KVMI_VCPU_EVENT_MSR); + cmd_control_msr(vm, msr, 1, 0); +} + +static void disable_msr_events(struct kvm_vm *vm, __u32 msr) +{ + cmd_control_msr(vm, msr, 0, 0); + disable_vcpu_event(vm, KVMI_VCPU_EVENT_MSR); +} + +static void handle_msr_event(struct kvm_vm *vm, __u16 event_id, __u32 msr, + __u64 *old_value) +{ + struct kvmi_msg_hdr hdr; + struct { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_msr msr; + } ev; + struct { + struct vcpu_reply common; + struct kvmi_vcpu_event_msr_reply msr; + } rpl = {}; + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), event_id); + + pr_debug("MSR 0x%x, old 0x%llx, new 0x%llx\n", + ev.msr.msr, ev.msr.old_value, ev.msr.new_value); + + TEST_ASSERT(ev.msr.msr == msr, + "Unexpected MSR event, received MSR 0x%x, expected MSR 0x%x", + ev.msr.msr, msr); + + *old_value = rpl.msr.new_val = ev.msr.old_value; + + reply_to_event(&hdr, &ev.vcpu_ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl.common, sizeof(rpl)); +} + +static void test_invalid_control_msr(struct kvm_vm *vm, __u32 msr) +{ + __u8 enable = 1, enable_inval = 2; + int expected_err = -KVM_EINVAL; + __u32 msr_inval = -1; + + cmd_control_msr(vm, msr, enable_inval, expected_err); + cmd_control_msr(vm, msr_inval, enable, expected_err); +} + +static void test_cmd_vcpu_control_msr(struct kvm_vm *vm) +{ + struct vcpu_worker_data data = { + .vm = vm, + .vcpu_id = VCPU_ID, + .test_id = GUEST_TEST_MSR, + }; + __u16 event_id = KVMI_VCPU_EVENT_MSR; + __u32 msr = MSR_MISC_FEATURES_ENABLES; + pthread_t vcpu_thread; + uint64_t msr_data; + __u64 old_value; + + enable_msr_events(vm, msr); + + vcpu_thread = start_vcpu_worker(&data); + + handle_msr_event(vm, event_id, msr, &old_value); + + wait_vcpu_worker(vcpu_thread); + + disable_msr_events(vm, msr); + + msr_data = vcpu_get_msr(vm, VCPU_ID, msr); + TEST_ASSERT(msr_data == old_value, + "Failed to block MSR 0x%x update, value 0x%lx, expected 0x%llx", + msr, msr_data, old_value); + + test_invalid_control_msr(vm, msr); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1610,6 +1720,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_xsave(vm); test_cmd_vcpu_get_mtrr_type(vm); test_event_descriptor(vm); + test_cmd_vcpu_control_msr(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:52 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930839 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 1A183C71156 for ; Wed, 25 Nov 2020 09:42:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id BDC312083E for ; Wed, 25 Nov 2020 09:42:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728630AbgKYJmL (ORCPT ); Wed, 25 Nov 2020 04:42:11 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57240 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728585AbgKYJmJ (ORCPT ); Wed, 25 Nov 2020 04:42:09 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id BBFBE305D3E5; Wed, 25 Nov 2020 11:35:54 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 98CC73072785; Wed, 25 Nov 2020 11:35:54 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 73/81] KVM: introspection: restore the state of MSR interception on unhook Date: Wed, 25 Nov 2020 11:35:52 +0200 Message-Id: <20201125093600.2766-74-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This commit also ensures that the introspection tool and the userspace do not disable each other the MSR access VM-exit. Signed-off-by: Nicușor Cîțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvmi_host.h | 12 +++ arch/x86/kvm/kvmi.c | 124 +++++++++++++++++++++++++++---- arch/x86/kvm/svm/svm.c | 10 +++ arch/x86/kvm/vmx/vmx.c | 11 +++ 4 files changed, 142 insertions(+), 15 deletions(-) diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 5a4fc5b80907..8822f0310156 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -26,6 +26,12 @@ struct kvmi_interception { DECLARE_BITMAP(low, KVMI_NUM_MSR); DECLARE_BITMAP(high, KVMI_NUM_MSR); } kvmi_mask; + struct { + DECLARE_BITMAP(low, KVMI_NUM_MSR); + DECLARE_BITMAP(high, KVMI_NUM_MSR); + } kvm_mask; + bool (*monitor_fct)(struct kvm_vcpu *vcpu, u32 msr, + bool enable); } msrw; }; @@ -61,6 +67,8 @@ void kvmi_xsetbv_event(struct kvm_vcpu *vcpu, u8 xcr, bool kvmi_monitor_desc_intercept(struct kvm_vcpu *vcpu, bool enable); bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write); bool kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr); +bool kvmi_monitor_msrw_intercept(struct kvm_vcpu *vcpu, u32 msr, bool enable); +bool kvmi_msrw_intercept_originator(struct kvm_vcpu *vcpu); #else /* CONFIG_KVM_INTROSPECTION */ @@ -82,6 +90,10 @@ static inline bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write) { return true; } static inline bool kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr) { return true; } +static inline bool kvmi_monitor_msrw_intercept(struct kvm_vcpu *vcpu, u32 msr, + bool enable) { return false; } +static inline bool kvmi_msrw_intercept_originator(struct kvm_vcpu *vcpu) + { return false; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index ce29e01ba7a6..e325dad88dbb 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -345,22 +345,25 @@ static void kvmi_arch_disable_desc_intercept(struct kvm_vcpu *vcpu) vcpu->arch.kvmi->descriptor.kvm_intercepted = false; } -static unsigned long *msr_mask(struct kvm_vcpu *vcpu, unsigned int *msr) +static unsigned long *msr_mask(struct kvm_vcpu *vcpu, unsigned int *msr, + bool kvmi) { switch (*msr) { case 0 ... 0x1fff: - return vcpu->arch.kvmi->msrw.kvmi_mask.low; + return kvmi ? vcpu->arch.kvmi->msrw.kvmi_mask.low : + vcpu->arch.kvmi->msrw.kvm_mask.low; case 0xc0000000 ... 0xc0001fff: *msr &= 0x1fff; - return vcpu->arch.kvmi->msrw.kvmi_mask.high; + return kvmi ? vcpu->arch.kvmi->msrw.kvmi_mask.high : + vcpu->arch.kvmi->msrw.kvm_mask.high; } return NULL; } -static bool test_msr_mask(struct kvm_vcpu *vcpu, unsigned int msr) +static bool test_msr_mask(struct kvm_vcpu *vcpu, unsigned int msr, bool kvmi) { - unsigned long *mask = msr_mask(vcpu, &msr); + unsigned long *mask = msr_mask(vcpu, &msr, kvmi); if (!mask) return false; @@ -368,9 +371,27 @@ static bool test_msr_mask(struct kvm_vcpu *vcpu, unsigned int msr) return !!test_bit(msr, mask); } -static bool msr_control(struct kvm_vcpu *vcpu, unsigned int msr, bool enable) +/* + * Returns true if one side (kvm or kvmi) tries to disable the MSR write + * interception while the other side is still tracking it. + */ +bool kvmi_monitor_msrw_intercept(struct kvm_vcpu *vcpu, u32 msr, bool enable) { - unsigned long *mask = msr_mask(vcpu, &msr); + struct kvmi_interception *arch_vcpui; + + if (!vcpu) + return false; + + arch_vcpui = READ_ONCE(vcpu->arch.kvmi); + + return (arch_vcpui && arch_vcpui->msrw.monitor_fct(vcpu, msr, enable)); +} +EXPORT_SYMBOL(kvmi_monitor_msrw_intercept); + +static bool msr_control(struct kvm_vcpu *vcpu, unsigned int msr, bool enable, + bool kvmi) +{ + unsigned long *mask = msr_mask(vcpu, &msr, kvmi); if (!mask) return false; @@ -383,6 +404,63 @@ static bool msr_control(struct kvm_vcpu *vcpu, unsigned int msr, bool enable) return true; } +static bool msr_intercepted_by_kvmi(struct kvm_vcpu *vcpu, u32 msr) +{ + return test_msr_mask(vcpu, msr, true); +} + +static bool msr_intercepted_by_kvm(struct kvm_vcpu *vcpu, u32 msr) +{ + return test_msr_mask(vcpu, msr, false); +} + +static void record_msr_intercept_status_for_kvmi(struct kvm_vcpu *vcpu, u32 msr, + bool enable) +{ + msr_control(vcpu, msr, enable, true); +} + +static void record_msr_intercept_status_for_kvm(struct kvm_vcpu *vcpu, u32 msr, + bool enable) +{ + msr_control(vcpu, msr, enable, false); +} + +static bool monitor_msrw_fct_kvmi(struct kvm_vcpu *vcpu, u32 msr, bool enable) +{ + bool ret = false; + + if (enable) { + if (kvm_x86_ops.msr_write_intercepted(vcpu, msr)) + record_msr_intercept_status_for_kvm(vcpu, msr, true); + } else { + if (unlikely(!msr_intercepted_by_kvmi(vcpu, msr))) + ret = true; + + if (msr_intercepted_by_kvm(vcpu, msr)) + ret = true; + } + + record_msr_intercept_status_for_kvmi(vcpu, msr, enable); + + return ret; +} + +static bool monitor_msrw_fct_kvm(struct kvm_vcpu *vcpu, u32 msr, bool enable) +{ + bool ret = false; + + if (!(msr_intercepted_by_kvmi(vcpu, msr))) + return false; + + if (!enable) + ret = true; + + record_msr_intercept_status_for_kvm(vcpu, msr, enable); + + return ret; +} + static unsigned int msr_mask_to_base(struct kvm_vcpu *vcpu, unsigned long *mask) { if (mask == vcpu->arch.kvmi->msrw.kvmi_mask.high) @@ -393,12 +471,13 @@ static unsigned int msr_mask_to_base(struct kvm_vcpu *vcpu, unsigned long *mask) void kvmi_control_msrw_intercept(struct kvm_vcpu *vcpu, u32 msr, bool enable) { + vcpu->arch.kvmi->msrw.monitor_fct = monitor_msrw_fct_kvmi; kvm_x86_ops.control_msr_intercept(vcpu, msr, MSR_TYPE_W, enable); - msr_control(vcpu, msr, enable); + vcpu->arch.kvmi->msrw.monitor_fct = monitor_msrw_fct_kvm; } -static void kvmi_arch_disable_msr_intercept(struct kvm_vcpu *vcpu, - unsigned long *mask) +static void kvmi_arch_disable_msrw_intercept(struct kvm_vcpu *vcpu, + unsigned long *mask) { unsigned int msr_base = msr_mask_to_base(vcpu, mask); int offset = -1; @@ -409,8 +488,7 @@ static void kvmi_arch_disable_msr_intercept(struct kvm_vcpu *vcpu, if (offset >= KVMI_NUM_MSR) break; - kvm_x86_ops.control_msr_intercept(vcpu, msr_base + offset, - MSR_TYPE_W, false); + kvmi_control_msrw_intercept(vcpu, msr_base + offset, false); } bitmap_zero(mask, KVMI_NUM_MSR); @@ -462,8 +540,8 @@ static void kvmi_arch_restore_interception(struct kvm_vcpu *vcpu) kvmi_arch_disable_bp_intercept(vcpu); kvmi_arch_disable_cr3w_intercept(vcpu); kvmi_arch_disable_desc_intercept(vcpu); - kvmi_arch_disable_msr_intercept(vcpu, arch_vcpui->msrw.kvmi_mask.low); - kvmi_arch_disable_msr_intercept(vcpu, arch_vcpui->msrw.kvmi_mask.high); + kvmi_arch_disable_msrw_intercept(vcpu, arch_vcpui->msrw.kvmi_mask.low); + kvmi_arch_disable_msrw_intercept(vcpu, arch_vcpui->msrw.kvmi_mask.high); } bool kvmi_arch_clean_up_interception(struct kvm_vcpu *vcpu) @@ -490,12 +568,14 @@ bool kvmi_arch_vcpu_alloc_interception(struct kvm_vcpu *vcpu) arch_vcpui->breakpoint.monitor_fct = monitor_bp_fct_kvm; arch_vcpui->cr3w.monitor_fct = monitor_cr3w_fct_kvm; arch_vcpui->descriptor.monitor_fct = monitor_desc_fct_kvm; + arch_vcpui->msrw.monitor_fct = monitor_msrw_fct_kvm; /* * paired with: * - kvmi_monitor_bp_intercept() * - kvmi_monitor_cr3w_intercept() * - kvmi_monitor_desc_intercept() + * - kvmi_monitor_msrw_intercept() */ smp_wmb(); WRITE_ONCE(vcpu->arch.kvmi, arch_vcpui); @@ -777,6 +857,20 @@ bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write) } EXPORT_SYMBOL(kvmi_descriptor_event); +bool kvmi_msrw_intercept_originator(struct kvm_vcpu *vcpu) +{ + struct kvmi_interception *arch_vcpui; + + if (!vcpu) + return false; + + arch_vcpui = READ_ONCE(vcpu->arch.kvmi); + + return (arch_vcpui && + arch_vcpui->msrw.monitor_fct == monitor_msrw_fct_kvmi); +} +EXPORT_SYMBOL(kvmi_msrw_intercept_originator); + static bool __kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr) { struct msr_data old_msr = { @@ -787,7 +881,7 @@ static bool __kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr) u32 action; bool ret; - if (!test_msr_mask(vcpu, msr->index)) + if (!test_msr_mask(vcpu, msr->index, true)) return true; if (kvm_x86_ops.get_msr(vcpu, &old_msr)) return true; diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 834e4b6c4112..08189e814f59 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -641,6 +641,16 @@ static void set_msr_interception_bitmap(struct kvm_vcpu *vcpu, u32 *msrpm, unsigned long tmp; u32 offset; +#ifdef CONFIG_KVM_INTROSPECTION + if ((type & MSR_TYPE_W) && + kvmi_monitor_msrw_intercept(vcpu, msr, !value)) + type &= ~MSR_TYPE_W; + + /* + * Avoid the below warning for kvmi intercepted msrs. + */ + if (!kvmi_msrw_intercept_originator(vcpu)) +#endif /* CONFIG_KVM_INTROSPECTION */ /* * If this warning triggers extend the direct_access_msrs list at the * beginning of the file diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 245332ce91a5..01d18c9243bc 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -3755,6 +3755,12 @@ static __always_inline void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, if (!cpu_has_vmx_msr_bitmap()) return; +#ifdef CONFIG_KVM_INTROSPECTION + if ((type & MSR_TYPE_W) && + kvmi_monitor_msrw_intercept(vcpu, msr, false)) + type &= ~MSR_TYPE_W; +#endif /* CONFIG_KVM_INTROSPECTION */ + if (static_branch_unlikely(&enable_evmcs)) evmcs_touch_msr_bitmap(); @@ -3801,6 +3807,11 @@ static __always_inline void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, if (!cpu_has_vmx_msr_bitmap()) return; +#ifdef CONFIG_KVM_INTROSPECTION + if (type & MSR_TYPE_W) + kvmi_monitor_msrw_intercept(vcpu, msr, true); +#endif /* CONFIG_KVM_INTROSPECTION */ + if (static_branch_unlikely(&enable_evmcs)) evmcs_touch_msr_bitmap(); From patchwork Wed Nov 25 09:35:53 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930887 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 04E05C56201 for ; Wed, 25 Nov 2020 09:41:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A7B9A2083E for ; Wed, 25 Nov 2020 09:41:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728144AbgKYJl6 (ORCPT ); Wed, 25 Nov 2020 04:41:58 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57140 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727378AbgKYJl5 (ORCPT ); Wed, 25 Nov 2020 04:41:57 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id E5B17305D3E7; Wed, 25 Nov 2020 11:35:54 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id BCDB03072784; Wed, 25 Nov 2020 11:35:54 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 74/81] KVM: introspection: add KVMI_VM_SET_PAGE_ACCESS Date: Wed, 25 Nov 2020 11:35:53 +0200 Message-Id: <20201125093600.2766-75-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This command sets the spte access bits (rwx) for an array of guest physical addresses (through the page tracking subsystem). These GPAs, with the requested access bits, are also kept in a radix tree in order to filter out the #PF events which are of no interest to the introspection tool and to reapply the settings when a memory slot is moved. Signed-off-by: Mihai Donțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 59 +++++++++ arch/x86/include/asm/kvm_host.h | 2 + arch/x86/include/asm/kvmi_host.h | 7 ++ arch/x86/kvm/kvmi.c | 40 ++++++ include/linux/kvmi_host.h | 3 + include/uapi/linux/kvmi.h | 20 +++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 50 ++++++++ virt/kvm/introspection/kvmi.c | 119 +++++++++++++++++- virt/kvm/introspection/kvmi_int.h | 10 ++ virt/kvm/introspection/kvmi_msg.c | 59 +++++++++ 10 files changed, 368 insertions(+), 1 deletion(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 3466db72e5e8..1540f75c4462 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -965,6 +965,65 @@ to control events for any other register will fail with -KVM_EINVAL:: * -KVM_EPERM - the interception of the selected MSR is disallowed from userspace (KVM_X86_SET_MSR_FILTER) +23. KVMI_VM_SET_PAGE_ACCESS +--------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vm_set_page_access { + __u16 count; + __u16 padding1; + __u32 padding2; + struct kvmi_page_access_entry entries[0]; + }; + +where:: + + struct kvmi_page_access_entry { + __u64 gpa; + __u8 access; + __u8 padding[7]; + }; + + +:Returns: + +:: + + struct kvmi_error_code + +Sets the access bits (rwx) for an array of ``count`` guest physical +addresses (``gpa``). + +The valid access bits are:: + + KVMI_PAGE_ACCESS_R + KVMI_PAGE_ACCESS_W + KVMI_PAGE_ACCESS_X + + +The command will fail with -KVM_EINVAL if any of the specified combination +of access bits is not supported or the address (``gpa``) is not valid +(visible). + +The command will try to apply all changes and return the first error if +some failed. The introspection tool should handle the rollback. + +In order to 'forget' an address, all three bits ('rwx') must be set. + +:Errors: + +* -KVM_EINVAL - the specified access bits combination is invalid +* -KVM_EINVAL - the address is not valid +* -KVM_EINVAL - the padding is not zero +* -KVM_EINVAL - the message size is invalid +* -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_ENOMEM - there is not enough memory to add the page tracking structures + Events ====== diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index d4e2fe493419..27406462aa05 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -45,6 +45,8 @@ #define KVM_PRIVATE_MEM_SLOTS 3 #define KVM_MEM_SLOTS_NUM (KVM_USER_MEM_SLOTS + KVM_PRIVATE_MEM_SLOTS) +#include + #define KVM_HALT_POLL_NS_DEFAULT 200000 #define KVM_IRQCHIP_NUM_PINS KVM_IOAPIC_NUM_PINS diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 8822f0310156..420358c4a9ae 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -2,6 +2,7 @@ #ifndef _ASM_X86_KVMI_HOST_H #define _ASM_X86_KVMI_HOST_H +#include #include struct msr_data; @@ -54,6 +55,12 @@ struct kvm_vcpu_arch_introspection { struct kvm_arch_introspection { }; +#define SLOTS_SIZE BITS_TO_LONGS(KVM_MEM_SLOTS_NUM) + +struct kvmi_arch_mem_access { + unsigned long active[KVM_PAGE_TRACK_MAX][SLOTS_SIZE]; +}; + #ifdef CONFIG_KVM_INTROSPECTION bool kvmi_monitor_bp_intercept(struct kvm_vcpu *vcpu, u32 dbg); diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index e325dad88dbb..acd4756e0d78 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -919,3 +919,43 @@ bool kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr) return ret; } + +static const struct { + unsigned int allow_bit; + enum kvm_page_track_mode track_mode; +} track_modes[] = { + { KVMI_PAGE_ACCESS_R, KVM_PAGE_TRACK_PREREAD }, + { KVMI_PAGE_ACCESS_W, KVM_PAGE_TRACK_PREWRITE }, + { KVMI_PAGE_ACCESS_X, KVM_PAGE_TRACK_PREEXEC }, +}; + +void kvmi_arch_update_page_tracking(struct kvm *kvm, + struct kvm_memory_slot *slot, + struct kvmi_mem_access *m) +{ + struct kvmi_arch_mem_access *arch = &m->arch; + int i; + + if (!slot) { + slot = gfn_to_memslot(kvm, m->gfn); + if (!slot) + return; + } + + for (i = 0; i < ARRAY_SIZE(track_modes); i++) { + unsigned int allow_bit = track_modes[i].allow_bit; + enum kvm_page_track_mode mode = track_modes[i].track_mode; + bool slot_tracked = test_bit(slot->id, arch->active[mode]); + + if (m->access & allow_bit) { + if (slot_tracked) { + kvm_slot_page_track_remove_page(kvm, slot, + m->gfn, mode); + clear_bit(slot->id, arch->active[mode]); + } + } else if (!slot_tracked) { + kvm_slot_page_track_add_page(kvm, slot, m->gfn, mode); + set_bit(slot->id, arch->active[mode]); + } + } +} diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 7a7360306812..14ac075a3ea9 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -52,6 +52,9 @@ struct kvm_introspection { atomic_t ev_seq; bool restore_on_unhook; + + struct radix_tree_root access_tree; + rwlock_t access_tree_lock; }; int kvmi_version(void); diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index c8e7f4516379..42c8e6342fcf 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -30,6 +30,7 @@ enum { KVMI_VM_PAUSE_VCPU = KVMI_VM_MESSAGE_ID(8), KVMI_VM_CONTROL_CLEANUP = KVMI_VM_MESSAGE_ID(9), KVMI_VM_GET_MAX_GFN = KVMI_VM_MESSAGE_ID(10), + KVMI_VM_SET_PAGE_ACCESS = KVMI_VM_MESSAGE_ID(11), KVMI_NEXT_VM_MESSAGE }; @@ -81,6 +82,12 @@ enum { KVMI_EVENT_ACTION_CRASH = 2, }; +enum { + KVMI_PAGE_ACCESS_R = 1 << 0, + KVMI_PAGE_ACCESS_W = 1 << 1, + KVMI_PAGE_ACCESS_X = 1 << 2, +}; + struct kvmi_msg_hdr { __u16 id; __u16 size; @@ -190,4 +197,17 @@ struct kvmi_vm_get_max_gfn_reply { __u64 gfn; }; +struct kvmi_page_access_entry { + __u64 gpa; + __u8 access; + __u8 padding[7]; +}; + +struct kvmi_vm_set_page_access { + __u16 count; + __u16 padding1; + __u32 padding2; + struct kvmi_page_access_entry entries[0]; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 2e07b22bc8c0..c9d6ee57d506 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1689,6 +1689,55 @@ static void test_cmd_vcpu_control_msr(struct kvm_vm *vm) test_invalid_control_msr(vm, msr); } +static int cmd_set_page_access(__u16 count, __u64 *gpa, __u8 *access) +{ + struct kvmi_page_access_entry *entry, *end; + struct kvmi_vm_set_page_access *cmd; + struct kvmi_msg_hdr *req; + size_t req_size; + int r; + + req_size = sizeof(*req) + sizeof(*cmd) + count * sizeof(*entry); + req = calloc(1, req_size); + + cmd = (struct kvmi_vm_set_page_access *)(req + 1); + cmd->count = count; + + entry = cmd->entries; + end = cmd->entries + count; + for (; entry < end; entry++) { + entry->gpa = *gpa++; + entry->access = *access++; + } + + r = do_command(KVMI_VM_SET_PAGE_ACCESS, req, req_size, NULL, 0); + + free(req); + return r; +} + +static void set_page_access(__u64 gpa, __u8 access) +{ + int r; + + r = cmd_set_page_access(1, &gpa, &access); + TEST_ASSERT(r == 0, + "KVMI_VM_SET_PAGE_ACCESS failed, gpa 0x%llx, access 0x%x, error %d (%s)\n", + gpa, access, -r, kvm_strerror(-r)); +} + +static void test_cmd_vm_set_page_access(struct kvm_vm *vm) +{ + __u8 full_access = KVMI_PAGE_ACCESS_R | KVMI_PAGE_ACCESS_W | + KVMI_PAGE_ACCESS_X; + __u8 no_access = 0; + __u64 gpa = 0; + + set_page_access(gpa, no_access); + + set_page_access(gpa, full_access); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1721,6 +1770,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_get_mtrr_type(vm); test_event_descriptor(vm); test_cmd_vcpu_control_msr(vm); + test_cmd_vm_set_page_access(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index b366ba6820d6..079b355540b4 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -26,6 +26,11 @@ static DECLARE_BITMAP(Kvmi_known_vcpu_events, KVMI_NUM_EVENTS); static struct kmem_cache *msg_cache; static struct kmem_cache *job_cache; +static struct kmem_cache *radix_cache; + +static const u8 full_access = KVMI_PAGE_ACCESS_R | + KVMI_PAGE_ACCESS_W | + KVMI_PAGE_ACCESS_X; void *kvmi_msg_alloc(void) { @@ -44,6 +49,8 @@ static void kvmi_cache_destroy(void) msg_cache = NULL; kmem_cache_destroy(job_cache); job_cache = NULL; + kmem_cache_destroy(radix_cache); + radix_cache = NULL; } static int kvmi_cache_create(void) @@ -53,8 +60,11 @@ static int kvmi_cache_create(void) job_cache = kmem_cache_create("kvmi_job", sizeof(struct kvmi_job), 0, SLAB_ACCOUNT, NULL); + radix_cache = kmem_cache_create("kvmi_radix_tree", + sizeof(struct kvmi_mem_access), + 0, SLAB_ACCOUNT, NULL); - if (!msg_cache || !job_cache) { + if (!msg_cache || !job_cache || !radix_cache) { kvmi_cache_destroy(); return -1; @@ -245,12 +255,38 @@ static void kvmi_free_vcpui(struct kvm_vcpu *vcpu, bool restore_interception) kvmi_make_request(vcpu, false); } +static void kvmi_clear_mem_access(struct kvm *kvm) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + struct radix_tree_iter iter; + void **slot; + int idx; + + idx = srcu_read_lock(&kvm->srcu); + spin_lock(&kvm->mmu_lock); + + radix_tree_for_each_slot(slot, &kvmi->access_tree, &iter, 0) { + struct kvmi_mem_access *m = *slot; + + m->access = full_access; + kvmi_arch_update_page_tracking(kvm, NULL, m); + + radix_tree_iter_delete(&kvmi->access_tree, &iter, slot); + kmem_cache_free(radix_cache, m); + } + + spin_unlock(&kvm->mmu_lock); + srcu_read_unlock(&kvm->srcu, idx); +} + static void kvmi_free(struct kvm *kvm) { bool restore_interception = KVMI(kvm)->restore_on_unhook; struct kvm_vcpu *vcpu; int i; + kvmi_clear_mem_access(kvm); + kvm_for_each_vcpu(i, vcpu, kvm) kvmi_free_vcpui(vcpu, restore_interception); @@ -305,6 +341,10 @@ kvmi_alloc(struct kvm *kvm, const struct kvm_introspection_hook *hook) atomic_set(&kvmi->ev_seq, 0); + INIT_RADIX_TREE(&kvmi->access_tree, + GFP_KERNEL & ~__GFP_DIRECT_RECLAIM); + rwlock_init(&kvmi->access_tree_lock); + kvm_for_each_vcpu(i, vcpu, kvm) { int err = kvmi_create_vcpui(vcpu); @@ -937,3 +977,80 @@ bool kvmi_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len) return ret; } EXPORT_SYMBOL(kvmi_breakpoint_event); + +static struct kvmi_mem_access * +__kvmi_get_gfn_access(struct kvm_introspection *kvmi, const gfn_t gfn) +{ + return radix_tree_lookup(&kvmi->access_tree, gfn); +} + +static void kvmi_update_mem_access(struct kvm *kvm, struct kvmi_mem_access *m) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + + kvmi_arch_update_page_tracking(kvm, NULL, m); + + if (m->access == full_access) { + radix_tree_delete(&kvmi->access_tree, m->gfn); + kmem_cache_free(radix_cache, m); + } +} + +static void kvmi_insert_mem_access(struct kvm *kvm, struct kvmi_mem_access *m) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + + radix_tree_insert(&kvmi->access_tree, m->gfn, m); + kvmi_arch_update_page_tracking(kvm, NULL, m); +} + +static void kvmi_set_mem_access(struct kvm *kvm, struct kvmi_mem_access *m, + bool *used) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + struct kvmi_mem_access *found; + int idx; + + idx = srcu_read_lock(&kvm->srcu); + spin_lock(&kvm->mmu_lock); + write_lock(&kvmi->access_tree_lock); + + found = __kvmi_get_gfn_access(kvmi, m->gfn); + if (found) { + found->access = m->access; + kvmi_update_mem_access(kvm, found); + } else if (m->access != full_access) { + kvmi_insert_mem_access(kvm, m); + *used = true; + } + + write_unlock(&kvmi->access_tree_lock); + spin_unlock(&kvm->mmu_lock); + srcu_read_unlock(&kvm->srcu, idx); +} + +int kvmi_set_gfn_access(struct kvm *kvm, gfn_t gfn, u8 access) +{ + struct kvmi_mem_access *m; + bool used = false; + int err = 0; + + m = kmem_cache_zalloc(radix_cache, GFP_KERNEL); + if (!m) + return -KVM_ENOMEM; + + m->gfn = gfn; + m->access = access; + + if (radix_tree_preload(GFP_KERNEL)) + err = -KVM_ENOMEM; + else + kvmi_set_mem_access(kvm, m, &used); + + radix_tree_preload_end(); + + if (!used) + kmem_cache_free(radix_cache, m); + + return err; +} diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 0a7a8285b981..41720b194458 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -26,6 +26,12 @@ typedef int (*kvmi_vcpu_msg_job_fct)(const struct kvmi_vcpu_msg_job *job, const struct kvmi_msg_hdr *msg, const void *req); +struct kvmi_mem_access { + gfn_t gfn; + u8 access; + struct kvmi_arch_mem_access arch; +}; + static inline bool is_vcpu_event_enabled(struct kvm_vcpu *vcpu, u16 event_id) { return test_bit(event_id, VCPUI(vcpu)->ev_enable_mask); @@ -86,6 +92,7 @@ int kvmi_cmd_read_physical(struct kvm *kvm, u64 gpa, size_t size, int kvmi_cmd_write_physical(struct kvm *kvm, u64 gpa, size_t size, const void *buf); int kvmi_cmd_vcpu_pause(struct kvm_vcpu *vcpu, bool wait); +int kvmi_set_gfn_access(struct kvm *kvm, gfn_t gfn, u8 access); /* arch */ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); @@ -104,5 +111,8 @@ void kvmi_arch_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len); int kvmi_arch_cmd_control_intercept(struct kvm_vcpu *vcpu, unsigned int event_id, bool enable); void kvmi_arch_send_pending_event(struct kvm_vcpu *vcpu); +void kvmi_arch_update_page_tracking(struct kvm *kvm, + struct kvm_memory_slot *slot, + struct kvmi_mem_access *m); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 42d066e92ba2..a3d6d3df3078 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -302,6 +302,64 @@ static int handle_vm_get_max_gfn(struct kvm_introspection *kvmi, return kvmi_msg_vm_reply(kvmi, msg, 0, &rpl, sizeof(rpl)); } +static bool kvmi_is_visible_gfn(struct kvm *kvm, gfn_t gfn) +{ + bool visible; + int idx; + + idx = srcu_read_lock(&kvm->srcu); + visible = kvm_is_visible_gfn(kvm, gfn); + srcu_read_unlock(&kvm->srcu, idx); + + return visible; +} + +static int set_page_access_entry(struct kvm_introspection *kvmi, + const struct kvmi_page_access_entry *entry) +{ + u8 unknown_bits = ~(KVMI_PAGE_ACCESS_R | KVMI_PAGE_ACCESS_W | + KVMI_PAGE_ACCESS_X); + gfn_t gfn = gpa_to_gfn(entry->gpa); + struct kvm *kvm = kvmi->kvm; + + if ((entry->access & unknown_bits) || + non_zero_padding(entry->padding, ARRAY_SIZE(entry->padding))) + return -KVM_EINVAL; + + if (!kvmi_is_visible_gfn(kvm, gfn)) + return -KVM_EINVAL; + + return kvmi_set_gfn_access(kvm, gfn, entry->access); +} + +static int handle_vm_set_page_access(struct kvm_introspection *kvmi, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vm_set_page_access *req = _req; + const struct kvmi_page_access_entry *entry; + size_t n = req->count; + int ec = 0; + + if (struct_size(req, entries, n) > msg->size) + return -EINVAL; + + if (req->padding1 || req->padding2) { + ec = -KVM_EINVAL; + goto reply; + } + + for (entry = req->entries; n; n--, entry++) { + int err = set_page_access_entry(kvmi, entry); + + if (err && !ec) + ec = err; + } + +reply: + return kvmi_msg_vm_reply(kvmi, msg, ec, NULL, 0); +} + /* * These commands are executed by the receiving thread. */ @@ -315,6 +373,7 @@ static kvmi_vm_msg_fct const msg_vm[] = { [KVMI_VM_GET_MAX_GFN] = handle_vm_get_max_gfn, [KVMI_VM_PAUSE_VCPU] = handle_vm_pause_vcpu, [KVMI_VM_READ_PHYSICAL] = handle_vm_read_physical, + [KVMI_VM_SET_PAGE_ACCESS] = handle_vm_set_page_access, [KVMI_VM_WRITE_PHYSICAL] = handle_vm_write_physical, }; From patchwork Wed Nov 25 09:35:54 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930845 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 63D97C83019 for ; Wed, 25 Nov 2020 09:42:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 19F8F2083E for ; Wed, 25 Nov 2020 09:42:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728709AbgKYJmY (ORCPT ); Wed, 25 Nov 2020 04:42:24 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57140 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728535AbgKYJmG (ORCPT ); Wed, 25 Nov 2020 04:42:06 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 1ED6C305D3E8; Wed, 25 Nov 2020 11:35:55 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id E9ADA3072785; Wed, 25 Nov 2020 11:35:54 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 75/81] KVM: introspection: add KVMI_VCPU_EVENT_PF Date: Wed, 25 Nov 2020 11:35:54 +0200 Message-Id: <20201125093600.2766-76-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu This event is sent when a #PF occurs due to a failed permission check in the shadow page tables, for a page in which the introspection tool has shown interest. Signed-off-by: Mihai Donțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 66 ++++++++++ arch/x86/include/asm/kvmi_host.h | 1 + arch/x86/kvm/kvmi.c | 122 ++++++++++++++++++ include/uapi/linux/kvmi.h | 10 ++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 76 +++++++++++ virt/kvm/introspection/kvmi.c | 116 +++++++++++++++++ virt/kvm/introspection/kvmi_int.h | 7 + virt/kvm/introspection/kvmi_msg.c | 19 +++ 8 files changed, 417 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 1540f75c4462..bdcc9066ae28 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -543,6 +543,7 @@ the following events:: KVMI_VCPU_EVENT_DESCRIPTOR KVMI_VCPU_EVENT_HYPERCALL KVMI_VCPU_EVENT_MSR + KVMI_VCPU_EVENT_PF KVMI_VCPU_EVENT_XSETBV When an event is enabled, the introspection tool is notified and @@ -1398,3 +1399,68 @@ register (see **KVMI_VCPU_CONTROL_EVENTS**). ``kvmi_vcpu_event`` (with the vCPU state), the MSR number (``msr``), the old value (``old_value``) and the new value (``new_value``) are sent to the introspection tool. The *CONTINUE* action will set the ``new_val``. + +10. KVMI_VCPU_EVENT_PF +---------------------- + +:Architectures: x86 +:Versions: >= 1 +:Actions: CONTINUE, CRASH, RETRY +:Parameters: + +:: + + struct kvmi_vcpu_event; + struct kvmi_vcpu_event_pf { + __u64 gva; + __u64 gpa; + __u8 access; + __u8 padding1; + __u16 padding2; + __u32 padding3; + }; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + +This event is sent when a hypervisor page fault occurs due to a failed +permission checks, the introspection has been enabled for this event +(see *KVMI_VCPU_CONTROL_EVENTS*) and the event was generated for a +page in which the introspection tool has shown interest (ie. has +previously touched it by adjusting the spte permissions; see +*KVMI_VM_SET_PAGE_ACCESS*). + +These permissions can be used by the introspection tool to guarantee +the purpose of code areas inside the guest (code, rodata, stack, heap +etc.) Each attempt at an operation unfitting for a certain memory +range (eg. execute code in heap) triggers a page fault and gives the +introspection tool the chance to audit the code attempting the operation. + +``kvmi_vcpu_event`` (with the vCPU state), guest virtual address (``gva``) +if available or ~0 (UNMAPPED_GVA), guest physical address (``gpa``) +and the ``access`` flags (e.g. KVMI_PAGE_ACCESS_R) are sent to the +introspection tool. + +In case of a restricted read access, the guest address is the location +of the memory being read. On write access, the guest address is the +location of the memory being written. On execute access, the guest +address is the location of the instruction being executed +(``gva == kvmi_vcpu_event.arch.regs.rip``). + +In the current implementation, most of these events are sent during +emulation. If the page fault has set more than one access bit +(e.g. r-x/-rw), the introspection tool may receive more than one +KVMI_VCPU_EVENT_PF and the order depends on the KVM emulator. Another +cause of multiple events is when the page fault is triggered on access +crossing the page boundary. + +The *CONTINUE* action will continue the page fault handling (e.g. via +emulation). + +The *RETRY* action is used by the introspection tool to retry the +execution of the current instruction, usually because it changed the +instruction pointer or the page restrictions. diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 420358c4a9ae..31500d3ff69d 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -53,6 +53,7 @@ struct kvm_vcpu_arch_introspection { }; struct kvm_arch_introspection { + struct kvm_page_track_notifier_node kptn_node; }; #define SLOTS_SIZE BITS_TO_LONGS(KVM_MEM_SLOTS_NUM) diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index acd4756e0d78..cd64762643d6 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -17,10 +17,26 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) set_bit(KVMI_VCPU_EVENT_HYPERCALL, supported); set_bit(KVMI_VCPU_EVENT_DESCRIPTOR, supported); set_bit(KVMI_VCPU_EVENT_MSR, supported); + set_bit(KVMI_VCPU_EVENT_PF, supported); set_bit(KVMI_VCPU_EVENT_TRAP, supported); set_bit(KVMI_VCPU_EVENT_XSETBV, supported); } +static bool kvmi_track_preread(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + int bytes, + struct kvm_page_track_notifier_node *node); +static bool kvmi_track_prewrite(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + const u8 *new, int bytes, + struct kvm_page_track_notifier_node *node); +static bool kvmi_track_preexec(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + struct kvm_page_track_notifier_node *node); +static void kvmi_track_create_slot(struct kvm *kvm, + struct kvm_memory_slot *slot, + unsigned long npages, + struct kvm_page_track_notifier_node *node); +static void kvmi_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot, + struct kvm_page_track_notifier_node *node); + static unsigned int kvmi_vcpu_mode(const struct kvm_vcpu *vcpu, const struct kvm_sregs *sregs) { @@ -959,3 +975,109 @@ void kvmi_arch_update_page_tracking(struct kvm *kvm, } } } + +void kvmi_arch_hook(struct kvm *kvm) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + + kvmi->arch.kptn_node.track_preread = kvmi_track_preread; + kvmi->arch.kptn_node.track_prewrite = kvmi_track_prewrite; + kvmi->arch.kptn_node.track_preexec = kvmi_track_preexec; + kvmi->arch.kptn_node.track_create_slot = kvmi_track_create_slot; + kvmi->arch.kptn_node.track_flush_slot = kvmi_track_flush_slot; + + kvm_page_track_register_notifier(kvm, &kvmi->arch.kptn_node); +} + +void kvmi_arch_unhook(struct kvm *kvm) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + + kvm_page_track_unregister_notifier(kvm, &kvmi->arch.kptn_node); +} + +static bool kvmi_track_preread(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + int bytes, + struct kvm_page_track_notifier_node *node) +{ + struct kvm_introspection *kvmi; + bool ret = true; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return true; + + if (is_vcpu_event_enabled(vcpu, KVMI_VCPU_EVENT_PF)) + ret = kvmi_pf_event(vcpu, gpa, gva, KVMI_PAGE_ACCESS_R); + + kvmi_put(vcpu->kvm); + + return ret; +} + +static bool kvmi_track_prewrite(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + const u8 *new, int bytes, + struct kvm_page_track_notifier_node *node) +{ + struct kvm_introspection *kvmi; + bool ret = true; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return true; + + if (is_vcpu_event_enabled(vcpu, KVMI_VCPU_EVENT_PF)) + ret = kvmi_pf_event(vcpu, gpa, gva, KVMI_PAGE_ACCESS_W); + + kvmi_put(vcpu->kvm); + + return ret; +} + +static bool kvmi_track_preexec(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, + struct kvm_page_track_notifier_node *node) +{ + struct kvm_introspection *kvmi; + bool ret = true; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return true; + + if (is_vcpu_event_enabled(vcpu, KVMI_VCPU_EVENT_PF)) + ret = kvmi_pf_event(vcpu, gpa, gva, KVMI_PAGE_ACCESS_X); + + kvmi_put(vcpu->kvm); + + return ret; +} + +static void kvmi_track_create_slot(struct kvm *kvm, + struct kvm_memory_slot *slot, + unsigned long npages, + struct kvm_page_track_notifier_node *node) +{ + struct kvm_introspection *kvmi; + + kvmi = kvmi_get(kvm); + if (!kvmi) + return; + + kvmi_add_memslot(kvm, slot, npages); + + kvmi_put(kvm); +} + +static void kvmi_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot, + struct kvm_page_track_notifier_node *node) +{ + struct kvm_introspection *kvmi; + + kvmi = kvmi_get(kvm); + if (!kvmi) + return; + + kvmi_remove_memslot(kvm, slot); + + kvmi_put(kvm); +} diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 42c8e6342fcf..3b432b37b17c 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -72,6 +72,7 @@ enum { KVMI_VCPU_EVENT_XSETBV = KVMI_VCPU_EVENT_ID(5), KVMI_VCPU_EVENT_DESCRIPTOR = KVMI_VCPU_EVENT_ID(6), KVMI_VCPU_EVENT_MSR = KVMI_VCPU_EVENT_ID(7), + KVMI_VCPU_EVENT_PF = KVMI_VCPU_EVENT_ID(8), KVMI_NEXT_VCPU_EVENT }; @@ -210,4 +211,13 @@ struct kvmi_vm_set_page_access { struct kvmi_page_access_entry entries[0]; }; +struct kvmi_vcpu_event_pf { + __u64 gva; + __u64 gpa; + __u8 access; + __u8 padding1; + __u16 padding2; + __u32 padding3; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index c9d6ee57d506..e36b574c264e 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -47,6 +47,11 @@ struct vcpu_reply { struct kvmi_vcpu_event_reply reply; }; +struct pf_ev { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_pf pf; +}; + struct vcpu_worker_data { struct kvm_vm *vm; int vcpu_id; @@ -54,6 +59,10 @@ struct vcpu_worker_data { bool restart_on_shutdown; }; +typedef void (*fct_pf_event)(struct kvm_vm *vm, struct kvmi_msg_hdr *hdr, + struct pf_ev *ev, + struct vcpu_reply *rpl); + enum { GUEST_TEST_NOOP = 0, GUEST_TEST_BP, @@ -61,6 +70,7 @@ enum { GUEST_TEST_DESCRIPTOR, GUEST_TEST_HYPERCALL, GUEST_TEST_MSR, + GUEST_TEST_PF, GUEST_TEST_XSETBV, }; @@ -114,6 +124,11 @@ static void guest_msr_test(void) wrmsr(MSR_MISC_FEATURES_ENABLES, msr); } +static void guest_pf_test(void) +{ + *((uint8_t *)test_gva) = GUEST_TEST_PF; +} + /* from fpu/internal.h */ static u64 xgetbv(u32 index) { @@ -174,6 +189,9 @@ static void guest_code(void) case GUEST_TEST_MSR: guest_msr_test(); break; + case GUEST_TEST_PF: + guest_pf_test(); + break; case GUEST_TEST_XSETBV: guest_xsetbv_test(); break; @@ -1738,6 +1756,63 @@ static void test_cmd_vm_set_page_access(struct kvm_vm *vm) set_page_access(gpa, full_access); } +static void test_pf(struct kvm_vm *vm, fct_pf_event cbk) +{ + __u16 event_id = KVMI_VCPU_EVENT_PF; + struct vcpu_worker_data data = { + .vm = vm, + .vcpu_id = VCPU_ID, + .test_id = GUEST_TEST_PF, + }; + struct kvmi_msg_hdr hdr; + struct vcpu_reply rpl = {}; + pthread_t vcpu_thread; + struct pf_ev ev; + + set_page_access(test_gpa, KVMI_PAGE_ACCESS_R); + enable_vcpu_event(vm, event_id); + + *((uint8_t *)test_hva) = ~GUEST_TEST_PF; + + vcpu_thread = start_vcpu_worker(&data); + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), event_id); + + pr_debug("PF event, gpa 0x%llx, gva 0x%llx, access 0x%x\n", + ev.pf.gpa, ev.pf.gva, ev.pf.access); + + TEST_ASSERT(ev.pf.gpa == test_gpa && ev.pf.gva == test_gva, + "Unexpected #PF event, gpa 0x%llx (expended 0x%lx), gva 0x%llx (expected 0x%lx)\n", + ev.pf.gpa, test_gpa, ev.pf.gva, test_gva); + + cbk(vm, &hdr, &ev, &rpl); + + wait_vcpu_worker(vcpu_thread); + + TEST_ASSERT(*((uint8_t *)test_hva) == GUEST_TEST_PF, + "Write failed, expected 0x%x, result 0x%x\n", + GUEST_TEST_PF, *((uint8_t *)test_hva)); + + disable_vcpu_event(vm, event_id); + set_page_access(test_gpa, KVMI_PAGE_ACCESS_R | + KVMI_PAGE_ACCESS_W | + KVMI_PAGE_ACCESS_X); +} + +static void cbk_test_event_pf(struct kvm_vm *vm, struct kvmi_msg_hdr *hdr, + struct pf_ev *ev, struct vcpu_reply *rpl) +{ + set_page_access(test_gpa, KVMI_PAGE_ACCESS_R | KVMI_PAGE_ACCESS_W); + + reply_to_event(hdr, &ev->vcpu_ev, KVMI_EVENT_ACTION_RETRY, + rpl, sizeof(*rpl)); +} + +static void test_event_pf(struct kvm_vm *vm) +{ + test_pf(vm, cbk_test_event_pf); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1771,6 +1846,7 @@ static void test_introspection(struct kvm_vm *vm) test_event_descriptor(vm); test_cmd_vcpu_control_msr(vm); test_cmd_vm_set_page_access(vm); + test_event_pf(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 079b355540b4..a228178ddba2 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -376,6 +376,8 @@ static void __kvmi_unhook(struct kvm *kvm) struct kvm_introspection *kvmi = KVMI(kvm); wait_for_completion_killable(&kvm->kvmi_complete); + + kvmi_arch_unhook(kvm); kvmi_sock_put(kvmi); } @@ -423,6 +425,8 @@ static int __kvmi_hook(struct kvm *kvm, if (!kvmi_sock_get(kvmi, hook->fd)) return -EINVAL; + kvmi_arch_hook(kvm); + return 0; } @@ -1054,3 +1058,115 @@ int kvmi_set_gfn_access(struct kvm *kvm, gfn_t gfn, u8 access) return err; } + +static int kvmi_get_gfn_access(struct kvm_introspection *kvmi, const gfn_t gfn, + u8 *access) +{ + struct kvmi_mem_access *m; + + read_lock(&kvmi->access_tree_lock); + m = __kvmi_get_gfn_access(kvmi, gfn); + if (m) + *access = m->access; + read_unlock(&kvmi->access_tree_lock); + + return m ? 0 : -1; +} + +bool kvmi_restricted_page_access(struct kvm_introspection *kvmi, gpa_t gpa, + u8 access) +{ + u8 allowed_access; + int err; + + err = kvmi_get_gfn_access(kvmi, gpa_to_gfn(gpa), &allowed_access); + if (err) + return false; + + /* + * We want to be notified only for violations involving access + * bits that we've specifically cleared + */ + if (access & (~allowed_access)) + return true; + + return false; +} + +bool kvmi_pf_event(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, u8 access) +{ + bool ret = false; + u32 action; + + if (!kvmi_restricted_page_access(KVMI(vcpu->kvm), gpa, access)) + return true; + + action = kvmi_msg_send_vcpu_pf(vcpu, gpa, gva, access); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + ret = true; + break; + case KVMI_EVENT_ACTION_RETRY: + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + } + + return ret; +} + +void kvmi_add_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, + unsigned long npages) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + gfn_t start = slot->base_gfn; + gfn_t end = start + npages; + int idx; + + idx = srcu_read_lock(&kvm->srcu); + spin_lock(&kvm->mmu_lock); + read_lock(&kvmi->access_tree_lock); + + while (start < end) { + struct kvmi_mem_access *m; + + m = __kvmi_get_gfn_access(kvmi, start); + if (m) + kvmi_arch_update_page_tracking(kvm, slot, m); + start++; + } + + read_unlock(&kvmi->access_tree_lock); + spin_unlock(&kvm->mmu_lock); + srcu_read_unlock(&kvm->srcu, idx); +} + +void kvmi_remove_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) +{ + struct kvm_introspection *kvmi = KVMI(kvm); + gfn_t start = slot->base_gfn; + gfn_t end = start + slot->npages; + int idx; + + idx = srcu_read_lock(&kvm->srcu); + spin_lock(&kvm->mmu_lock); + write_lock(&kvmi->access_tree_lock); + + while (start < end) { + struct kvmi_mem_access *m; + + m = __kvmi_get_gfn_access(kvmi, start); + if (m) { + u8 prev_access = m->access; + + m->access = full_access; + kvmi_arch_update_page_tracking(kvm, slot, m); + m->access = prev_access; + } + start++; + } + + write_unlock(&kvmi->access_tree_lock); + spin_unlock(&kvm->mmu_lock); + srcu_read_unlock(&kvm->srcu, idx); +} diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 41720b194458..bf6545e66425 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -63,6 +63,7 @@ int kvmi_msg_vcpu_reply(const struct kvmi_vcpu_msg_job *job, u32 kvmi_msg_send_vcpu_pause(struct kvm_vcpu *vcpu); u32 kvmi_msg_send_vcpu_hypercall(struct kvm_vcpu *vcpu); u32 kvmi_msg_send_vcpu_bp(struct kvm_vcpu *vcpu, u64 gpa, u8 insn_len); +u32 kvmi_msg_send_vcpu_pf(struct kvm_vcpu *vcpu, u64 gpa, u64 gva, u8 access); /* kvmi.c */ void *kvmi_msg_alloc(void); @@ -93,6 +94,10 @@ int kvmi_cmd_write_physical(struct kvm *kvm, u64 gpa, size_t size, const void *buf); int kvmi_cmd_vcpu_pause(struct kvm_vcpu *vcpu, bool wait); int kvmi_set_gfn_access(struct kvm *kvm, gfn_t gfn, u8 access); +bool kvmi_pf_event(struct kvm_vcpu *vcpu, gpa_t gpa, gva_t gva, u8 access); +void kvmi_add_memslot(struct kvm *kvm, struct kvm_memory_slot *slot, + unsigned long npages); +void kvmi_remove_memslot(struct kvm *kvm, struct kvm_memory_slot *slot); /* arch */ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported); @@ -114,5 +119,7 @@ void kvmi_arch_send_pending_event(struct kvm_vcpu *vcpu); void kvmi_arch_update_page_tracking(struct kvm *kvm, struct kvm_memory_slot *slot, struct kvmi_mem_access *m); +void kvmi_arch_hook(struct kvm *kvm); +void kvmi_arch_unhook(struct kvm *kvm); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index a3d6d3df3078..276b898912fd 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -870,3 +870,22 @@ u32 kvmi_msg_send_vcpu_bp(struct kvm_vcpu *vcpu, u64 gpa, u8 insn_len) return action; } + +u32 kvmi_msg_send_vcpu_pf(struct kvm_vcpu *vcpu, u64 gpa, u64 gva, u8 access) +{ + struct kvmi_vcpu_event_pf e; + u32 action; + int err; + + memset(&e, 0, sizeof(e)); + e.gpa = gpa; + e.gva = gva; + e.access = access; + + err = kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_PF, &e, sizeof(e), + NULL, 0, &action); + if (err) + return KVMI_EVENT_ACTION_CONTINUE; + + return action; +} From patchwork Wed Nov 25 09:35:55 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930809 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 8C469C64E7C for ; Wed, 25 Nov 2020 09:42:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3853A206F9 for ; Wed, 25 Nov 2020 09:42:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728229AbgKYJl7 (ORCPT ); Wed, 25 Nov 2020 04:41:59 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57200 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727838AbgKYJl6 (ORCPT ); Wed, 25 Nov 2020 04:41:58 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 3B879305D3E9; Wed, 25 Nov 2020 11:35:55 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 1F6A83072784; Wed, 25 Nov 2020 11:35:55 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 76/81] KVM: introspection: extend KVMI_GET_VERSION with struct kvmi_features Date: Wed, 25 Nov 2020 11:35:55 +0200 Message-Id: <20201125093600.2766-77-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This is used by the introspection tool to check the hardware support for the single step feature. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 15 +++++++++++++-- arch/x86/include/uapi/asm/kvmi.h | 5 +++++ arch/x86/kvm/kvmi.c | 5 +++++ include/uapi/linux/kvmi.h | 1 + tools/testing/selftests/kvm/x86_64/kvmi_test.c | 6 ++++++ virt/kvm/introspection/kvmi_int.h | 1 + virt/kvm/introspection/kvmi_msg.c | 2 ++ 7 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index bdcc9066ae28..991922897f1d 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -243,10 +243,21 @@ The vCPU commands start with:: struct kvmi_get_version_reply { __u32 version; __u32 max_msg_size; + struct kvmi_features features; }; -Returns the introspection API version and the largest accepted message -size (useful for variable length messages). +For x86 + +:: + + struct kvmi_features { + __u8 singlestep; + __u8 padding[7]; + }; + +Returns the introspection API version, the largest accepted message size +(useful for variable length messages) and some of the hardware supported +features. This command is always allowed and successful. diff --git a/arch/x86/include/uapi/asm/kvmi.h b/arch/x86/include/uapi/asm/kvmi.h index 705e90c2137a..54d21e3c18c4 100644 --- a/arch/x86/include/uapi/asm/kvmi.h +++ b/arch/x86/include/uapi/asm/kvmi.h @@ -159,4 +159,9 @@ struct kvmi_vcpu_event_msr_reply { __u64 new_val; }; +struct kvmi_features { + __u8 singlestep; + __u8 padding[7]; +}; + #endif /* _UAPI_ASM_X86_KVMI_H */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index cd64762643d6..e0302883aec5 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -1081,3 +1081,8 @@ static void kvmi_track_flush_slot(struct kvm *kvm, struct kvm_memory_slot *slot, kvmi_put(kvm); } + +void kvmi_arch_features(struct kvmi_features *feat) +{ + feat->singlestep = !!kvm_x86_ops.control_singlestep; +} diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 3b432b37b17c..43631ed2b06c 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -103,6 +103,7 @@ struct kvmi_error_code { struct kvmi_get_version_reply { __u32 version; __u32 max_msg_size; + struct kvmi_features features; }; struct kvmi_vm_check_command { diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index e36b574c264e..9984b0247ae9 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -59,6 +59,8 @@ struct vcpu_worker_data { bool restart_on_shutdown; }; +static struct kvmi_features features; + typedef void (*fct_pf_event)(struct kvm_vm *vm, struct kvmi_msg_hdr *hdr, struct pf_ev *ev, struct vcpu_reply *rpl); @@ -443,6 +445,10 @@ static void test_cmd_get_version(void) pr_debug("KVMI version: %u\n", rpl.version); pr_debug("Max message size: %u\n", rpl.max_msg_size); + + features = rpl.features; + + pr_debug("singlestep support: %u\n", features.singlestep); } static void cmd_vm_check_command(__u16 id, int expected_err) diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index bf6545e66425..a51e7e4ed511 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -121,5 +121,6 @@ void kvmi_arch_update_page_tracking(struct kvm *kvm, struct kvmi_mem_access *m); void kvmi_arch_hook(struct kvm *kvm); void kvmi_arch_unhook(struct kvm *kvm); +void kvmi_arch_features(struct kvmi_features *feat); #endif diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index 276b898912fd..ee887ade62cb 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -134,6 +134,8 @@ static int handle_get_version(struct kvm_introspection *kvmi, rpl.version = kvmi_version(); rpl.max_msg_size = KVMI_MAX_MSG_SIZE; + kvmi_arch_features(&rpl.features); + return kvmi_msg_vm_reply(kvmi, msg, 0, &rpl, sizeof(rpl)); } From patchwork Wed Nov 25 09:35:56 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930821 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 2A07DC8300E for ; Wed, 25 Nov 2020 09:42:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CB060206F9 for ; Wed, 25 Nov 2020 09:42:06 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728583AbgKYJmG (ORCPT ); Wed, 25 Nov 2020 04:42:06 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57140 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727682AbgKYJmC (ORCPT ); Wed, 25 Nov 2020 04:42:02 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 62D96305D3EA; Wed, 25 Nov 2020 11:35:55 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 3EF253072785; Wed, 25 Nov 2020 11:35:55 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 77/81] KVM: introspection: add KVMI_VCPU_CONTROL_SINGLESTEP Date: Wed, 25 Nov 2020 11:35:56 +0200 Message-Id: <20201125093600.2766-78-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu The next commit that adds the KVMI_VCPU_EVENT_SINGLESTEP event will make this command more useful. Signed-off-by: Nicușor Cîțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 33 +++++++++++ arch/x86/kvm/kvmi.c | 14 ++++- arch/x86/kvm/kvmi_msg.c | 56 +++++++++++++++---- arch/x86/kvm/x86.c | 12 +++- include/linux/kvmi_host.h | 7 +++ include/uapi/linux/kvmi.h | 30 ++++++---- .../testing/selftests/kvm/x86_64/kvmi_test.c | 39 +++++++++++++ virt/kvm/introspection/kvmi.c | 22 ++++++++ virt/kvm/introspection/kvmi_int.h | 2 + 9 files changed, 187 insertions(+), 28 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 991922897f1d..c8822912d8c8 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -791,6 +791,7 @@ exception. * -KVM_EINVAL - the selected vCPU is invalid * -KVM_EINVAL - the padding is not zero * -KVM_EAGAIN - the selected vCPU can't be introspected yet +* -KVM_EBUSY - the vCPU is switched in singlestep mode (*KVMI_VCPU_CONTROL_SINGLESTEP*) * -KVM_EBUSY - another *KVMI_VCPU_INJECT_EXCEPTION*-*KVMI_VCPU_EVENT_TRAP* pair is in progress @@ -1036,6 +1037,38 @@ In order to 'forget' an address, all three bits ('rwx') must be set. * -KVM_EAGAIN - the selected vCPU can't be introspected yet * -KVM_ENOMEM - there is not enough memory to add the page tracking structures +24. KVMI_VCPU_CONTROL_SINGLESTEP +-------------------------------- + +:Architectures: x86 (vmx) +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_control_singlestep { + __u8 enable; + __u8 padding[7]; + }; + +:Returns: + +:: + + struct kvmi_error_code; + +Enables/disables singlestep for the selected vCPU. + +The introspection tool should use *KVMI_GET_VERSION*, to check +if the hardware supports singlestep (see **KVMI_GET_VERSION**). + +:Errors: + +* -KVM_EOPNOTSUPP - the hardware doesn't support singlestep +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet + Events ====== diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index e0302883aec5..31a2de24de29 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -776,7 +776,9 @@ void kvmi_enter_guest(struct kvm_vcpu *vcpu) if (kvmi) { vcpui = VCPUI(vcpu); - if (vcpui->arch.exception.pending) + if (vcpui->singlestep.loop) + kvmi_arch_start_singlestep(vcpu); + else if (vcpui->arch.exception.pending) kvmi_inject_pending_exception(vcpu); kvmi_put(vcpu->kvm); @@ -1086,3 +1088,13 @@ void kvmi_arch_features(struct kvmi_features *feat) { feat->singlestep = !!kvm_x86_ops.control_singlestep; } + +void kvmi_arch_start_singlestep(struct kvm_vcpu *vcpu) +{ + kvm_x86_ops.control_singlestep(vcpu, true); +} + +void kvmi_arch_stop_singlestep(struct kvm_vcpu *vcpu) +{ + kvm_x86_ops.control_singlestep(vcpu, false); +} diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index c961c5367a13..8b59f9f73c5d 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -166,7 +166,8 @@ static int handle_vcpu_inject_exception(const struct kvmi_vcpu_msg_job *job, else if (req->padding1 || req->padding2) ec = -KVM_EINVAL; else if (VCPUI(vcpu)->arch.exception.pending || - VCPUI(vcpu)->arch.exception.send_event) + VCPUI(vcpu)->arch.exception.send_event || + VCPUI(vcpu)->singlestep.loop) ec = -KVM_EBUSY; else ec = kvmi_arch_cmd_vcpu_inject_exception(vcpu, req); @@ -276,18 +277,49 @@ static int handle_vcpu_control_msr(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); } +static int handle_vcpu_control_singlestep(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vcpu_control_singlestep *req = _req; + struct kvm_vcpu *vcpu = job->vcpu; + int ec = 0; + + if (non_zero_padding(req->padding, ARRAY_SIZE(req->padding)) || + req->enable > 1) { + ec = -KVM_EINVAL; + goto reply; + } + + if (!kvm_x86_ops.control_singlestep) { + ec = -KVM_EOPNOTSUPP; + goto reply; + } + + if (req->enable) + kvmi_arch_start_singlestep(vcpu); + else + kvmi_arch_stop_singlestep(vcpu); + + VCPUI(vcpu)->singlestep.loop = !!req->enable; + +reply: + return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { - [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, - [KVMI_VCPU_CONTROL_MSR] = handle_vcpu_control_msr, - [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, - [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, - [KVMI_VCPU_GET_MTRR_TYPE] = handle_vcpu_get_mtrr_type, - [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, - [KVMI_VCPU_GET_XCR] = handle_vcpu_get_xcr, - [KVMI_VCPU_GET_XSAVE] = handle_vcpu_get_xsave, - [KVMI_VCPU_INJECT_EXCEPTION] = handle_vcpu_inject_exception, - [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, - [KVMI_VCPU_SET_XSAVE] = handle_vcpu_set_xsave, + [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, + [KVMI_VCPU_CONTROL_MSR] = handle_vcpu_control_msr, + [KVMI_VCPU_CONTROL_SINGLESTEP] = handle_vcpu_control_singlestep, + [KVMI_VCPU_GET_CPUID] = handle_vcpu_get_cpuid, + [KVMI_VCPU_GET_INFO] = handle_vcpu_get_info, + [KVMI_VCPU_GET_MTRR_TYPE] = handle_vcpu_get_mtrr_type, + [KVMI_VCPU_GET_REGISTERS] = handle_vcpu_get_registers, + [KVMI_VCPU_GET_XCR] = handle_vcpu_get_xcr, + [KVMI_VCPU_GET_XSAVE] = handle_vcpu_get_xsave, + [KVMI_VCPU_INJECT_EXCEPTION] = handle_vcpu_inject_exception, + [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, + [KVMI_VCPU_SET_XSAVE] = handle_vcpu_set_xsave, }; kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 0ffa2e73284e..cc7292ee3b2d 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -8957,9 +8957,15 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) goto out; } - inject_pending_event(vcpu, &req_immediate_exit); - if (req_int_win) - kvm_x86_ops.enable_irq_window(vcpu); + if (!kvmi_vcpu_running_singlestep(vcpu)) { + /* + * We cannot inject events during single-stepping. + * Try again later. + */ + inject_pending_event(vcpu, &req_immediate_exit); + if (req_int_win) + kvm_x86_ops.enable_irq_window(vcpu); + } if (kvm_lapic_enabled(vcpu)) { update_cr8_intercept(vcpu); diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index 14ac075a3ea9..e2103ab9d0d5 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -33,6 +33,10 @@ struct kvm_vcpu_introspection { bool waiting_for_reply; unsigned long *ev_enable_mask; + + struct { + bool loop; + } singlestep; }; struct kvm_introspection { @@ -76,6 +80,7 @@ int kvmi_ioctl_preunhook(struct kvm *kvm); void kvmi_handle_requests(struct kvm_vcpu *vcpu); bool kvmi_hypercall_event(struct kvm_vcpu *vcpu); bool kvmi_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len); +bool kvmi_vcpu_running_singlestep(struct kvm_vcpu *vcpu); #else @@ -90,6 +95,8 @@ static inline void kvmi_handle_requests(struct kvm_vcpu *vcpu) { } static inline bool kvmi_hypercall_event(struct kvm_vcpu *vcpu) { return false; } static inline bool kvmi_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len) { return true; } +static inline bool kvmi_vcpu_running_singlestep(struct kvm_vcpu *vcpu) + { return false; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 43631ed2b06c..91126607b7eb 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -38,18 +38,19 @@ enum { enum { KVMI_VCPU_EVENT = KVMI_VCPU_MESSAGE_ID(0), - KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), - KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2), - KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3), - KVMI_VCPU_SET_REGISTERS = KVMI_VCPU_MESSAGE_ID(4), - KVMI_VCPU_GET_CPUID = KVMI_VCPU_MESSAGE_ID(5), - KVMI_VCPU_CONTROL_CR = KVMI_VCPU_MESSAGE_ID(6), - KVMI_VCPU_INJECT_EXCEPTION = KVMI_VCPU_MESSAGE_ID(7), - KVMI_VCPU_GET_XCR = KVMI_VCPU_MESSAGE_ID(8), - KVMI_VCPU_GET_XSAVE = KVMI_VCPU_MESSAGE_ID(9), - KVMI_VCPU_SET_XSAVE = KVMI_VCPU_MESSAGE_ID(10), - KVMI_VCPU_GET_MTRR_TYPE = KVMI_VCPU_MESSAGE_ID(11), - KVMI_VCPU_CONTROL_MSR = KVMI_VCPU_MESSAGE_ID(12), + KVMI_VCPU_GET_INFO = KVMI_VCPU_MESSAGE_ID(1), + KVMI_VCPU_CONTROL_EVENTS = KVMI_VCPU_MESSAGE_ID(2), + KVMI_VCPU_GET_REGISTERS = KVMI_VCPU_MESSAGE_ID(3), + KVMI_VCPU_SET_REGISTERS = KVMI_VCPU_MESSAGE_ID(4), + KVMI_VCPU_GET_CPUID = KVMI_VCPU_MESSAGE_ID(5), + KVMI_VCPU_CONTROL_CR = KVMI_VCPU_MESSAGE_ID(6), + KVMI_VCPU_INJECT_EXCEPTION = KVMI_VCPU_MESSAGE_ID(7), + KVMI_VCPU_GET_XCR = KVMI_VCPU_MESSAGE_ID(8), + KVMI_VCPU_GET_XSAVE = KVMI_VCPU_MESSAGE_ID(9), + KVMI_VCPU_SET_XSAVE = KVMI_VCPU_MESSAGE_ID(10), + KVMI_VCPU_GET_MTRR_TYPE = KVMI_VCPU_MESSAGE_ID(11), + KVMI_VCPU_CONTROL_MSR = KVMI_VCPU_MESSAGE_ID(12), + KVMI_VCPU_CONTROL_SINGLESTEP = KVMI_VCPU_MESSAGE_ID(13), KVMI_NEXT_VCPU_MESSAGE }; @@ -221,4 +222,9 @@ struct kvmi_vcpu_event_pf { __u32 padding3; }; +struct kvmi_vcpu_control_singlestep { + __u8 enable; + __u8 padding[7]; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index 9984b0247ae9..d959216aac9d 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1819,6 +1819,44 @@ static void test_event_pf(struct kvm_vm *vm) test_pf(vm, cbk_test_event_pf); } +static void cmd_vcpu_singlestep(struct kvm_vm *vm, __u8 enable, + int expected_err) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_control_singlestep cmd; + } req = {}; + + req.cmd.enable = enable; + + test_vcpu0_command(vm, KVMI_VCPU_CONTROL_SINGLESTEP, + &req.hdr, sizeof(req), NULL, 0, expected_err); +} + +static void test_supported_singlestep(struct kvm_vm *vm) +{ + __u8 disable = 0, enable = 1, enable_inval = 2; + + cmd_vcpu_singlestep(vm, enable, 0); + cmd_vcpu_singlestep(vm, disable, 0); + + cmd_vcpu_singlestep(vm, enable_inval, -KVM_EINVAL); +} + +static void test_unsupported_singlestep(struct kvm_vm *vm) +{ + cmd_vcpu_singlestep(vm, 1, -KVM_EOPNOTSUPP); +} + +static void test_cmd_vcpu_control_singlestep(struct kvm_vm *vm) +{ + if (features.singlestep) + test_supported_singlestep(vm); + else + test_unsupported_singlestep(vm); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1853,6 +1891,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vcpu_control_msr(vm); test_cmd_vm_set_page_access(vm); test_event_pf(vm); + test_cmd_vcpu_control_singlestep(vm); unhook_introspection(vm); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index a228178ddba2..f620396d1887 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -436,6 +436,11 @@ static void kvmi_job_release_vcpu(struct kvm_vcpu *vcpu, void *ctx) atomic_set(&vcpui->pause_requests, 0); vcpui->waiting_for_reply = false; + + if (vcpui->singlestep.loop) { + kvmi_arch_stop_singlestep(vcpu); + vcpui->singlestep.loop = false; + } } static void kvmi_release_vcpus(struct kvm *kvm) @@ -1170,3 +1175,20 @@ void kvmi_remove_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) spin_unlock(&kvm->mmu_lock); srcu_read_unlock(&kvm->srcu, idx); } + +bool kvmi_vcpu_running_singlestep(struct kvm_vcpu *vcpu) +{ + struct kvm_introspection *kvmi; + bool ret; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return false; + + ret = VCPUI(vcpu)->singlestep.loop; + + kvmi_put(vcpu->kvm); + + return ret; +} +EXPORT_SYMBOL(kvmi_vcpu_running_singlestep); diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index a51e7e4ed511..4815fa61b136 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -122,5 +122,7 @@ void kvmi_arch_update_page_tracking(struct kvm *kvm, void kvmi_arch_hook(struct kvm *kvm); void kvmi_arch_unhook(struct kvm *kvm); void kvmi_arch_features(struct kvmi_features *feat); +void kvmi_arch_start_singlestep(struct kvm_vcpu *vcpu); +void kvmi_arch_stop_singlestep(struct kvm_vcpu *vcpu); #endif From patchwork Wed Nov 25 09:35:57 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930951 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 61490C83019 for ; Wed, 25 Nov 2020 09:43:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1F3E72083E for ; Wed, 25 Nov 2020 09:43:02 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728799AbgKYJm5 (ORCPT ); Wed, 25 Nov 2020 04:42:57 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57198 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727780AbgKYJl6 (ORCPT ); Wed, 25 Nov 2020 04:41:58 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id 8D162305D3E6; Wed, 25 Nov 2020 11:35:55 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 6568E3072784; Wed, 25 Nov 2020 11:35:55 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?b?TmljdciZb3IgQ8OuyJt1?= , =?utf-8?q?Adalbert?= =?utf-8?q?_Laz=C4=83r?= Subject: [PATCH v10 78/81] KVM: introspection: add KVMI_VCPU_EVENT_SINGLESTEP Date: Wed, 25 Nov 2020 11:35:57 +0200 Message-Id: <20201125093600.2766-79-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Nicușor Cîțu This event is sent after each instruction when the singlestep has been enabled for a vCPU. Signed-off-by: Nicușor Cîțu Co-developed-by: Adalbert Lazăr Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 31 +++++++++++ arch/x86/kvm/kvmi.c | 1 + arch/x86/kvm/kvmi_msg.c | 6 +++ arch/x86/kvm/vmx/vmx.c | 6 +++ include/linux/kvmi_host.h | 4 ++ include/uapi/linux/kvmi.h | 6 +++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 54 +++++++++++++++++-- virt/kvm/introspection/kvmi.c | 43 +++++++++++++++ virt/kvm/introspection/kvmi_int.h | 1 + virt/kvm/introspection/kvmi_msg.c | 17 ++++++ 10 files changed, 166 insertions(+), 3 deletions(-) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index c8822912d8c8..4b2e7809f052 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -565,6 +565,7 @@ because these are sent as a result of certain commands (but they can be disallowed by the device manager) :: KVMI_VCPU_EVENT_PAUSE + KVMI_VCPU_EVENT_SINGLESTEP KVMI_VCPU_EVENT_TRAP The VM events (e.g. *KVMI_VM_EVENT_UNHOOK*) are controlled with @@ -1063,8 +1064,12 @@ Enables/disables singlestep for the selected vCPU. The introspection tool should use *KVMI_GET_VERSION*, to check if the hardware supports singlestep (see **KVMI_GET_VERSION**). +After every instruction, a *KVMI_VCPU_EVENT_SINGLESTEP* event is sent +to the introspection tool. + :Errors: +* -KVM_EPERM - the *KVMI_VCPU_EVENT_SINGLESTEP* event is disallowed * -KVM_EOPNOTSUPP - the hardware doesn't support singlestep * -KVM_EINVAL - the padding is not zero * -KVM_EAGAIN - the selected vCPU can't be introspected yet @@ -1508,3 +1513,29 @@ emulation). The *RETRY* action is used by the introspection tool to retry the execution of the current instruction, usually because it changed the instruction pointer or the page restrictions. + +11. KVMI_VCPU_EVENT_SINGLESTEP +------------------------------ + +:Architectures: x86 +:Versions: >= 1 +:Actions: CONTINUE, CRASH +:Parameters: + +:: + + struct kvmi_vcpu_event; + +:Returns: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_event_reply; + struct kvmi_vcpu_event_singlestep { + __u8 failed; + __u8 padding[7]; + }; + +This event is sent after each instruction, as long as the singlestep is +enabled for the current vCPU (see **KVMI_VCPU_CONTROL_SINGLESTEP**). diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index 31a2de24de29..b010d2369756 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -18,6 +18,7 @@ void kvmi_arch_init_vcpu_events_mask(unsigned long *supported) set_bit(KVMI_VCPU_EVENT_DESCRIPTOR, supported); set_bit(KVMI_VCPU_EVENT_MSR, supported); set_bit(KVMI_VCPU_EVENT_PF, supported); + set_bit(KVMI_VCPU_EVENT_SINGLESTEP, supported); set_bit(KVMI_VCPU_EVENT_TRAP, supported); set_bit(KVMI_VCPU_EVENT_XSETBV, supported); } diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index 8b59f9f73c5d..c4b43b3b7b92 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -285,6 +285,12 @@ static int handle_vcpu_control_singlestep(const struct kvmi_vcpu_msg_job *job, struct kvm_vcpu *vcpu = job->vcpu; int ec = 0; + if (!kvmi_is_event_allowed(KVMI(vcpu->kvm), + KVMI_VCPU_EVENT_SINGLESTEP)) { + ec = -KVM_EPERM; + goto reply; + } + if (non_zero_padding(req->padding, ARRAY_SIZE(req->padding)) || req->enable > 1) { ec = -KVM_EINVAL; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 01d18c9243bc..4804eaa012de 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -5614,6 +5614,7 @@ static int handle_invalid_op(struct kvm_vcpu *vcpu) static int handle_monitor_trap(struct kvm_vcpu *vcpu) { + kvmi_singlestep_done(vcpu); return 1; } @@ -6142,6 +6143,11 @@ static int vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) } } + if (kvmi_vcpu_running_singlestep(vcpu) && + exit_reason != EXIT_REASON_EPT_VIOLATION && + exit_reason != EXIT_REASON_MONITOR_TRAP_FLAG) + kvmi_singlestep_failed(vcpu); + if (exit_fastpath != EXIT_FASTPATH_NONE) return 1; diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index e2103ab9d0d5..ec38e434c8e9 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -81,6 +81,8 @@ void kvmi_handle_requests(struct kvm_vcpu *vcpu); bool kvmi_hypercall_event(struct kvm_vcpu *vcpu); bool kvmi_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len); bool kvmi_vcpu_running_singlestep(struct kvm_vcpu *vcpu); +void kvmi_singlestep_done(struct kvm_vcpu *vcpu); +void kvmi_singlestep_failed(struct kvm_vcpu *vcpu); #else @@ -97,6 +99,8 @@ static inline bool kvmi_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len) { return true; } static inline bool kvmi_vcpu_running_singlestep(struct kvm_vcpu *vcpu) { return false; } +static inline void kvmi_singlestep_done(struct kvm_vcpu *vcpu) { } +static inline void kvmi_singlestep_failed(struct kvm_vcpu *vcpu) { } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 91126607b7eb..9c646229a25a 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -74,6 +74,7 @@ enum { KVMI_VCPU_EVENT_DESCRIPTOR = KVMI_VCPU_EVENT_ID(6), KVMI_VCPU_EVENT_MSR = KVMI_VCPU_EVENT_ID(7), KVMI_VCPU_EVENT_PF = KVMI_VCPU_EVENT_ID(8), + KVMI_VCPU_EVENT_SINGLESTEP = KVMI_VCPU_EVENT_ID(9), KVMI_NEXT_VCPU_EVENT }; @@ -227,4 +228,9 @@ struct kvmi_vcpu_control_singlestep { __u8 padding[7]; }; +struct kvmi_vcpu_event_singlestep { + __u8 failed; + __u8 padding[7]; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index d959216aac9d..c6f41f54f9b0 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1834,12 +1834,60 @@ static void cmd_vcpu_singlestep(struct kvm_vm *vm, __u8 enable, &req.hdr, sizeof(req), NULL, 0, expected_err); } +static void __control_singlestep(bool enable) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_control_singlestep cmd; + } req = {}; + int r; + + req.cmd.enable = enable ? 1 : 0; + + r = __do_vcpu0_command(KVMI_VCPU_CONTROL_SINGLESTEP, + &req.hdr, sizeof(req), NULL, 0); + TEST_ASSERT(r == 0, + "KVMI_VCPU_CONTROL_SINGLESTEP failed, error %d(%s)\n", + -r, kvm_strerror(-r)); +} + +static void test_singlestep_event(__u16 event_id) +{ + struct { + struct vcpu_event vcpu_ev; + struct kvmi_vcpu_event_singlestep singlestep; + } ev; + bool enable = true, disable = false; + struct vcpu_reply rpl = { }; + struct kvmi_msg_hdr hdr; + + __control_singlestep(enable); + + receive_vcpu_event(&hdr, &ev.vcpu_ev, sizeof(ev), event_id); + + pr_debug("SINGLESTEP event, rip 0x%llx success %d\n", + ev.vcpu_ev.common.arch.regs.rip, !ev.singlestep.failed); + TEST_ASSERT(!ev.singlestep.failed, "Singlestep failed"); + + __control_singlestep(disable); + + reply_to_event(&hdr, &ev.vcpu_ev, KVMI_EVENT_ACTION_CONTINUE, + &rpl, sizeof(rpl)); +} + static void test_supported_singlestep(struct kvm_vm *vm) { - __u8 disable = 0, enable = 1, enable_inval = 2; + struct vcpu_worker_data data = {.vm = vm, .vcpu_id = VCPU_ID }; + __u16 event_id = KVMI_VCPU_EVENT_SINGLESTEP; + __u8 enable_inval = 2; + pthread_t vcpu_thread; - cmd_vcpu_singlestep(vm, enable, 0); - cmd_vcpu_singlestep(vm, disable, 0); + enable_vcpu_event(vm, event_id); + vcpu_thread = start_vcpu_worker(&data); + test_singlestep_event(event_id); + wait_vcpu_worker(vcpu_thread); + disable_vcpu_event(vm, event_id); cmd_vcpu_singlestep(vm, enable_inval, -KVM_EINVAL); } diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index f620396d1887..3057f9f343c0 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -1192,3 +1192,46 @@ bool kvmi_vcpu_running_singlestep(struct kvm_vcpu *vcpu) return ret; } EXPORT_SYMBOL(kvmi_vcpu_running_singlestep); + +static void kvmi_singlestep_event(struct kvm_vcpu *vcpu, bool success) +{ + u32 action; + + action = kvmi_msg_send_vcpu_singlestep(vcpu, success); + switch (action) { + case KVMI_EVENT_ACTION_CONTINUE: + break; + default: + kvmi_handle_common_event_actions(vcpu, action); + } +} + +static void kvmi_handle_singlestep_exit(struct kvm_vcpu *vcpu, bool success) +{ + struct kvm_vcpu_introspection *vcpui; + struct kvm_introspection *kvmi; + struct kvm *kvm = vcpu->kvm; + + kvmi = kvmi_get(kvm); + if (!kvmi) + return; + + vcpui = VCPUI(vcpu); + + if (vcpui->singlestep.loop) + kvmi_singlestep_event(vcpu, success); + + kvmi_put(kvm); +} + +void kvmi_singlestep_done(struct kvm_vcpu *vcpu) +{ + kvmi_handle_singlestep_exit(vcpu, true); +} +EXPORT_SYMBOL(kvmi_singlestep_done); + +void kvmi_singlestep_failed(struct kvm_vcpu *vcpu) +{ + kvmi_handle_singlestep_exit(vcpu, false); +} +EXPORT_SYMBOL(kvmi_singlestep_failed); diff --git a/virt/kvm/introspection/kvmi_int.h b/virt/kvm/introspection/kvmi_int.h index 4815fa61b136..5f4bcdf27d6a 100644 --- a/virt/kvm/introspection/kvmi_int.h +++ b/virt/kvm/introspection/kvmi_int.h @@ -64,6 +64,7 @@ u32 kvmi_msg_send_vcpu_pause(struct kvm_vcpu *vcpu); u32 kvmi_msg_send_vcpu_hypercall(struct kvm_vcpu *vcpu); u32 kvmi_msg_send_vcpu_bp(struct kvm_vcpu *vcpu, u64 gpa, u8 insn_len); u32 kvmi_msg_send_vcpu_pf(struct kvm_vcpu *vcpu, u64 gpa, u64 gva, u8 access); +u32 kvmi_msg_send_vcpu_singlestep(struct kvm_vcpu *vcpu, bool success); /* kvmi.c */ void *kvmi_msg_alloc(void); diff --git a/virt/kvm/introspection/kvmi_msg.c b/virt/kvm/introspection/kvmi_msg.c index ee887ade62cb..fa0db00570f4 100644 --- a/virt/kvm/introspection/kvmi_msg.c +++ b/virt/kvm/introspection/kvmi_msg.c @@ -891,3 +891,20 @@ u32 kvmi_msg_send_vcpu_pf(struct kvm_vcpu *vcpu, u64 gpa, u64 gva, u8 access) return action; } + +u32 kvmi_msg_send_vcpu_singlestep(struct kvm_vcpu *vcpu, bool success) +{ + struct kvmi_vcpu_event_singlestep e; + u32 action; + int err; + + memset(&e, 0, sizeof(e)); + e.failed = success ? 0 : 1; + + err = kvmi_send_vcpu_event(vcpu, KVMI_VCPU_EVENT_SINGLESTEP, + &e, sizeof(e), NULL, 0, &action); + if (err) + return KVMI_EVENT_ACTION_CONTINUE; + + return action; +} From patchwork Wed Nov 25 09:35:58 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930893 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id A2199C83016 for ; Wed, 25 Nov 2020 09:42:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 5CD562083E for ; Wed, 25 Nov 2020 09:42:42 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728702AbgKYJmi (ORCPT ); Wed, 25 Nov 2020 04:42:38 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57144 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728507AbgKYJmE (ORCPT ); Wed, 25 Nov 2020 04:42:04 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id AE036305D3EB; Wed, 25 Nov 2020 11:35:55 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id 8F65C3072785; Wed, 25 Nov 2020 11:35:55 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Adalbert_Laz=C4=83r?= Subject: [PATCH v10 79/81] KVM: introspection: add KVMI_VCPU_TRANSLATE_GVA Date: Wed, 25 Nov 2020 11:35:58 +0200 Message-Id: <20201125093600.2766-80-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org This helps the introspection tool with the GVA to GPA translations without the need to read or monitor the guest page tables. Signed-off-by: Adalbert Lazăr --- Documentation/virt/kvm/kvmi.rst | 32 +++++++++++++++++++ arch/x86/kvm/kvmi_msg.c | 15 +++++++++ include/uapi/linux/kvmi.h | 9 ++++++ .../testing/selftests/kvm/x86_64/kvmi_test.c | 30 +++++++++++++++++ 4 files changed, 86 insertions(+) diff --git a/Documentation/virt/kvm/kvmi.rst b/Documentation/virt/kvm/kvmi.rst index 4b2e7809f052..1a33d009ad49 100644 --- a/Documentation/virt/kvm/kvmi.rst +++ b/Documentation/virt/kvm/kvmi.rst @@ -1074,6 +1074,38 @@ to the introspection tool. * -KVM_EINVAL - the padding is not zero * -KVM_EAGAIN - the selected vCPU can't be introspected yet +25. KVMI_VCPU_TRANSLATE_GVA +--------------------------- + +:Architectures: x86 +:Versions: >= 1 +:Parameters: + +:: + + struct kvmi_vcpu_hdr; + struct kvmi_vcpu_translate_gva { + __u64 gva; + }; + +:Returns: + +:: + + struct kvmi_error_code; + struct kvmi_vcpu_translate_gva_reply { + __u64 gpa; + }; + +Translates a guest virtual address (``gva``) to a guest physical address +(``gpa``) or ~0 if the address cannot be translated. + +:Errors: + +* -KVM_EINVAL - the selected vCPU is invalid +* -KVM_EINVAL - the padding is not zero +* -KVM_EAGAIN - the selected vCPU can't be introspected yet + Events ====== diff --git a/arch/x86/kvm/kvmi_msg.c b/arch/x86/kvm/kvmi_msg.c index c4b43b3b7b92..a2b37ef3cf2c 100644 --- a/arch/x86/kvm/kvmi_msg.c +++ b/arch/x86/kvm/kvmi_msg.c @@ -313,6 +313,20 @@ static int handle_vcpu_control_singlestep(const struct kvmi_vcpu_msg_job *job, return kvmi_msg_vcpu_reply(job, msg, ec, NULL, 0); } +int handle_vcpu_translate_gva(const struct kvmi_vcpu_msg_job *job, + const struct kvmi_msg_hdr *msg, + const void *_req) +{ + const struct kvmi_vcpu_translate_gva *req = _req; + struct kvmi_vcpu_translate_gva_reply rpl; + + memset(&rpl, 0, sizeof(rpl)); + + rpl.gpa = kvm_mmu_gva_to_gpa_system(job->vcpu, req->gva, 0, NULL); + + return kvmi_msg_vcpu_reply(job, msg, 0, &rpl, sizeof(rpl)); +} + static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { [KVMI_VCPU_CONTROL_CR] = handle_vcpu_control_cr, [KVMI_VCPU_CONTROL_MSR] = handle_vcpu_control_msr, @@ -326,6 +340,7 @@ static kvmi_vcpu_msg_job_fct const msg_vcpu[] = { [KVMI_VCPU_INJECT_EXCEPTION] = handle_vcpu_inject_exception, [KVMI_VCPU_SET_REGISTERS] = handle_vcpu_set_registers, [KVMI_VCPU_SET_XSAVE] = handle_vcpu_set_xsave, + [KVMI_VCPU_TRANSLATE_GVA] = handle_vcpu_translate_gva, }; kvmi_vcpu_msg_job_fct kvmi_arch_vcpu_msg_handler(u16 id) diff --git a/include/uapi/linux/kvmi.h b/include/uapi/linux/kvmi.h index 9c646229a25a..a78d42dd6415 100644 --- a/include/uapi/linux/kvmi.h +++ b/include/uapi/linux/kvmi.h @@ -51,6 +51,7 @@ enum { KVMI_VCPU_GET_MTRR_TYPE = KVMI_VCPU_MESSAGE_ID(11), KVMI_VCPU_CONTROL_MSR = KVMI_VCPU_MESSAGE_ID(12), KVMI_VCPU_CONTROL_SINGLESTEP = KVMI_VCPU_MESSAGE_ID(13), + KVMI_VCPU_TRANSLATE_GVA = KVMI_VCPU_MESSAGE_ID(14), KVMI_NEXT_VCPU_MESSAGE }; @@ -233,4 +234,12 @@ struct kvmi_vcpu_event_singlestep { __u8 padding[7]; }; +struct kvmi_vcpu_translate_gva { + __u64 gva; +}; + +struct kvmi_vcpu_translate_gva_reply { + __u64 gpa; +}; + #endif /* _UAPI__LINUX_KVMI_H */ diff --git a/tools/testing/selftests/kvm/x86_64/kvmi_test.c b/tools/testing/selftests/kvm/x86_64/kvmi_test.c index c6f41f54f9b0..6deaf8dee610 100644 --- a/tools/testing/selftests/kvm/x86_64/kvmi_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvmi_test.c @@ -1905,6 +1905,35 @@ static void test_cmd_vcpu_control_singlestep(struct kvm_vm *vm) test_unsupported_singlestep(vm); } +static void cmd_translate_gva(struct kvm_vm *vm, vm_vaddr_t gva, + vm_paddr_t expected_gpa) +{ + struct { + struct kvmi_msg_hdr hdr; + struct kvmi_vcpu_hdr vcpu_hdr; + struct kvmi_vcpu_translate_gva cmd; + } req = { 0 }; + struct kvmi_vcpu_translate_gva_reply rpl; + + req.cmd.gva = gva; + + test_vcpu0_command(vm, KVMI_VCPU_TRANSLATE_GVA, &req.hdr, sizeof(req), + &rpl, sizeof(rpl), 0); + TEST_ASSERT(rpl.gpa == expected_gpa, + "Translation failed for gva 0x%lx -> gpa 0x%llx instead of 0x%lx\n", + gva, rpl.gpa, expected_gpa); +} + +static void test_cmd_translate_gva(struct kvm_vm *vm) +{ + cmd_translate_gva(vm, test_gva, test_gpa); + pr_info("Tested gva 0x%lx to gpa 0x%lx\n", test_gva, test_gpa); + + cmd_translate_gva(vm, -1, ~0); + pr_info("Tested gva 0x%lx to gpa 0x%lx\n", + (vm_vaddr_t)-1, (vm_paddr_t)-1); +} + static void test_introspection(struct kvm_vm *vm) { srandom(time(0)); @@ -1940,6 +1969,7 @@ static void test_introspection(struct kvm_vm *vm) test_cmd_vm_set_page_access(vm); test_event_pf(vm); test_cmd_vcpu_control_singlestep(vm); + test_cmd_translate_gva(vm); unhook_introspection(vm); } From patchwork Wed Nov 25 09:35:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930909 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 3D100C8300B for ; Wed, 25 Nov 2020 09:42:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E26E92083E for ; Wed, 25 Nov 2020 09:42:58 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728803AbgKYJm6 (ORCPT ); Wed, 25 Nov 2020 04:42:58 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57196 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727737AbgKYJl6 (ORCPT ); Wed, 25 Nov 2020 04:41:58 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id CF00A305D3EC; Wed, 25 Nov 2020 11:35:55 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id AE3F03072784; Wed, 25 Nov 2020 11:35:55 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 80/81] KVM: introspection: emulate a guest page table walk on SPT violations due to A/D bit updates Date: Wed, 25 Nov 2020 11:35:59 +0200 Message-Id: <20201125093600.2766-81-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu On SPT page faults caused by guest page table walks, use the existing guest page table walk code to make the necessary adjustments to the A/D bits and return to guest. This effectively bypasses the x86 emulator who was making the wrong modifications leading one OS (Windows 8.1 x64) to triple-fault very early in the boot process with the introspection enabled. With introspection disabled, these faults are handled by simply removing the protection from the affected guest page and returning to guest. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/include/asm/kvmi_host.h | 2 ++ arch/x86/kvm/kvmi.c | 30 ++++++++++++++++++++++++++++++ arch/x86/kvm/mmu/mmu.c | 12 ++++++++++-- include/linux/kvmi_host.h | 3 +++ virt/kvm/introspection/kvmi.c | 26 ++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/kvmi_host.h b/arch/x86/include/asm/kvmi_host.h index 31500d3ff69d..0502293bd0c9 100644 --- a/arch/x86/include/asm/kvmi_host.h +++ b/arch/x86/include/asm/kvmi_host.h @@ -77,6 +77,7 @@ bool kvmi_descriptor_event(struct kvm_vcpu *vcpu, u8 descriptor, bool write); bool kvmi_msr_event(struct kvm_vcpu *vcpu, struct msr_data *msr); bool kvmi_monitor_msrw_intercept(struct kvm_vcpu *vcpu, u32 msr, bool enable); bool kvmi_msrw_intercept_originator(struct kvm_vcpu *vcpu); +bool kvmi_update_ad_flags(struct kvm_vcpu *vcpu); #else /* CONFIG_KVM_INTROSPECTION */ @@ -102,6 +103,7 @@ static inline bool kvmi_monitor_msrw_intercept(struct kvm_vcpu *vcpu, u32 msr, bool enable) { return false; } static inline bool kvmi_msrw_intercept_originator(struct kvm_vcpu *vcpu) { return false; } +static inline bool kvmi_update_ad_flags(struct kvm_vcpu *vcpu) { return false; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/arch/x86/kvm/kvmi.c b/arch/x86/kvm/kvmi.c index b010d2369756..6dc5df59f274 100644 --- a/arch/x86/kvm/kvmi.c +++ b/arch/x86/kvm/kvmi.c @@ -1099,3 +1099,33 @@ void kvmi_arch_stop_singlestep(struct kvm_vcpu *vcpu) { kvm_x86_ops.control_singlestep(vcpu, false); } + +bool kvmi_update_ad_flags(struct kvm_vcpu *vcpu) +{ + struct kvm_introspection *kvmi; + bool ret = false; + gva_t gva; + gpa_t gpa; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return false; + + gva = kvm_x86_ops.fault_gla(vcpu); + if (gva == ~0ull) + goto out; + + gpa = kvm_mmu_gva_to_gpa_system(vcpu, gva, PFERR_WRITE_MASK, NULL); + if (gpa == UNMAPPED_GVA) { + struct x86_exception exception = { }; + + gpa = kvm_mmu_gva_to_gpa_system(vcpu, gva, 0, &exception); + } + + ret = (gpa != UNMAPPED_GVA); + +out: + kvmi_put(vcpu->kvm); + + return ret; +} diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index f79cf58a27dc..204e44d4e465 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -5184,8 +5185,15 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, u64 error_code, */ if (vcpu->arch.mmu->direct_map && (error_code & PFERR_NESTED_GUEST_PAGE) == PFERR_NESTED_GUEST_PAGE) { - kvm_mmu_unprotect_page(vcpu->kvm, gpa_to_gfn(cr2_or_gpa)); - return 1; + gfn_t gfn = gpa_to_gfn(cr2_or_gpa); + + if (kvmi_tracked_gfn(vcpu, gfn)) { + if (kvmi_update_ad_flags(vcpu)) + return 1; + } else { + kvm_mmu_unprotect_page(vcpu->kvm, gfn); + return 1; + } } /* diff --git a/include/linux/kvmi_host.h b/include/linux/kvmi_host.h index ec38e434c8e9..90647bb2a570 100644 --- a/include/linux/kvmi_host.h +++ b/include/linux/kvmi_host.h @@ -83,6 +83,7 @@ bool kvmi_breakpoint_event(struct kvm_vcpu *vcpu, u64 gva, u8 insn_len); bool kvmi_vcpu_running_singlestep(struct kvm_vcpu *vcpu); void kvmi_singlestep_done(struct kvm_vcpu *vcpu); void kvmi_singlestep_failed(struct kvm_vcpu *vcpu); +bool kvmi_tracked_gfn(struct kvm_vcpu *vcpu, gfn_t gfn); #else @@ -101,6 +102,8 @@ static inline bool kvmi_vcpu_running_singlestep(struct kvm_vcpu *vcpu) { return false; } static inline void kvmi_singlestep_done(struct kvm_vcpu *vcpu) { } static inline void kvmi_singlestep_failed(struct kvm_vcpu *vcpu) { } +static inline bool kvmi_tracked_gfn(struct kvm_vcpu *vcpu, gfn_t gfn) + { return false; } #endif /* CONFIG_KVM_INTROSPECTION */ diff --git a/virt/kvm/introspection/kvmi.c b/virt/kvm/introspection/kvmi.c index 3057f9f343c0..7816af9709d8 100644 --- a/virt/kvm/introspection/kvmi.c +++ b/virt/kvm/introspection/kvmi.c @@ -1235,3 +1235,29 @@ void kvmi_singlestep_failed(struct kvm_vcpu *vcpu) kvmi_handle_singlestep_exit(vcpu, false); } EXPORT_SYMBOL(kvmi_singlestep_failed); + +static bool __kvmi_tracked_gfn(struct kvm_introspection *kvmi, gfn_t gfn) +{ + u8 ignored_access; + + if (kvmi_get_gfn_access(kvmi, gfn, &ignored_access)) + return false; + + return true; +} + +bool kvmi_tracked_gfn(struct kvm_vcpu *vcpu, gfn_t gfn) +{ + struct kvm_introspection *kvmi; + bool ret; + + kvmi = kvmi_get(vcpu->kvm); + if (!kvmi) + return false; + + ret = __kvmi_tracked_gfn(kvmi, gfn); + + kvmi_put(vcpu->kvm); + + return ret; +} From patchwork Wed Nov 25 09:36:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Adalbert_Laz=C4=83r?= X-Patchwork-Id: 11930883 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-13.8 required=3.0 tests=BAYES_00, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS,URIBL_BLOCKED autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B59ECC8300B for ; Wed, 25 Nov 2020 09:42:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 6E6CA21527 for ; Wed, 25 Nov 2020 09:42:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728628AbgKYJmK (ORCPT ); Wed, 25 Nov 2020 04:42:10 -0500 Received: from mx01.bbu.dsd.mx.bitdefender.com ([91.199.104.161]:57142 "EHLO mx01.bbu.dsd.mx.bitdefender.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728612AbgKYJmJ (ORCPT ); Wed, 25 Nov 2020 04:42:09 -0500 Received: from smtp.bitdefender.com (smtp01.buh.bitdefender.com [10.17.80.75]) by mx01.bbu.dsd.mx.bitdefender.com (Postfix) with ESMTPS id F2A13305D3ED; Wed, 25 Nov 2020 11:35:55 +0200 (EET) Received: from localhost.localdomain (unknown [91.199.104.27]) by smtp.bitdefender.com (Postfix) with ESMTPSA id D50E63072785; Wed, 25 Nov 2020 11:35:55 +0200 (EET) From: =?utf-8?q?Adalbert_Laz=C4=83r?= To: kvm@vger.kernel.org Cc: virtualization@lists.linux-foundation.org, Paolo Bonzini , =?utf-8?q?Mihai_Don=C8=9Bu?= , =?utf-8?q?Adalbert_L?= =?utf-8?q?az=C4=83r?= Subject: [PATCH v10 81/81] KVM: x86: call the page tracking code on emulation failure Date: Wed, 25 Nov 2020 11:36:00 +0200 Message-Id: <20201125093600.2766-82-alazar@bitdefender.com> In-Reply-To: <20201125093600.2766-1-alazar@bitdefender.com> References: <20201125093600.2766-1-alazar@bitdefender.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org From: Mihai Donțu The information we can provide this way is incomplete, but current users of the page tracking code can work with it. Signed-off-by: Mihai Donțu Signed-off-by: Adalbert Lazăr --- arch/x86/kvm/x86.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index cc7292ee3b2d..c4de25778942 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -7328,6 +7328,51 @@ static bool is_vmware_backdoor_opcode(struct x86_emulate_ctxt *ctxt) return false; } +/* + * With introspection enabled, emulation failures translate in events being + * missed because the read/write callbacks are not invoked. All we have is + * the fetch event (kvm_page_track_preexec). Below we use the EPT/NPT VMEXIT + * information to generate the events, but without providing accurate + * data and size (the emulator would have computed those). If an instruction + * would happen to read and write in the same page, the second event will + * initially be missed and we rely on the page tracking mechanism to bring + * us back here to send it. + */ +static bool kvm_page_track_emulation_failure(struct kvm_vcpu *vcpu, gpa_t gpa) +{ + u64 error_code = vcpu->arch.error_code; + u8 data = 0; + gva_t gva; + bool ret; + + /* MMIO emulation failures should be treated the normal way */ + if (unlikely(error_code & PFERR_RSVD_MASK)) + return true; + + /* EPT/NTP must be enabled */ + if (unlikely(!vcpu->arch.mmu->direct_map)) + return true; + + /* + * The A/D bit emulation should make this test unneeded, but just + * in case + */ + if (unlikely((error_code & PFERR_NESTED_GUEST_PAGE) == + PFERR_NESTED_GUEST_PAGE)) + return true; + + gva = kvm_x86_ops.fault_gla(vcpu); + + if (error_code & PFERR_WRITE_MASK) + ret = kvm_page_track_prewrite(vcpu, gpa, gva, &data, 0); + else if (error_code & PFERR_USER_MASK) + ret = kvm_page_track_preread(vcpu, gpa, gva, 0); + else + ret = true; + + return ret; +} + int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, int emulation_type, void *insn, int insn_len) { @@ -7381,6 +7426,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, kvm_queue_exception(vcpu, UD_VECTOR); return 1; } + if (!kvm_page_track_emulation_failure(vcpu, cr2_or_gpa)) + return 1; if (reexecute_instruction(vcpu, cr2_or_gpa, write_fault_to_spt, emulation_type)) @@ -7450,6 +7497,8 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, return 1; if (r == EMULATION_FAILED) { + if (!kvm_page_track_emulation_failure(vcpu, cr2_or_gpa)) + return 1; if (reexecute_instruction(vcpu, cr2_or_gpa, write_fault_to_spt, emulation_type)) return 1;