From patchwork Mon Aug 10 20:11:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Lewis X-Patchwork-Id: 11708107 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 50600739 for ; Mon, 10 Aug 2020 20:11:50 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3046A20678 for ; Mon, 10 Aug 2020 20:11:50 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="i59gTdRa" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726396AbgHJULr (ORCPT ); Mon, 10 Aug 2020 16:11:47 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51418 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726143AbgHJULr (ORCPT ); Mon, 10 Aug 2020 16:11:47 -0400 Received: from mail-pj1-x1049.google.com (mail-pj1-x1049.google.com [IPv6:2607:f8b0:4864:20::1049]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 46197C061756 for ; Mon, 10 Aug 2020 13:11:47 -0700 (PDT) Received: by mail-pj1-x1049.google.com with SMTP id s4so535058pjq.8 for ; Mon, 10 Aug 2020 13:11:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=5oAjokWOZe4cAdJFgfr8j682KuQkIW1okam8n6z5dy8=; b=i59gTdRapHyeCjEIgC6k7Z6BefmKEkcLk0+1v8FF7cm2ERlx1uHnIQGWOkDdO2e4j9 TA6kjushKsVVelS0Gfx/Q7ybyzqloGYVNDLuH4Fc08tQpejJrrbJA59wtIW4OiSLPWH3 ur+T82okRpFkuz+lPAkddiP8njjUZl3edbZ92bXK2P13p60m1F15aGYNkk6VEk8oBBb3 Rv680oVKT81RW4hDrYoras8S3pY3YsB/F5AckzuuKDSyCJDyuEdM3XlyvrvsGgNOeKdm 32pZ3yCJwiJR6UCZqBD5suGRD2DGW85wDrUC9uLipWq0VgV88vEhwKL2chEK0lXmEiWy tpPg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=5oAjokWOZe4cAdJFgfr8j682KuQkIW1okam8n6z5dy8=; b=PGE32LilIaOowaxX7fqpwXH4S17v3KEIHq3/az7TGog+p99LGhaFzaUExnZCZnynSs TIKOzDBTwFa7veheO9ZoX2NLm1cfrsRMxVhS3EgBKfxHquav1eTxrK8Y5gWIbf8LBVhK PMTyh+PikhS2sOyrxCSnwzXUbfEmFUDs537MZ0URPRM9Mv40NpCw4qVTh5KEtkk3JZwE Q4tyXTSt4g4Twpfjsm3mrngaP9paevo5UEw36Eg6PdqzFPcDTvBdFCLC8mkHNqzgz8+N chH8RmgwBBvgqgTIhxWiTIwbdpkn1cBvNYm8Hreho+lBA2aXKDq9D9YrZByZkJQflwAa JrGw== X-Gm-Message-State: AOAM5321fB2McDDHDUyTqfHr1ZK8KzBcPc3TKTuyWq4WEAqY09Olsshf xAtg8Nqu3mW35fxvGpjqKFuW00yikRvgFhNE X-Google-Smtp-Source: ABdhPJwdJ6t5/vb3IuTDPqMyfW341aPpIqq/ytFj+8mB54vEu+1b3PjuQ0LgzRAdIR/5dEZ1HWnMNmT+YELWCvnU X-Received: by 2002:a17:90a:2110:: with SMTP id a16mr1027012pje.104.1597090306166; Mon, 10 Aug 2020 13:11:46 -0700 (PDT) Date: Mon, 10 Aug 2020 13:11:27 -0700 In-Reply-To: <20200810201134.2031613-1-aaronlewis@google.com> Message-Id: <20200810201134.2031613-2-aaronlewis@google.com> Mime-Version: 1.0 References: <20200810201134.2031613-1-aaronlewis@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v2 1/8] KVM: x86: Add ioctl for accepting a userspace provided MSR list From: Aaron Lewis To: jmattson@google.com, graf@amazon.com Cc: pshier@google.com, oupton@google.com, kvm@vger.kernel.org, Aaron Lewis Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add KVM_SET_EXIT_MSRS ioctl to allow userspace to pass in a list of MSRs that force an exit to userspace when rdmsr or wrmsr are used by the guest. KVM_SET_EXIT_MSRS will need to be called before any vCPUs are created to protect the 'user_exit_msrs' list from being mutated while vCPUs are running. Add KVM_CAP_SET_MSR_EXITS to identify the feature exists. Signed-off-by: Aaron Lewis Reviewed-by: Oliver Upton --- Documentation/virt/kvm/api.rst | 24 +++++++++++++++++++ arch/x86/include/asm/kvm_host.h | 2 ++ arch/x86/kvm/x86.c | 41 +++++++++++++++++++++++++++++++++ include/uapi/linux/kvm.h | 2 ++ 4 files changed, 69 insertions(+) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 320788f81a05..7d8167c165aa 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -1006,6 +1006,30 @@ such as migration. :Parameters: struct kvm_vcpu_event (out) :Returns: 0 on success, -1 on error +4.32 KVM_SET_EXIT_MSRS +------------------ + +:Capability: KVM_CAP_SET_MSR_EXITS +:Architectures: x86 +:Type: vm ioctl +:Parameters: struct kvm_msr_list (in) +:Returns: 0 on success, -1 on error + +Sets the userspace MSR list which is used to track which MSRs KVM should send +to userspace to be serviced when the guest executes rdmsr or wrmsr. + +This ioctl needs to be called before vCPUs are setup otherwise the list of MSRs +will not be accepted and an EINVAL error will be returned. Also, if a list of +MSRs has already been supplied, and this ioctl is called again an EEXIST error +will be returned. + +:: + + struct kvm_msr_list { + __u32 nmsrs; + __u32 indices[0]; +}; + X86: ^^^^ diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index be5363b21540..510055471dd0 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -1004,6 +1004,8 @@ struct kvm_arch { struct kvm_pmu_event_filter *pmu_event_filter; struct task_struct *nx_lpage_recovery_thread; + + struct kvm_msr_list *user_exit_msrs; }; struct kvm_vm_stat { diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 88c593f83b28..46a0fb9e0869 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3419,6 +3419,42 @@ static inline bool kvm_can_mwait_in_guest(void) boot_cpu_has(X86_FEATURE_ARAT); } +static int kvm_vm_ioctl_set_exit_msrs(struct kvm *kvm, + struct kvm_msr_list __user *user_msr_list) +{ + struct kvm_msr_list *msr_list, hdr; + size_t indices_size; + + if (kvm->arch.user_exit_msrs != NULL) + return -EEXIST; + + if (kvm->created_vcpus) + return -EINVAL; + + if (copy_from_user(&hdr, user_msr_list, sizeof(hdr))) + return -EFAULT; + + if (hdr.nmsrs >= MAX_IO_MSRS) + return -E2BIG; + + indices_size = sizeof(hdr.indices[0]) * hdr.nmsrs; + msr_list = kvzalloc(sizeof(struct kvm_msr_list) + indices_size, + GFP_KERNEL_ACCOUNT); + if (!msr_list) + return -ENOMEM; + msr_list->nmsrs = hdr.nmsrs; + + if (copy_from_user(msr_list->indices, user_msr_list->indices, + indices_size)) { + kvfree(msr_list); + return -EFAULT; + } + + kvm->arch.user_exit_msrs = msr_list; + + return 0; +} + int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { int r = 0; @@ -3476,6 +3512,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_MSR_PLATFORM_INFO: case KVM_CAP_EXCEPTION_PAYLOAD: case KVM_CAP_SET_GUEST_DEBUG: + case KVM_CAP_SET_MSR_EXITS: r = 1; break; case KVM_CAP_SYNC_REGS: @@ -5261,6 +5298,10 @@ long kvm_arch_vm_ioctl(struct file *filp, r = 0; break; } + case KVM_SET_EXIT_MSRS: { + r = kvm_vm_ioctl_set_exit_msrs(kvm, argp); + break; + } case KVM_MEMORY_ENCRYPT_OP: { r = -ENOTTY; if (kvm_x86_ops.mem_enc_op) diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 4fdf30316582..de4638c1bd15 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -1031,6 +1031,7 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_PPC_SECURE_GUEST 181 #define KVM_CAP_HALT_POLL 182 #define KVM_CAP_ASYNC_PF_INT 183 +#define KVM_CAP_SET_MSR_EXITS 185 #ifdef KVM_CAP_IRQ_ROUTING @@ -1371,6 +1372,7 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_PMU_EVENT_FILTER */ #define KVM_SET_PMU_EVENT_FILTER _IOW(KVMIO, 0xb2, struct kvm_pmu_event_filter) #define KVM_PPC_SVM_OFF _IO(KVMIO, 0xb3) +#define KVM_SET_EXIT_MSRS _IOW(KVMIO, 0xb4, struct kvm_msr_list) /* ioctl for vm fd */ #define KVM_CREATE_DEVICE _IOWR(KVMIO, 0xe0, struct kvm_create_device) From patchwork Mon Aug 10 20:11:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Lewis X-Patchwork-Id: 11708109 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6C6FE739 for ; Mon, 10 Aug 2020 20:11:52 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 49D3220678 for ; Mon, 10 Aug 2020 20:11:52 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="UtRgdU8G" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726454AbgHJULv (ORCPT ); Mon, 10 Aug 2020 16:11:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51432 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726401AbgHJULu (ORCPT ); Mon, 10 Aug 2020 16:11:50 -0400 Received: from mail-yb1-xb49.google.com (mail-yb1-xb49.google.com [IPv6:2607:f8b0:4864:20::b49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0B2B6C061787 for ; Mon, 10 Aug 2020 13:11:49 -0700 (PDT) Received: by mail-yb1-xb49.google.com with SMTP id v11so14008246ybm.22 for ; Mon, 10 Aug 2020 13:11:49 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=37nYOD48n/vVqgBq9MIlMweMYibuOoa/U4EqBJhegJc=; b=UtRgdU8G5nxZLyIzA0fBRWKca0yx5pmhl23QubZs6TAUfRcSUYa5S04Q4iEP2XEwOF mspQNhUA0OTIlNnN0jl2jg4Ym91/p5HgAkqwwM624Z2yMrwCui80fv+zAiaAQxNCiRNC pz4s75eQWkOSVdvLTli/Y1fFnbZMZajSFSxktoKnc/f9toON9xZZel32aTukfDtw0jN+ ECmlteTCHKbEHBM/tkMENnZ9WkY2XG7ibnDuXb7GIkxrV5jt0aob3G4Isclw3g4koOgT 66ysyE1YfUku2aDg9c1gv5HrFQmzx0PG6jtoNLMrxVz3WuTihwlVX3AOHRmfMjlOfMMm 4CNQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=37nYOD48n/vVqgBq9MIlMweMYibuOoa/U4EqBJhegJc=; b=HtC4pIbmN/OktVpEgsTStH+4VxHJxAZqEmyD8aY5uOAsdMwoSBR1feRw46PLgdteer +Ubyts9CIKQxIu99Faf1xNH0GLFrIQUCgsJWrTAXdC6AprSjkumVlwU+f6xvxAo9xnjF Phof7pMKrLx/tjKtFDZBBTeEf5LFmkzuOIELImeI1RBpOvvV+Ckqw0AOvbUNRsVC7NPz aRDgy1bZ0+6KDjnIbRoftAWKjc3mmx8e8wNMGoyx+rWjGXf81ieQWSiwk3EG819jiiJ2 VNjfYhWP1rw4OBLDGe5o19UCDgjfnU9Sg8uxI+uex+Hr/4IWqOL42hDAuShNY+HBu3Ds gR6Q== X-Gm-Message-State: AOAM533cuQViFfjO58+LwMEIomB4egij36PjbzXMtqtYFTs/d4Wwnmc7 qvgpELyOLiX9D1rJBd26CuFbHqsNPpgF3GLA X-Google-Smtp-Source: ABdhPJz86yMp+/S0D9QyAWgaWUs/wHZcJMHUok3Lxw0WPOHDDrRZMdsljVDJ9RJso12AX/eMC5KvvDqAXlyeboGV X-Received: by 2002:a5b:b05:: with SMTP id z5mr13923874ybp.321.1597090308817; Mon, 10 Aug 2020 13:11:48 -0700 (PDT) Date: Mon, 10 Aug 2020 13:11:28 -0700 In-Reply-To: <20200810201134.2031613-1-aaronlewis@google.com> Message-Id: <20200810201134.2031613-3-aaronlewis@google.com> Mime-Version: 1.0 References: <20200810201134.2031613-1-aaronlewis@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v2 2/8] KVM: x86: Add support for exiting to userspace on rdmsr or wrmsr From: Aaron Lewis To: jmattson@google.com, graf@amazon.com Cc: pshier@google.com, oupton@google.com, kvm@vger.kernel.org, Aaron Lewis Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add support for exiting to userspace on a rdmsr or wrmsr instruction if the MSR being read from or written to is in the user_exit_msrs list. Signed-off-by: Aaron Lewis --- Documentation/virt/kvm/api.rst | 29 +++++++++++- arch/x86/kvm/trace.h | 24 ++++++++++ arch/x86/kvm/x86.c | 83 ++++++++++++++++++++++++++++++++++ include/trace/events/kvm.h | 2 +- include/uapi/linux/kvm.h | 10 ++++ 5 files changed, 145 insertions(+), 3 deletions(-) diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index 7d8167c165aa..59c45e6b3f6b 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -4885,8 +4885,9 @@ to the byte array. .. note:: - For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_PAPR and - KVM_EXIT_EPR the corresponding + For KVM_EXIT_IO, KVM_EXIT_MMIO, KVM_EXIT_OSI, KVM_EXIT_PAPR, + KVM_EXIT_EPR, KVM_EXIT_X86_RDMSR, and KVM_EXIT_X86_WRMSR the + corresponding operations are complete (and guest state is consistent) only after userspace has re-entered the kernel with KVM_RUN. The kernel side will first finish @@ -5179,6 +5180,30 @@ Note that KVM does not skip the faulting instruction as it does for KVM_EXIT_MMIO, but userspace has to emulate any change to the processing state if it decides to decode and emulate the instruction. +:: + + /* KVM_EXIT_X86_RDMSR */ + /* KVM_EXIT_X86_WRMSR */ + struct { + __u8 inject_gp; + __u8 pad[3]; + __u32 index; + __u64 data; + } msr; + +If the exit_reason is KVM_EXIT_X86_RDMSR then a rdmsr instruction in the guest +needs to be processed by userspace. If the exit_reason is KVM_EXIT_X86_WRMSR +then a wrmsr instruction in the guest needs to be processed by userspace. + +Userspace can tell KVM to inject a #GP into the guest by setting the +'inject_gp' flag. Setting the flag to 1 tells KVM to inject a GP into the +guest. Setting the flag to 0 tells KVM to not inject a GP into the guest. + +The MSR being processed is indicated by 'index'. If a read is being processed +the 'data' field is expected to be filled out by userspace (as an out +parameter). If a write is being processed the 'data' field will contain the +updated value of the MSR (as an in parameter). + :: /* Fix the size of the union. */ diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index b66432b015d2..d03143ebd6f0 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h @@ -367,6 +367,30 @@ TRACE_EVENT(kvm_msr, #define trace_kvm_msr_read_ex(ecx) trace_kvm_msr(0, ecx, 0, true) #define trace_kvm_msr_write_ex(ecx, data) trace_kvm_msr(1, ecx, data, true) +TRACE_EVENT(kvm_userspace_msr, + TP_PROTO(bool is_write, u8 inject_gp, u32 index, u64 data), + TP_ARGS(is_write, inject_gp, index, data), + + TP_STRUCT__entry( + __field(bool, is_write) + __field(u8, inject_gp) + __field(u32, index) + __field(u64, data) + ), + + TP_fast_assign( + __entry->is_write = is_write; + __entry->inject_gp = inject_gp; + __entry->index = index; + __entry->data = data; + ), + + TP_printk("userspace %s %x = 0x%llx, %s", + __entry->is_write ? "wrmsr" : "rdmsr", + __entry->index, __entry->data, + __entry->inject_gp ? "inject_gp" : "no_gp") +); + /* * Tracepoint for guest CR access. */ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 46a0fb9e0869..47619b49818a 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -108,6 +108,8 @@ static void __kvm_set_rflags(struct kvm_vcpu *vcpu, unsigned long rflags); static void store_regs(struct kvm_vcpu *vcpu); static int sync_regs(struct kvm_vcpu *vcpu); +bool kvm_msr_user_exit(struct kvm *kvm, u32 index); + struct kvm_x86_ops kvm_x86_ops __read_mostly; EXPORT_SYMBOL_GPL(kvm_x86_ops); @@ -1549,11 +1551,61 @@ int kvm_set_msr(struct kvm_vcpu *vcpu, u32 index, u64 data) } EXPORT_SYMBOL_GPL(kvm_set_msr); +/* + * On success, returns 1 so that __vcpu_run() will happen next. On + * error, returns 0. + */ +static int complete_userspace_msr(struct kvm_vcpu *vcpu, bool is_write) +{ + u32 ecx = vcpu->run->msr.index; + u64 data = vcpu->run->msr.data; + + trace_kvm_userspace_msr(is_write, + vcpu->run->msr.inject_gp, + vcpu->run->msr.index, + vcpu->run->msr.data); + + if (vcpu->run->msr.inject_gp) { + trace_kvm_msr(is_write, ecx, data, true); + kvm_inject_gp(vcpu, 0); + return 1; + } + + trace_kvm_msr(is_write, ecx, data, false); + if (!is_write) { + kvm_rax_write(vcpu, data & -1u); + kvm_rdx_write(vcpu, (data >> 32) & -1u); + } + + return kvm_skip_emulated_instruction(vcpu); +} + +static int complete_userspace_rdmsr(struct kvm_vcpu *vcpu) +{ + return complete_userspace_msr(vcpu, false); +} + +static int complete_userspace_wrmsr(struct kvm_vcpu *vcpu) +{ + return complete_userspace_msr(vcpu, true); +} + int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu) { u32 ecx = kvm_rcx_read(vcpu); u64 data; + if (kvm_msr_user_exit(vcpu->kvm, ecx)) { + vcpu->run->exit_reason = KVM_EXIT_X86_RDMSR; + vcpu->run->msr.index = ecx; + vcpu->run->msr.data = 0; + vcpu->run->msr.inject_gp = 0; + memset(vcpu->run->msr.pad, 0, sizeof(vcpu->run->msr.pad)); + vcpu->arch.complete_userspace_io = + complete_userspace_rdmsr; + return 0; + } + if (kvm_get_msr(vcpu, ecx, &data)) { trace_kvm_msr_read_ex(ecx); kvm_inject_gp(vcpu, 0); @@ -1573,6 +1625,17 @@ int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu) u32 ecx = kvm_rcx_read(vcpu); u64 data = kvm_read_edx_eax(vcpu); + if (kvm_msr_user_exit(vcpu->kvm, ecx)) { + vcpu->run->exit_reason = KVM_EXIT_X86_WRMSR; + vcpu->run->msr.index = ecx; + vcpu->run->msr.data = data; + vcpu->run->msr.inject_gp = 0; + memset(vcpu->run->msr.pad, 0, sizeof(vcpu->run->msr.pad)); + vcpu->arch.complete_userspace_io = + complete_userspace_wrmsr; + return 0; + } + if (kvm_set_msr(vcpu, ecx, data)) { trace_kvm_msr_write_ex(ecx, data); kvm_inject_gp(vcpu, 0); @@ -3455,6 +3518,25 @@ static int kvm_vm_ioctl_set_exit_msrs(struct kvm *kvm, return 0; } +bool kvm_msr_user_exit(struct kvm *kvm, u32 index) +{ + struct kvm_msr_list *exit_msrs; + int i; + + exit_msrs = kvm->arch.user_exit_msrs; + + if (!exit_msrs) + return false; + + for (i = 0; i < exit_msrs->nmsrs; ++i) { + if (exit_msrs->indices[i] == index) + return true; + } + + return false; +} +EXPORT_SYMBOL_GPL(kvm_msr_user_exit); + int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { int r = 0; @@ -10762,3 +10844,4 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_unaccelerated_access); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_incomplete_ipi); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_ga_log); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_apicv_update_request); +EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_userspace_msr); diff --git a/include/trace/events/kvm.h b/include/trace/events/kvm.h index 2c735a3e6613..19f33a704174 100644 --- a/include/trace/events/kvm.h +++ b/include/trace/events/kvm.h @@ -17,7 +17,7 @@ ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL), \ ERSN(S390_UCONTROL), ERSN(WATCHDOG), ERSN(S390_TSCH), ERSN(EPR),\ ERSN(SYSTEM_EVENT), ERSN(S390_STSI), ERSN(IOAPIC_EOI), \ - ERSN(HYPERV) + ERSN(HYPERV), ERSN(X86_RDMSR), ERSN(X86_WRMSR) TRACE_EVENT(kvm_userspace_exit, TP_PROTO(__u32 reason, int errno), diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index de4638c1bd15..6cbbe58a8ecb 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -248,6 +248,8 @@ struct kvm_hyperv_exit { #define KVM_EXIT_IOAPIC_EOI 26 #define KVM_EXIT_HYPERV 27 #define KVM_EXIT_ARM_NISV 28 +#define KVM_EXIT_X86_RDMSR 29 +#define KVM_EXIT_X86_WRMSR 30 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -412,6 +414,14 @@ struct kvm_run { __u64 esr_iss; __u64 fault_ipa; } arm_nisv; + /* KVM_EXIT_X86_RDMSR */ + /* KVM_EXIT_X86_WRMSR */ + struct { + __u8 inject_gp; + __u8 pad[3]; + __u32 index; + __u64 data; + } msr; /* Fix the size of the union. */ char padding[256]; }; From patchwork Mon Aug 10 20:11:29 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Lewis X-Patchwork-Id: 11708111 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2651F739 for ; Mon, 10 Aug 2020 20:11:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 02C9D20656 for ; Mon, 10 Aug 2020 20:11:59 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="CiUPpx5T" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726500AbgHJUL6 (ORCPT ); Mon, 10 Aug 2020 16:11:58 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51442 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726511AbgHJULx (ORCPT ); Mon, 10 Aug 2020 16:11:53 -0400 Received: from mail-pg1-x54a.google.com (mail-pg1-x54a.google.com [IPv6:2607:f8b0:4864:20::54a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 21C1EC061787 for ; Mon, 10 Aug 2020 13:11:53 -0700 (PDT) Received: by mail-pg1-x54a.google.com with SMTP id h2so7273725pgc.19 for ; Mon, 10 Aug 2020 13:11:53 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=lANKPJQmUG0ftqWuRSW+TyNatCjqHLxhR7LE07lIvXY=; b=CiUPpx5TpPqcqPtLQO7X78wqB4rbvxu1Wr4VH9aUsfvZKfBXlvCu6siENfHareEIF4 bB+7CpnccbM5jn/ImvKbSlcLJBoyP3HryW+lEgB2NSPjpjijTArBTT+mBMcwVB5/D6fh yLLEc9xG8jPwolcJg1rsYMehNMC1w54DqDDDGFHjlMlHFCJPgHvcoFPwE5oUp83JfngQ ClpOzPPMmguHhdHGIeheIlNngnk02plIQTwAcgD5Dqe8JiG1C3oWytX+xQ7EA/hY8u62 RLZLbFjSK/6dOJQXkChkzmFT0L9i1XUg5xKNYHfjggwtzW8D+8f6fBM4oBo3GjCXKbu2 AwFw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=lANKPJQmUG0ftqWuRSW+TyNatCjqHLxhR7LE07lIvXY=; b=EIIK33/r/KQYjgiG1LLA64RGv0cotZxAPlUYWk7dTtSyWsVjcC2TuGm8aZ7BPGEWV8 PBEXqqiI3w1UMNbrlBdB1NdZK1NdKwF+Qu/1EsgQ8CUcJnO5EzV1Jx9JhMhTsqM5Qphs 3nFAviMRbRifSjxC9T4Zwzb184Za0sGxRl0Jymj5ITerRYWERjxPU0imXquikzJMgGvp Hw90kFILat3FQHxZfOB4PI3z7LEWC7aU/cyj88qQeOGoWTMPfm0uZ9CuaVIVneVMnoAj xTFFpFmsMnh40CcnNaTYkUKs7k0lWk1OryLUTqGCpfBiLEffWW3U6sUcGdPO4qCe2mwx dLPg== X-Gm-Message-State: AOAM530YTuEZ9gUCyWm61kCRabovZTJiPnPIyT6/3/9gX+CuqEgPDWbw NI2TiDpbA62Y48b+uRmq6KmJ77qa6aL1/kbW X-Google-Smtp-Source: ABdhPJyqQo8CSenEOesGM8PM9FKa1RMrif+xZZ1nXF7SHrJqH0DM8fWG2NWf1TpfFTvrjXwKcFUvx12apZBWspSs X-Received: by 2002:a62:1c58:: with SMTP id c85mr2632185pfc.105.1597090311355; Mon, 10 Aug 2020 13:11:51 -0700 (PDT) Date: Mon, 10 Aug 2020 13:11:29 -0700 In-Reply-To: <20200810201134.2031613-1-aaronlewis@google.com> Message-Id: <20200810201134.2031613-4-aaronlewis@google.com> Mime-Version: 1.0 References: <20200810201134.2031613-1-aaronlewis@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v2 3/8] KVM: x86: Allow em_{rdmsr,wrmsr} to bounce to userspace From: Aaron Lewis To: jmattson@google.com, graf@amazon.com Cc: pshier@google.com, oupton@google.com, kvm@vger.kernel.org, Aaron Lewis Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Refactor em_{rdmsr,wrmsr} to allow an MSR to bounce to userspace when it exists in the list of MSRs userspace has requested to manage with the ioctl KVM_SET_EXIT_MSRS. Signed-off-by: Aaron Lewis Based-on-patch-by: Alexander Graf --- arch/x86/kvm/emulate.c | 18 +++++++++-- arch/x86/kvm/x86.c | 70 ++++++++++++++++++++++++++++++------------ 2 files changed, 66 insertions(+), 22 deletions(-) diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index d0e2825ae617..744ab9c92b73 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -3689,11 +3689,18 @@ static int em_dr_write(struct x86_emulate_ctxt *ctxt) static int em_wrmsr(struct x86_emulate_ctxt *ctxt) { + u64 msr_index = reg_read(ctxt, VCPU_REGS_RCX); u64 msr_data; + int r; msr_data = (u32)reg_read(ctxt, VCPU_REGS_RAX) | ((u64)reg_read(ctxt, VCPU_REGS_RDX) << 32); - if (ctxt->ops->set_msr(ctxt, reg_read(ctxt, VCPU_REGS_RCX), msr_data)) + r = ctxt->ops->set_msr(ctxt, msr_index, msr_data); + + if (r == X86EMUL_IO_NEEDED) + return r; + + if (r) return emulate_gp(ctxt, 0); return X86EMUL_CONTINUE; @@ -3701,9 +3708,16 @@ static int em_wrmsr(struct x86_emulate_ctxt *ctxt) static int em_rdmsr(struct x86_emulate_ctxt *ctxt) { + u64 msr_index = reg_read(ctxt, VCPU_REGS_RCX); u64 msr_data; + int r; + + r = ctxt->ops->get_msr(ctxt, msr_index, &msr_data); + + if (r == X86EMUL_IO_NEEDED) + return r; - if (ctxt->ops->get_msr(ctxt, reg_read(ctxt, VCPU_REGS_RCX), &msr_data)) + if (r) return emulate_gp(ctxt, 0); *reg_write(ctxt, VCPU_REGS_RAX) = (u32)msr_data; diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 47619b49818a..4dff6147557e 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -1590,21 +1590,46 @@ static int complete_userspace_wrmsr(struct kvm_vcpu *vcpu) return complete_userspace_msr(vcpu, true); } +static int kvm_get_msr_user(struct kvm_vcpu *vcpu, u32 index) +{ + if (!kvm_msr_user_exit(vcpu->kvm, index)) + return 0; + + vcpu->run->exit_reason = KVM_EXIT_X86_RDMSR; + vcpu->run->msr.index = index; + vcpu->run->msr.data = 0; + vcpu->run->msr.inject_gp = 0; + memset(vcpu->run->msr.pad, 0, sizeof(vcpu->run->msr.pad)); + vcpu->arch.complete_userspace_io = + complete_userspace_rdmsr; + + return 1; +} + +static int kvm_set_msr_user(struct kvm_vcpu *vcpu, u32 index, u64 data) +{ + if (!kvm_msr_user_exit(vcpu->kvm, index)) + return 0; + + vcpu->run->exit_reason = KVM_EXIT_X86_WRMSR; + vcpu->run->msr.index = index; + vcpu->run->msr.data = data; + vcpu->run->msr.inject_gp = 0; + memset(vcpu->run->msr.pad, 0, sizeof(vcpu->run->msr.pad)); + vcpu->arch.complete_userspace_io = + complete_userspace_wrmsr; + + return 1; +} + int kvm_emulate_rdmsr(struct kvm_vcpu *vcpu) { u32 ecx = kvm_rcx_read(vcpu); u64 data; - if (kvm_msr_user_exit(vcpu->kvm, ecx)) { - vcpu->run->exit_reason = KVM_EXIT_X86_RDMSR; - vcpu->run->msr.index = ecx; - vcpu->run->msr.data = 0; - vcpu->run->msr.inject_gp = 0; - memset(vcpu->run->msr.pad, 0, sizeof(vcpu->run->msr.pad)); - vcpu->arch.complete_userspace_io = - complete_userspace_rdmsr; + if (kvm_get_msr_user(vcpu, ecx)) + /* Bounce to user space */ return 0; - } if (kvm_get_msr(vcpu, ecx, &data)) { trace_kvm_msr_read_ex(ecx); @@ -1625,16 +1650,9 @@ int kvm_emulate_wrmsr(struct kvm_vcpu *vcpu) u32 ecx = kvm_rcx_read(vcpu); u64 data = kvm_read_edx_eax(vcpu); - if (kvm_msr_user_exit(vcpu->kvm, ecx)) { - vcpu->run->exit_reason = KVM_EXIT_X86_WRMSR; - vcpu->run->msr.index = ecx; - vcpu->run->msr.data = data; - vcpu->run->msr.inject_gp = 0; - memset(vcpu->run->msr.pad, 0, sizeof(vcpu->run->msr.pad)); - vcpu->arch.complete_userspace_io = - complete_userspace_wrmsr; + if (kvm_set_msr_user(vcpu, ecx, data)) + /* Bounce to user space */ return 0; - } if (kvm_set_msr(vcpu, ecx, data)) { trace_kvm_msr_write_ex(ecx, data); @@ -6442,13 +6460,25 @@ static void emulator_set_segment(struct x86_emulate_ctxt *ctxt, u16 selector, static int emulator_get_msr(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 *pdata) { - return kvm_get_msr(emul_to_vcpu(ctxt), msr_index, pdata); + struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); + + if (kvm_get_msr_user(vcpu, msr_index)) + /* Bounce to user space */ + return X86EMUL_IO_NEEDED; + + return kvm_get_msr(vcpu, msr_index, pdata); } static int emulator_set_msr(struct x86_emulate_ctxt *ctxt, u32 msr_index, u64 data) { - return kvm_set_msr(emul_to_vcpu(ctxt), msr_index, data); + struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); + + if (kvm_set_msr_user(vcpu, msr_index, data)) + /* Bounce to user space */ + return X86EMUL_IO_NEEDED; + + return kvm_set_msr(vcpu, msr_index, data); } static u64 emulator_get_smbase(struct x86_emulate_ctxt *ctxt) From patchwork Mon Aug 10 20:11:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Lewis X-Patchwork-Id: 11708113 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6B7C0739 for ; Mon, 10 Aug 2020 20:12:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3A12A20656 for ; Mon, 10 Aug 2020 20:12:00 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="b5/213a3" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726523AbgHJUL7 (ORCPT ); Mon, 10 Aug 2020 16:11:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51452 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726402AbgHJULy (ORCPT ); Mon, 10 Aug 2020 16:11:54 -0400 Received: from mail-pg1-x54a.google.com (mail-pg1-x54a.google.com [IPv6:2607:f8b0:4864:20::54a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A0E2EC061756 for ; Mon, 10 Aug 2020 13:11:54 -0700 (PDT) Received: by mail-pg1-x54a.google.com with SMTP id e3so7299311pgs.3 for ; Mon, 10 Aug 2020 13:11:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=rafbdsjQarsUKGSPEv71YSQhAev+6FYwzXOqulSlqTA=; b=b5/213a3U0WAjg/nK0YbGOxDfvLddQK/3zY5lqTTanMpHb2hQefjZnaGGq/f9y/VWz Q+8rGYZKZGRuNZCb0TPf0V7IaRffAe8EQWgGqJ5kHL1GwNs9oxsZyOrDYi2MzPYDUbY5 lsjOWd7ooTEyZqtPgsG2CKzo425f3MQS+qt+i0Fg9I0IQK8PQF0f2xim5gGnkd72sOoG ZHax/UKodA0ipRzncaaQiJPOdUxSQBCpuiXl+BO+2rmQLMsk1N0lrk3rjqjCoaMiAtWq 43VNi8PL3Ei0MH04x4oedXlRfzWnmoN5AjiRhQ4Dwa6nxLyuKcbXs/rByscy3QbyMuQY QoOQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=rafbdsjQarsUKGSPEv71YSQhAev+6FYwzXOqulSlqTA=; b=ZkOjbkJGgJ5O3Ncea6LeEaRnGokjuBzRwarnPej4fqQ7OzrGFF1hVmjAQIqLcBPMNp I/Jpaz/1LkCOhqW/+N5afMuXjePkPadZo7MhRGkHjPlgdF6FrqZZkhOtJqZGd2510P9c C3CcNF+1Y38fFOPZPWHfhUacRC++ekWb2Pbm0g9DvaQ/eXqn6bEVNpowlUBjzaEnw1f0 rIIfyp6KsTsfSqod5HPQNPL1jF5SR7qJQBlK6yQJrl3gegLs3gT8LGJ51+oRW5H004L4 hr4A1o8zYDht9zVSOD7ctVa5gKEgbRShOSHC0PGCk4KrorikDP2QmsGNS89ePxHc/djY Wcxw== X-Gm-Message-State: AOAM5320NOuOQfTJXA1b5DCexPOLtxGQt/cvtmancl6A6+2eD304W1rs 7G9ZEEu87YXpfoj8QLXUe0xM3Mjcx9oQMa1T X-Google-Smtp-Source: ABdhPJzLtux6t7PE1E8df3nmmdpFAF+8UQ0uUuaelaekC/jbLeYBzdS9FTgDPkpHdAUUTfkwP8DVr2cgzof1K/FL X-Received: by 2002:a17:90b:390d:: with SMTP id ob13mr1059894pjb.122.1597090314131; Mon, 10 Aug 2020 13:11:54 -0700 (PDT) Date: Mon, 10 Aug 2020 13:11:30 -0700 In-Reply-To: <20200810201134.2031613-1-aaronlewis@google.com> Message-Id: <20200810201134.2031613-5-aaronlewis@google.com> Mime-Version: 1.0 References: <20200810201134.2031613-1-aaronlewis@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v2 4/8] KVM: x86: Prepare MSR bitmaps for userspace tracked MSRs From: Aaron Lewis To: jmattson@google.com, graf@amazon.com Cc: pshier@google.com, oupton@google.com, kvm@vger.kernel.org, Aaron Lewis Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Prepare vmx and svm for a subsequent change that ensures the MSR bitmap allows for a vm_exit for all MSRs that userspace is tracking. Signed-off-by: Aaron Lewis Reviewed-by: Oliver Upton --- arch/x86/kvm/svm/svm.c | 48 +++++++++++----------- arch/x86/kvm/vmx/nested.c | 2 +- arch/x86/kvm/vmx/vmx.c | 83 +++++++++++++++++++-------------------- arch/x86/kvm/vmx/vmx.h | 2 +- 4 files changed, 67 insertions(+), 68 deletions(-) diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index c0da4dd78ac5..eb673b59f7b7 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -564,7 +564,7 @@ static bool valid_msr_intercept(u32 index) return false; } -static bool msr_write_intercepted(struct kvm_vcpu *vcpu, unsigned msr) +static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr) { u8 bit_write; unsigned long tmp; @@ -583,9 +583,11 @@ static bool msr_write_intercepted(struct kvm_vcpu *vcpu, unsigned msr) return !!test_bit(bit_write, &tmp); } -static void set_msr_interception(u32 *msrpm, unsigned msr, - int read, int write) +static void set_msr_interception(struct kvm_vcpu *vcpu, u32 msr, int read, + int write) { + struct vcpu_svm *svm = to_svm(vcpu); + u32 *msrpm = svm->msrpm; u8 bit_read, bit_write; unsigned long tmp; u32 offset; @@ -609,7 +611,7 @@ static void set_msr_interception(u32 *msrpm, unsigned msr, msrpm[offset] = tmp; } -static void svm_vcpu_init_msrpm(u32 *msrpm) +static void svm_vcpu_init_msrpm(struct kvm_vcpu *vcpu, u32 *msrpm) { int i; @@ -619,7 +621,7 @@ static void svm_vcpu_init_msrpm(u32 *msrpm) if (!direct_access_msrs[i].always) continue; - set_msr_interception(msrpm, direct_access_msrs[i].index, 1, 1); + set_msr_interception(vcpu, direct_access_msrs[i].index, 1, 1); } } @@ -666,26 +668,26 @@ static void init_msrpm_offsets(void) } } -static void svm_enable_lbrv(struct vcpu_svm *svm) +static void svm_enable_lbrv(struct kvm_vcpu *vcpu) { - u32 *msrpm = svm->msrpm; + struct vcpu_svm *svm = to_svm(vcpu); svm->vmcb->control.virt_ext |= LBR_CTL_ENABLE_MASK; - set_msr_interception(msrpm, MSR_IA32_LASTBRANCHFROMIP, 1, 1); - set_msr_interception(msrpm, MSR_IA32_LASTBRANCHTOIP, 1, 1); - set_msr_interception(msrpm, MSR_IA32_LASTINTFROMIP, 1, 1); - set_msr_interception(msrpm, MSR_IA32_LASTINTTOIP, 1, 1); + set_msr_interception(vcpu, MSR_IA32_LASTBRANCHFROMIP, 1, 1); + set_msr_interception(vcpu, MSR_IA32_LASTBRANCHTOIP, 1, 1); + set_msr_interception(vcpu, MSR_IA32_LASTINTFROMIP, 1, 1); + set_msr_interception(vcpu, MSR_IA32_LASTINTTOIP, 1, 1); } -static void svm_disable_lbrv(struct vcpu_svm *svm) +static void svm_disable_lbrv(struct kvm_vcpu *vcpu) { - u32 *msrpm = svm->msrpm; + struct vcpu_svm *svm = to_svm(vcpu); svm->vmcb->control.virt_ext &= ~LBR_CTL_ENABLE_MASK; - set_msr_interception(msrpm, MSR_IA32_LASTBRANCHFROMIP, 0, 0); - set_msr_interception(msrpm, MSR_IA32_LASTBRANCHTOIP, 0, 0); - set_msr_interception(msrpm, MSR_IA32_LASTINTFROMIP, 0, 0); - set_msr_interception(msrpm, MSR_IA32_LASTINTTOIP, 0, 0); + set_msr_interception(vcpu, MSR_IA32_LASTBRANCHFROMIP, 0, 0); + set_msr_interception(vcpu, MSR_IA32_LASTBRANCHTOIP, 0, 0); + set_msr_interception(vcpu, MSR_IA32_LASTINTFROMIP, 0, 0); + set_msr_interception(vcpu, MSR_IA32_LASTINTTOIP, 0, 0); } void disable_nmi_singlestep(struct vcpu_svm *svm) @@ -1196,10 +1198,10 @@ static int svm_create_vcpu(struct kvm_vcpu *vcpu) clear_page(svm->nested.hsave); svm->msrpm = page_address(msrpm_pages); - svm_vcpu_init_msrpm(svm->msrpm); + svm_vcpu_init_msrpm(vcpu, svm->msrpm); svm->nested.msrpm = page_address(nested_msrpm_pages); - svm_vcpu_init_msrpm(svm->nested.msrpm); + svm_vcpu_init_msrpm(vcpu, svm->nested.msrpm); svm->vmcb = page_address(page); clear_page(svm->vmcb); @@ -2540,7 +2542,7 @@ 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(svm->msrpm, MSR_IA32_SPEC_CTRL, 1, 1); + set_msr_interception(vcpu, MSR_IA32_SPEC_CTRL, 1, 1); break; case MSR_IA32_PRED_CMD: if (!msr->host_initiated && @@ -2555,7 +2557,7 @@ 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(svm->msrpm, MSR_IA32_PRED_CMD, 0, 1); + set_msr_interception(vcpu, MSR_IA32_PRED_CMD, 0, 1); break; case MSR_AMD64_VIRT_SPEC_CTRL: if (!msr->host_initiated && @@ -2619,9 +2621,9 @@ static int svm_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr) svm->vmcb->save.dbgctl = data; mark_dirty(svm->vmcb, VMCB_LBR); if (data & (1ULL<<0)) - svm_enable_lbrv(svm); + svm_enable_lbrv(vcpu); else - svm_disable_lbrv(svm); + svm_disable_lbrv(vcpu); break; case MSR_VM_HSAVE_PA: svm->nested.hsave_msr = data; diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index d4a4cec034d0..9313814d9e91 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -4704,7 +4704,7 @@ static int enter_vmx_operation(struct kvm_vcpu *vcpu) if (vmx_pt_mode_is_host_guest()) { vmx->pt_desc.guest.ctl = 0; - pt_update_intercept_for_msr(vmx); + pt_update_intercept_for_msr(vcpu); } return 0; diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index 13745f2a5ecd..1313e47a5a1e 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -342,7 +342,7 @@ module_param_cb(vmentry_l1d_flush, &vmentry_l1d_flush_ops, NULL, 0644); static bool guest_state_valid(struct kvm_vcpu *vcpu); static u32 vmx_segment_access_rights(struct kvm_segment *var); -static __always_inline void vmx_disable_intercept_for_msr(unsigned long *msr_bitmap, +static __always_inline void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type); void vmx_vmexit(void); @@ -2081,7 +2081,7 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) * in the merging. We update the vmcs01 here for L1 as well * since it will end up touching the MSR anyway now. */ - vmx_disable_intercept_for_msr(vmx->vmcs01.msr_bitmap, + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SPEC_CTRL, MSR_TYPE_RW); break; @@ -2117,8 +2117,7 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) * vmcs02.msr_bitmap here since it gets completely overwritten * in the merging. */ - vmx_disable_intercept_for_msr(vmx->vmcs01.msr_bitmap, MSR_IA32_PRED_CMD, - MSR_TYPE_W); + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_PRED_CMD, MSR_TYPE_W); break; case MSR_IA32_CR_PAT: if (!kvm_pat_valid(data)) @@ -2168,7 +2167,7 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; vmcs_write64(GUEST_IA32_RTIT_CTL, data); vmx->pt_desc.guest.ctl = data; - pt_update_intercept_for_msr(vmx); + pt_update_intercept_for_msr(vcpu); break; case MSR_IA32_RTIT_STATUS: if (!pt_can_write_msr(vmx)) @@ -3691,9 +3690,11 @@ void free_vpid(int vpid) spin_unlock(&vmx_vpid_lock); } -static __always_inline void vmx_disable_intercept_for_msr(unsigned long *msr_bitmap, +static __always_inline void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type) { + struct vcpu_vmx *vmx = to_vmx(vcpu); + unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap; int f = sizeof(unsigned long); if (!cpu_has_vmx_msr_bitmap()) @@ -3729,9 +3730,11 @@ static __always_inline void vmx_disable_intercept_for_msr(unsigned long *msr_bit } } -static __always_inline void vmx_enable_intercept_for_msr(unsigned long *msr_bitmap, +static __always_inline void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, u32 msr, int type) { + struct vcpu_vmx *vmx = to_vmx(vcpu); + unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap; int f = sizeof(unsigned long); if (!cpu_has_vmx_msr_bitmap()) @@ -3767,13 +3770,13 @@ static __always_inline void vmx_enable_intercept_for_msr(unsigned long *msr_bitm } } -static __always_inline void vmx_set_intercept_for_msr(unsigned long *msr_bitmap, - u32 msr, int type, bool value) +static __always_inline void vmx_set_intercept_for_msr(struct kvm_vcpu *vcpu, + u32 msr, int type, bool value) { if (value) - vmx_enable_intercept_for_msr(msr_bitmap, msr, type); + vmx_enable_intercept_for_msr(vcpu, msr, type); else - vmx_disable_intercept_for_msr(msr_bitmap, msr, type); + vmx_disable_intercept_for_msr(vcpu, msr, type); } static u8 vmx_msr_bitmap_mode(struct kvm_vcpu *vcpu) @@ -3791,8 +3794,8 @@ static u8 vmx_msr_bitmap_mode(struct kvm_vcpu *vcpu) return mode; } -static void vmx_update_msr_bitmap_x2apic(unsigned long *msr_bitmap, - u8 mode) +static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu, + unsigned long *msr_bitmap, u8 mode) { int msr; @@ -3807,11 +3810,11 @@ static void vmx_update_msr_bitmap_x2apic(unsigned long *msr_bitmap, * TPR reads and writes can be virtualized even if virtual interrupt * delivery is not in use. */ - vmx_disable_intercept_for_msr(msr_bitmap, X2APIC_MSR(APIC_TASKPRI), MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TASKPRI), MSR_TYPE_RW); if (mode & MSR_BITMAP_MODE_X2APIC_APICV) { - vmx_enable_intercept_for_msr(msr_bitmap, X2APIC_MSR(APIC_TMCCT), MSR_TYPE_R); - vmx_disable_intercept_for_msr(msr_bitmap, X2APIC_MSR(APIC_EOI), MSR_TYPE_W); - vmx_disable_intercept_for_msr(msr_bitmap, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W); + vmx_enable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TMCCT), MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W); + vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W); } } } @@ -3827,30 +3830,24 @@ void vmx_update_msr_bitmap(struct kvm_vcpu *vcpu) return; if (changed & (MSR_BITMAP_MODE_X2APIC | MSR_BITMAP_MODE_X2APIC_APICV)) - vmx_update_msr_bitmap_x2apic(msr_bitmap, mode); + vmx_update_msr_bitmap_x2apic(vcpu, msr_bitmap, mode); vmx->msr_bitmap_mode = mode; } -void pt_update_intercept_for_msr(struct vcpu_vmx *vmx) +void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu) { - unsigned long *msr_bitmap = vmx->vmcs01.msr_bitmap; + struct vcpu_vmx *vmx = to_vmx(vcpu); bool flag = !(vmx->pt_desc.guest.ctl & RTIT_CTL_TRACEEN); u32 i; - vmx_set_intercept_for_msr(msr_bitmap, MSR_IA32_RTIT_STATUS, - MSR_TYPE_RW, flag); - vmx_set_intercept_for_msr(msr_bitmap, MSR_IA32_RTIT_OUTPUT_BASE, - MSR_TYPE_RW, flag); - vmx_set_intercept_for_msr(msr_bitmap, MSR_IA32_RTIT_OUTPUT_MASK, - MSR_TYPE_RW, flag); - vmx_set_intercept_for_msr(msr_bitmap, MSR_IA32_RTIT_CR3_MATCH, - MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_STATUS, MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_BASE, MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_OUTPUT_MASK, MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_CR3_MATCH, MSR_TYPE_RW, flag); for (i = 0; i < vmx->pt_desc.addr_range; i++) { - vmx_set_intercept_for_msr(msr_bitmap, - MSR_IA32_RTIT_ADDR0_A + i * 2, MSR_TYPE_RW, flag); - vmx_set_intercept_for_msr(msr_bitmap, - MSR_IA32_RTIT_ADDR0_B + i * 2, MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_A + i * 2, MSR_TYPE_RW, flag); + vmx_set_intercept_for_msr(vcpu, MSR_IA32_RTIT_ADDR0_B + i * 2, MSR_TYPE_RW, flag); } } @@ -6905,18 +6902,18 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu) goto free_pml; msr_bitmap = vmx->vmcs01.msr_bitmap; - vmx_disable_intercept_for_msr(msr_bitmap, MSR_IA32_TSC, MSR_TYPE_R); - vmx_disable_intercept_for_msr(msr_bitmap, MSR_FS_BASE, MSR_TYPE_RW); - vmx_disable_intercept_for_msr(msr_bitmap, MSR_GS_BASE, MSR_TYPE_RW); - vmx_disable_intercept_for_msr(msr_bitmap, MSR_KERNEL_GS_BASE, MSR_TYPE_RW); - vmx_disable_intercept_for_msr(msr_bitmap, MSR_IA32_SYSENTER_CS, MSR_TYPE_RW); - vmx_disable_intercept_for_msr(msr_bitmap, MSR_IA32_SYSENTER_ESP, MSR_TYPE_RW); - vmx_disable_intercept_for_msr(msr_bitmap, MSR_IA32_SYSENTER_EIP, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_TSC, MSR_TYPE_R); + vmx_disable_intercept_for_msr(vcpu, MSR_FS_BASE, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_GS_BASE, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_KERNEL_GS_BASE, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_CS, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_ESP, MSR_TYPE_RW); + vmx_disable_intercept_for_msr(vcpu, MSR_IA32_SYSENTER_EIP, MSR_TYPE_RW); if (kvm_cstate_in_guest(vcpu->kvm)) { - vmx_disable_intercept_for_msr(msr_bitmap, MSR_CORE_C1_RES, MSR_TYPE_R); - vmx_disable_intercept_for_msr(msr_bitmap, MSR_CORE_C3_RESIDENCY, MSR_TYPE_R); - vmx_disable_intercept_for_msr(msr_bitmap, MSR_CORE_C6_RESIDENCY, MSR_TYPE_R); - vmx_disable_intercept_for_msr(msr_bitmap, MSR_CORE_C7_RESIDENCY, MSR_TYPE_R); + vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C1_RES, MSR_TYPE_R); + vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C3_RESIDENCY, MSR_TYPE_R); + vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C6_RESIDENCY, MSR_TYPE_R); + vmx_disable_intercept_for_msr(vcpu, MSR_CORE_C7_RESIDENCY, MSR_TYPE_R); } vmx->msr_bitmap_mode = 0; diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 639798e4a6ca..b3c74f0fe8a1 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -350,7 +350,7 @@ bool vmx_get_nmi_mask(struct kvm_vcpu *vcpu); void vmx_set_nmi_mask(struct kvm_vcpu *vcpu, bool masked); void vmx_set_virtual_apic_mode(struct kvm_vcpu *vcpu); struct shared_msr_entry *find_msr_entry(struct vcpu_vmx *vmx, u32 msr); -void pt_update_intercept_for_msr(struct vcpu_vmx *vmx); +void pt_update_intercept_for_msr(struct kvm_vcpu *vcpu); void vmx_update_host_rsp(struct vcpu_vmx *vmx, unsigned long host_rsp); int vmx_find_msr_index(struct vmx_msrs *m, u32 msr); int vmx_handle_memory_failure(struct kvm_vcpu *vcpu, int r, From patchwork Mon Aug 10 20:11:31 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Lewis X-Patchwork-Id: 11708115 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DBC0B13B6 for ; Mon, 10 Aug 2020 20:12:00 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id B1AA82075D for ; Mon, 10 Aug 2020 20:12:00 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="mSZ07bh7" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726405AbgHJUL7 (ORCPT ); Mon, 10 Aug 2020 16:11:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51464 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726595AbgHJUL5 (ORCPT ); Mon, 10 Aug 2020 16:11:57 -0400 Received: from mail-pl1-x649.google.com (mail-pl1-x649.google.com [IPv6:2607:f8b0:4864:20::649]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BCA8AC061787 for ; Mon, 10 Aug 2020 13:11:57 -0700 (PDT) Received: by mail-pl1-x649.google.com with SMTP id y10so7507466plp.6 for ; Mon, 10 Aug 2020 13:11:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=vMfoBO7nBJeomzdNZAfqP4kKn3LHAx4KtNydfQWOUvY=; b=mSZ07bh7fcEy9UixbUl9HQf/fuMlTOmLjhafIPqKwMpso/Td57Kf3gncedJFy6vhd8 VyOKeX9nDL5LmbVxWtdPOeuGNCO1LDlC6GhI3w6lIrzvhc2ZXkhiyHDFFDI4OKx0eytv 7STB2Tf0lNwd7qRLF/mv92A6PpzTdFezj8BQ7zo1ZAnFguFjKUYwjmSn6AQCdsdCbzUz fuEvqsMPI8lYuld07Wdou5Z94G173yNNFRMgLcGBmvZaKT4cQXRdVNW90s9mKxhEHpul f+XkXwKoAC29FM4GEr5oRl7S5xVtrnfFUd2s12uuH73y0hzK+CMiA/YGDIp4CXvrwtkx CSpA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=vMfoBO7nBJeomzdNZAfqP4kKn3LHAx4KtNydfQWOUvY=; b=VdIo7XUKUpl+QDmlNIbbgzQPwk6MnyLtwtUCw80w9XvP8VPiytMciJPJY2u+5lwR38 e6FfTe2hzS82Een2G8223suttdCMD+EfXxLSBn137ly0KxAwfvqAOO6WGA6JJAyUw93I Qg0mDdZQzCyfDhQYIo0OyreuDR6RvdyGHidOGsxH2XCL0ZvDaZMljVKWZoNogDiU7eWP +UmzFPn0Cyk7KeIe0uDCNhpGGa+FTji1noyVha39azUl5PB6CUF48rOoclmTTplf0Yq9 G0Ht0r6zm0nInP4VWkH2wjhsEr974Th2MVRfn6mIvIQkPADT7stxsUzlq5wcqrXEG2K1 X8kQ== X-Gm-Message-State: AOAM532WPyS6HVLSdERvbumTpdAl7vHOcS0lCigMol2j+IoIXs6NwLzx mqZ0SZAx1j31olfHUXur0/nAp9V+wLNUhcJi X-Google-Smtp-Source: ABdhPJx+RNosn8DwAGvGubjSRxUkYLsVnopy23TtK2d5wT51KYL14jXwO6hc77+hw6wQphaKZluwx4iUGCMfza+v X-Received: by 2002:a17:90a:3488:: with SMTP id p8mr192043pjb.1.1597090316804; Mon, 10 Aug 2020 13:11:56 -0700 (PDT) Date: Mon, 10 Aug 2020 13:11:31 -0700 In-Reply-To: <20200810201134.2031613-1-aaronlewis@google.com> Message-Id: <20200810201134.2031613-6-aaronlewis@google.com> Mime-Version: 1.0 References: <20200810201134.2031613-1-aaronlewis@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v2 5/8] KVM: x86: Ensure the MSR bitmap never clears userspace tracked MSRs From: Aaron Lewis To: jmattson@google.com, graf@amazon.com Cc: pshier@google.com, oupton@google.com, kvm@vger.kernel.org, Aaron Lewis Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org SDM volume 3: 24.6.9 "MSR-Bitmap Address" and APM volume 2: 15.11 "MS intercepts" describe MSR permission bitmaps. Permission bitmaps are used to control whether an execution of rdmsr or wrmsr will cause a vm exit. For userspace tracked MSRs it is required they cause a vm exit, so the host is able to forward the MSR to userspace. This change adds vmx/svm support to ensure the permission bitmap is properly set to cause a vm_exit to the host when rdmsr or wrmsr is used by one of the userspace tracked MSRs. Also, to avoid repeatedly setting them, kvm_make_request() is used to coalesce these into a single call. Signed-off-by: Aaron Lewis Reviewed-by: Oliver Upton Reported-by: kernel test robot --- arch/x86/include/asm/kvm_host.h | 3 ++ arch/x86/kvm/svm/svm.c | 49 ++++++++++++++++++++++++++------- arch/x86/kvm/vmx/vmx.c | 13 ++++++++- arch/x86/kvm/x86.c | 16 +++++++++++ 4 files changed, 70 insertions(+), 11 deletions(-) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 510055471dd0..07a85f5f0b8a 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -87,6 +87,7 @@ #define KVM_REQ_HV_TLB_FLUSH \ KVM_ARCH_REQ_FLAGS(27, KVM_REQUEST_NO_WAKEUP) #define KVM_REQ_APF_READY KVM_ARCH_REQ(28) +#define KVM_REQ_USER_MSR_UPDATE KVM_ARCH_REQ(29) #define CR0_RESERVED_BITS \ (~(unsigned long)(X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS \ @@ -1271,6 +1272,8 @@ struct kvm_x86_ops { int (*enable_direct_tlbflush)(struct kvm_vcpu *vcpu); void (*migrate_timers)(struct kvm_vcpu *vcpu); + + void (*set_user_msr_intercept)(struct kvm_vcpu *vcpu, u32 msr); }; struct kvm_x86_nested_ops { diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index eb673b59f7b7..c560d283b2af 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -583,13 +583,27 @@ static bool msr_write_intercepted(struct kvm_vcpu *vcpu, u32 msr) return !!test_bit(bit_write, &tmp); } +static void __set_msr_interception(u32 *msrpm, u32 msr, int read, int write, + u32 offset) +{ + u8 bit_read, bit_write; + unsigned long tmp; + + bit_read = 2 * (msr & 0x0f); + bit_write = 2 * (msr & 0x0f) + 1; + tmp = msrpm[offset]; + + read ? clear_bit(bit_read, &tmp) : set_bit(bit_read, &tmp); + write ? clear_bit(bit_write, &tmp) : set_bit(bit_write, &tmp); + + msrpm[offset] = tmp; +} + static void set_msr_interception(struct kvm_vcpu *vcpu, u32 msr, int read, int write) { struct vcpu_svm *svm = to_svm(vcpu); u32 *msrpm = svm->msrpm; - u8 bit_read, bit_write; - unsigned long tmp; u32 offset; /* @@ -598,17 +612,30 @@ static void set_msr_interception(struct kvm_vcpu *vcpu, u32 msr, int read, */ WARN_ON(!valid_msr_intercept(msr)); - offset = svm_msrpm_offset(msr); - bit_read = 2 * (msr & 0x0f); - bit_write = 2 * (msr & 0x0f) + 1; - tmp = msrpm[offset]; - + offset = svm_msrpm_offset(msr); 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); + __set_msr_interception(msrpm, msr, read, write, offset); - msrpm[offset] = tmp; + if (read || write) + kvm_make_request(KVM_REQ_USER_MSR_UPDATE, vcpu); +} + +static void set_user_msr_interception(struct kvm_vcpu *vcpu, u32 msr, int read, + int write) +{ + struct vcpu_svm *svm = to_svm(vcpu); + u32 *msrpm = svm->msrpm; + u32 offset; + + offset = svm_msrpm_offset(msr); + if (offset != MSR_INVALID) + __set_msr_interception(msrpm, msr, read, write, offset); +} + +void svm_set_user_msr_intercept(struct kvm_vcpu *vcpu, u32 msr) +{ + set_user_msr_interception(vcpu, msr, 0, 0); } static void svm_vcpu_init_msrpm(struct kvm_vcpu *vcpu, u32 *msrpm) @@ -4088,6 +4115,8 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .need_emulation_on_page_fault = svm_need_emulation_on_page_fault, .apic_init_signal_blocked = svm_apic_init_signal_blocked, + + .set_user_msr_intercept = svm_set_user_msr_intercept, }; 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 1313e47a5a1e..3d3d9eaeca64 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -3728,6 +3728,10 @@ static __always_inline void vmx_disable_intercept_for_msr(struct kvm_vcpu *vcpu, __clear_bit(msr, msr_bitmap + 0xc00 / f); } + + if (type & MSR_TYPE_R || type & MSR_TYPE_W) { + kvm_make_request(KVM_REQ_USER_MSR_UPDATE, vcpu); + } } static __always_inline void vmx_enable_intercept_for_msr(struct kvm_vcpu *vcpu, @@ -3795,7 +3799,7 @@ static u8 vmx_msr_bitmap_mode(struct kvm_vcpu *vcpu) } static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu, - unsigned long *msr_bitmap, u8 mode) + unsigned long *msr_bitmap, u8 mode) { int msr; @@ -3819,6 +3823,11 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu, } } +void vmx_set_user_msr_intercept(struct kvm_vcpu *vcpu, u32 msr) +{ + vmx_enable_intercept_for_msr(vcpu, msr, MSR_TYPE_RW); +} + void vmx_update_msr_bitmap(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); @@ -7965,6 +7974,8 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .need_emulation_on_page_fault = vmx_need_emulation_on_page_fault, .apic_init_signal_blocked = vmx_apic_init_signal_blocked, .migrate_timers = vmx_migrate_timers, + + .set_user_msr_intercept = vmx_set_user_msr_intercept, }; static __init int hardware_setup(void) diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4dff6147557e..b79600086bd3 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3555,6 +3555,19 @@ bool kvm_msr_user_exit(struct kvm *kvm, u32 index) } EXPORT_SYMBOL_GPL(kvm_msr_user_exit); +static void kvm_set_user_msr_intercepts(struct kvm_vcpu *vcpu) +{ + struct kvm_msr_list *msr_list = vcpu->kvm->arch.user_exit_msrs; + u32 i, msr; + + if (msr_list) { + for (i = 0; i < msr_list->nmsrs; i++) { + msr = msr_list->indices[i]; + kvm_x86_ops.set_user_msr_intercept(vcpu, msr); + } + } +} + int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) { int r = 0; @@ -8583,6 +8596,9 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) kvm_vcpu_update_apicv(vcpu); if (kvm_check_request(KVM_REQ_APF_READY, vcpu)) kvm_check_async_pf_completion(vcpu); + + if (kvm_check_request(KVM_REQ_USER_MSR_UPDATE, vcpu)) + kvm_set_user_msr_intercepts(vcpu); } if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win) { From patchwork Mon Aug 10 20:11:32 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Lewis X-Patchwork-Id: 11708117 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6909B1392 for ; Mon, 10 Aug 2020 20:12:04 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3E4A02076B for ; Mon, 10 Aug 2020 20:12:04 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="D/WXZF2c" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726596AbgHJUMD (ORCPT ); Mon, 10 Aug 2020 16:12:03 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51472 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726587AbgHJUMA (ORCPT ); Mon, 10 Aug 2020 16:12:00 -0400 Received: from mail-qv1-xf49.google.com (mail-qv1-xf49.google.com [IPv6:2607:f8b0:4864:20::f49]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 884B7C061787 for ; Mon, 10 Aug 2020 13:12:00 -0700 (PDT) Received: by mail-qv1-xf49.google.com with SMTP id q4so8028046qvu.6 for ; Mon, 10 Aug 2020 13:12:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=sOMEn0K6jrOMRw0X2/vhJfeR19zk5k6nzsJWKIT2bMI=; b=D/WXZF2cCnwUnGp48QIWtDV5bjcR/HTLtaUAvPbpIz6KdSssYsEIKehsmc4bu0lbhY Az2qtcq45T2Xyi6VrL+RoWctqnhTEpsxQW3ueZAKyimfRpp8T507AUfVOh0efkHRM5lD ZmhI0/KdA1yHxmxRwv+PGnGzzv8GM0vQWqbrHRAckRU6Wn4h54WABUz2u96xrYJP13PT eNujUZPdX2MzDpP130PYjcjFyporw+57iZpotLdhYD0Ev9WzZq1VDdEtlpyAIiopmIr6 O1QaSLY95kjPsludhdl8lEx/+o9ulke9a+tgSYi2z/W+dv6Ijm+XtH19fPaDb4vYf/tK sUFQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=sOMEn0K6jrOMRw0X2/vhJfeR19zk5k6nzsJWKIT2bMI=; b=qCDvYUjYcwEQfbRAK2Jdn6Kmt3uBEb1LkWhXdAxztRlt4RciNKL+8Cy1JpCn8igXCb /fXFW0MKZAvpkjP0Vk4Br1GjRu34ieRzcJXRuVsbuh3hllLxTK6zGehSzjts8RWFldY8 agz9Ppy1v0pnPgA3AKAbpRa4Y4UuOYvtwAjfWpWCc2A8AplPyS8fNgAtTiXfcAIsMstB iHEWZWRWstadyLCYBL/fkPkeYsP4mWORpKwW73N6FvvGvBcoN4D7bkDfnx1wdLx1CMyL wKIhjhgw7RR7J1q+gRJXFc0+UxUnwEyDsm5L4bZWbhwsXmOTOMvKGJJ8Hc2SpHbDdD6o OFtw== X-Gm-Message-State: AOAM531fg8CDO2WYlFsHmNbWXgxoN4TtIHGa3cQEbkR4RossOHNqkG0B RZETxiMgQl6FeHyQZ7+cPXV1kEtZgSMUpunV X-Google-Smtp-Source: ABdhPJy6B31b9vrSrqvw6DX91UL6tvZ558B+TUkbnxv3Xlnib5JClzxPe+lOslCkuyFT4UJFg49PmFkcDOowxBhz X-Received: by 2002:a0c:f6cb:: with SMTP id d11mr30964423qvo.84.1597090319517; Mon, 10 Aug 2020 13:11:59 -0700 (PDT) Date: Mon, 10 Aug 2020 13:11:32 -0700 In-Reply-To: <20200810201134.2031613-1-aaronlewis@google.com> Message-Id: <20200810201134.2031613-7-aaronlewis@google.com> Mime-Version: 1.0 References: <20200810201134.2031613-1-aaronlewis@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v2 6/8] selftests: kvm: Fix the segment descriptor layout to match the actual layout From: Aaron Lewis To: jmattson@google.com, graf@amazon.com Cc: pshier@google.com, oupton@google.com, kvm@vger.kernel.org, Aaron Lewis Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Fix the layout of 'struct desc64' to match the layout described in the SDM Vol 3, 3.4.5 Segment Descriptors, Figure 3-8. The test added later in this series relies on this and crashes if this layout is not correct. Signed-off-by: Aaron Lewis --- tools/testing/selftests/kvm/include/x86_64/processor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h index 82b7fe16a824..0a65e7bb5249 100644 --- a/tools/testing/selftests/kvm/include/x86_64/processor.h +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h @@ -59,7 +59,7 @@ struct gpr64_regs { struct desc64 { uint16_t limit0; uint16_t base0; - unsigned base1:8, s:1, type:4, dpl:2, p:1; + unsigned base1:8, type:4, s:1, dpl:2, p:1; unsigned limit1:4, avl:1, l:1, db:1, g:1, base2:8; uint32_t base3; uint32_t zero1; From patchwork Mon Aug 10 20:11:33 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Lewis X-Patchwork-Id: 11708121 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 6AD2A739 for ; Mon, 10 Aug 2020 20:12:15 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3837E20678 for ; Mon, 10 Aug 2020 20:12:15 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="AjAsHFan" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726621AbgHJUME (ORCPT ); Mon, 10 Aug 2020 16:12:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51486 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726624AbgHJUMC (ORCPT ); Mon, 10 Aug 2020 16:12:02 -0400 Received: from mail-pf1-x449.google.com (mail-pf1-x449.google.com [IPv6:2607:f8b0:4864:20::449]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CC687C06178A for ; Mon, 10 Aug 2020 13:12:02 -0700 (PDT) Received: by mail-pf1-x449.google.com with SMTP id y11so8646460pfq.8 for ; Mon, 10 Aug 2020 13:12:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=ul5urjgbEqqH2rypiHVn98NMp12Xfj5k9Z6hAnjK+MU=; b=AjAsHFan1w2SIlLulVs3r5fn7Vl+7xesn6N/QNd2XN5TeRjkYv/2qkQLyLuTNOAbDM c65EImhQjIwuA/fv4kwi4hZmc6mou19UvSjHOUjHeghRlvtOotNEdwuqkyywDiHN50Tb EznV4/6yrUQsivgVTiWjrsSUcR+PChEM9v5HzB6EidmaJzTyVEtT5Wm5IZjoI79qlwB9 0uDM7+VZ+gngCCzlDq2rfbAWsxaX2MYnkXq40+6mVBbfKuwMEqxlDaZCPerj5EJRv2u+ Fuenq58cCk10nUDQJ5kweNz5+wFayH33QFHaZ3T6PepUI7nBFRtY4JYvhdvst/O51uRC LFbQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=ul5urjgbEqqH2rypiHVn98NMp12Xfj5k9Z6hAnjK+MU=; b=X18QJQ0d0eEjO3QjLHLtn3Mufz73lMGhskqkiVLevp2aQAwJhgCOsLf9CXF8YStBJH jeJ9TvCP0OMcNt8DHRTlJTINAdW5iSLcPqsEKbceYg/fbUSQqMJq6abBJxdaFr83fATA l3ShVFjr2bC/DpRQuOKsa38C+vsHHsk+0waiBOlzU9j6/w/r9qXChR94SEJFk0jzbPQn V8Xr1U72xEyJRTqoaqh5q+b5XkQcIqZ9/kDbWl1XW3ry49LsbvU+i/WDOnsgjBGfC9Pw wcoVdQaN2srz2nDE5MwRSYk4eZxwPwWEiLK4YZVKNjRr1ENWnT9342V3tPgwoDuD8nCr 3nkw== X-Gm-Message-State: AOAM531+76oRjNDJkKe1VEnj6ge0JHsKohu/0pXfKgBy6x1rSFZhUa2V oEFIjP1iGnTau3qrJ1lv8qOSXwn21oQ2+njA X-Google-Smtp-Source: ABdhPJxSn9Ro1iT+DndlWHnXWc/ffVOz88hLwd59kT30PQZ8BSm4ynOlqyk6dBb0C4qJNsxYc78cYOYvs+FIMtuO X-Received: by 2002:a17:90a:9516:: with SMTP id t22mr917559pjo.134.1597090321930; Mon, 10 Aug 2020 13:12:01 -0700 (PDT) Date: Mon, 10 Aug 2020 13:11:33 -0700 In-Reply-To: <20200810201134.2031613-1-aaronlewis@google.com> Message-Id: <20200810201134.2031613-8-aaronlewis@google.com> Mime-Version: 1.0 References: <20200810201134.2031613-1-aaronlewis@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v2 7/8] selftests: kvm: Add test to exercise userspace MSR list From: Aaron Lewis To: jmattson@google.com, graf@amazon.com Cc: pshier@google.com, oupton@google.com, kvm@vger.kernel.org, Aaron Lewis Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add a selftest to test that when ioctl KVM_SET_EXIT_MSRS is called with an MSR list the guest exits to the host and then to userspace when an MSR in that list is read from or written to. This test uses 3 MSRs to test these new features: 1. MSR_IA32_XSS, an MSR the kernel knows about. 2. MSR_IA32_FLUSH_CMD, an MSR the kernel does not know about. 3. MSR_NON_EXISTENT, an MSR invented in this test for the purposes of passing a fake MSR from the guest to userspace and having the guest be able to read from and write to it, with userspace handling it. KVM just acts as a pass through. Userspace is also able to inject a #GP. This is demonstrated when MSR_IA32_XSS and MSR_IA32_FLUSH_CMD are misused in the test. When this happens a #GP is initiated in userspace to be thrown in the guest. To be able to handle this, exception handling was added to selftests, and a to inject a #GP and be able to handle it gracefully. Signed-off-by: Aaron Lewis --- tools/testing/selftests/kvm/.gitignore | 1 + tools/testing/selftests/kvm/Makefile | 20 +- .../selftests/kvm/include/x86_64/processor.h | 27 ++ tools/testing/selftests/kvm/lib/kvm_util.c | 17 ++ .../selftests/kvm/lib/kvm_util_internal.h | 2 + .../selftests/kvm/lib/x86_64/handlers.S | 83 ++++++ .../selftests/kvm/lib/x86_64/processor.c | 168 ++++++++++- .../testing/selftests/kvm/lib/x86_64/ucall.c | 3 + .../selftests/kvm/x86_64/userspace_msr_exit.c | 279 ++++++++++++++++++ 9 files changed, 590 insertions(+), 10 deletions(-) create mode 100644 tools/testing/selftests/kvm/lib/x86_64/handlers.S create mode 100644 tools/testing/selftests/kvm/x86_64/userspace_msr_exit.c diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index 452787152748..33619f915857 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -14,6 +14,7 @@ /x86_64/vmx_preemption_timer_test /x86_64/svm_vmcall_test /x86_64/sync_regs_test +/x86_64/userspace_msr_exit /x86_64/vmx_close_while_nested_test /x86_64/vmx_dirty_log_test /x86_64/vmx_set_nested_state_test diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 4a166588d99f..66a6652ca305 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -34,7 +34,7 @@ ifeq ($(ARCH),s390) endif LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/sparsebit.c lib/test_util.c -LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c +LIBKVM_x86_64 = lib/x86_64/processor.c lib/x86_64/vmx.c lib/x86_64/svm.c lib/x86_64/ucall.c lib/x86_64/handlers.S LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c @@ -49,6 +49,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/state_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_preemption_timer_test TEST_GEN_PROGS_x86_64 += x86_64/svm_vmcall_test TEST_GEN_PROGS_x86_64 += x86_64/sync_regs_test +TEST_GEN_PROGS_x86_64 += x86_64/userspace_msr_exit TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_dirty_log_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test @@ -108,14 +109,21 @@ LDFLAGS += -pthread $(no-pie-option) $(pgste-option) include ../lib.mk STATIC_LIBS := $(OUTPUT)/libkvm.a -LIBKVM_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM)) -EXTRA_CLEAN += $(LIBKVM_OBJ) $(STATIC_LIBS) cscope.* +LIBKVM_C := $(filter %.c,$(LIBKVM)) +LIBKVM_S := $(filter %.S,$(LIBKVM)) +LIBKVM_C_OBJ := $(patsubst %.c, $(OUTPUT)/%.o, $(LIBKVM_C)) +LIBKVM_S_OBJ := $(patsubst %.S, $(OUTPUT)/%.o, $(LIBKVM_S)) +EXTRA_CLEAN += $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) $(STATIC_LIBS) cscope.* + +x := $(shell mkdir -p $(sort $(dir $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ)))) +$(LIBKVM_C_OBJ): $(OUTPUT)/%.o: %.c + $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ -x := $(shell mkdir -p $(sort $(dir $(LIBKVM_OBJ)))) -$(LIBKVM_OBJ): $(OUTPUT)/%.o: %.c +$(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c $< -o $@ -$(OUTPUT)/libkvm.a: $(LIBKVM_OBJ) +LIBKVM_OBJS = $(LIBKVM_C_OBJ) $(LIBKVM_S_OBJ) +$(OUTPUT)/libkvm.a: $(LIBKVM_OBJS) $(AR) crs $@ $^ x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS)))) diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h index 0a65e7bb5249..a4de429eb408 100644 --- a/tools/testing/selftests/kvm/include/x86_64/processor.h +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h @@ -36,6 +36,8 @@ #define X86_CR4_SMAP (1ul << 21) #define X86_CR4_PKE (1ul << 22) +#define UNEXPECTED_VECTOR_PORT 0xfff0u + /* General Registers in 64-Bit Mode */ struct gpr64_regs { u64 rax; @@ -239,6 +241,11 @@ static inline struct desc_ptr get_idt(void) return idt; } +static inline void outl(uint16_t port, uint32_t value) +{ + __asm__ __volatile__("outl %%eax, %%dx" : : "d"(port), "a"(value)); +} + #define SET_XMM(__var, __xmm) \ asm volatile("movq %0, %%"#__xmm : : "r"(__var) : #__xmm) @@ -334,10 +341,30 @@ int _vcpu_set_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index, void vcpu_set_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index, uint64_t msr_value); +void kvm_set_exit_msrs(struct kvm_vm *vm, uint32_t nmsrs, + uint32_t msr_indices[]); + uint32_t kvm_get_cpuid_max_basic(void); uint32_t kvm_get_cpuid_max_extended(void); void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits); +struct ex_regs { + uint64_t rax, rcx, rdx, rbx; + uint64_t dummy, rbp, rsi, rdi; + uint64_t r8, r9, r10, r11; + uint64_t r12, r13, r14, r15; + uint64_t vector; + uint64_t error_code; + uint64_t rip; + uint64_t cs; + uint64_t rflags; +}; + +void vm_init_descriptor_tables(struct kvm_vm *vm); +void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid); +void vm_handle_exception(struct kvm_vm *vm, int vector, + void (*handler)(struct ex_regs *)); + /* * Basic CPU control in CR0 */ diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 74776ee228f2..f8dde1cdbef0 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -1195,6 +1195,21 @@ int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid) do { rc = ioctl(vcpu->fd, KVM_RUN, NULL); } while (rc == -1 && errno == EINTR); + +#ifdef __x86_64__ + if (vcpu_state(vm, vcpuid)->exit_reason == KVM_EXIT_IO + && vcpu_state(vm, vcpuid)->io.port == UNEXPECTED_VECTOR_PORT + && vcpu_state(vm, vcpuid)->io.size == 4) { + /* Grab pointer to io data */ + uint32_t *data = (void *)vcpu_state(vm, vcpuid) + + vcpu_state(vm, vcpuid)->io.data_offset; + + TEST_ASSERT(false, + "Unexpected vectored event in guest (vector:0x%x)", + *data); + } +#endif + return rc; } @@ -1590,6 +1605,8 @@ static struct exit_reason { {KVM_EXIT_INTERNAL_ERROR, "INTERNAL_ERROR"}, {KVM_EXIT_OSI, "OSI"}, {KVM_EXIT_PAPR_HCALL, "PAPR_HCALL"}, + {KVM_EXIT_X86_RDMSR, "RDMSR"}, + {KVM_EXIT_X86_WRMSR, "WRMSR"}, #ifdef KVM_EXIT_MEMORY_NOT_PRESENT {KVM_EXIT_MEMORY_NOT_PRESENT, "MEMORY_NOT_PRESENT"}, #endif diff --git a/tools/testing/selftests/kvm/lib/kvm_util_internal.h b/tools/testing/selftests/kvm/lib/kvm_util_internal.h index 2ef446520748..f07d383d03a1 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util_internal.h +++ b/tools/testing/selftests/kvm/lib/kvm_util_internal.h @@ -50,6 +50,8 @@ struct kvm_vm { vm_paddr_t pgd; vm_vaddr_t gdt; vm_vaddr_t tss; + vm_vaddr_t idt; + vm_vaddr_t handlers; }; struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid); diff --git a/tools/testing/selftests/kvm/lib/x86_64/handlers.S b/tools/testing/selftests/kvm/lib/x86_64/handlers.S new file mode 100644 index 000000000000..783d2c0fc741 --- /dev/null +++ b/tools/testing/selftests/kvm/lib/x86_64/handlers.S @@ -0,0 +1,83 @@ +handle_exception: + push %r15 + push %r14 + push %r13 + push %r12 + push %r11 + push %r10 + push %r9 + push %r8 + + push %rdi + push %rsi + push %rbp + sub $8, %rsp + push %rbx + push %rdx + push %rcx + push %rax + mov %rsp, %rdi + + call route_exception + + pop %rax + pop %rcx + pop %rdx + pop %rbx + add $8, %rsp + pop %rbp + pop %rsi + pop %rdi + pop %r8 + pop %r9 + pop %r10 + pop %r11 + pop %r12 + pop %r13 + pop %r14 + pop %r15 + + /* Discard vector and error code. */ + add $16, %rsp + iretq + +/* + * Build the handle_exception wrappers which push the vector/error code on the + * stack and an array of pointers to those wrappers. + */ +.pushsection .rodata +.globl idt_handlers +idt_handlers: +.popsection + +.macro HANDLERS has_error from to + vector = \from + .rept \to - \from + 1 + .align 8 + + /* Fetch current address and append it to idt_handlers. */ + current_handler = . +.pushsection .rodata +.quad current_handler +.popsection + + .if ! \has_error + pushq $0 + .endif + pushq $vector + jmp handle_exception + vector = vector + 1 + .endr +.endm + +.global idt_handler_code +idt_handler_code: + HANDLERS has_error=0 from=0 to=7 + HANDLERS has_error=1 from=8 to=8 + HANDLERS has_error=0 from=9 to=9 + HANDLERS has_error=1 from=10 to=14 + HANDLERS has_error=0 from=15 to=16 + HANDLERS has_error=1 from=17 to=17 + HANDLERS has_error=0 from=18 to=255 + +.section .note.GNU-stack, "", %progbits diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c index f6eb34eaa0d2..ff56753f205f 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c @@ -12,6 +12,13 @@ #include "../kvm_util_internal.h" #include "processor.h" +#ifndef NUM_INTERRUPTS +#define NUM_INTERRUPTS 256 +#endif + +#define DEFAULT_CODE_SELECTOR 0x8 +#define DEFAULT_DATA_SELECTOR 0x10 + /* Minimum physical address used for virtual translation tables. */ #define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000 @@ -392,11 +399,12 @@ static void kvm_seg_fill_gdt_64bit(struct kvm_vm *vm, struct kvm_segment *segp) desc->limit0 = segp->limit & 0xFFFF; desc->base0 = segp->base & 0xFFFF; desc->base1 = segp->base >> 16; - desc->s = segp->s; desc->type = segp->type; + desc->s = segp->s; desc->dpl = segp->dpl; desc->p = segp->present; desc->limit1 = segp->limit >> 16; + desc->avl = segp->avl; desc->l = segp->l; desc->db = segp->db; desc->g = segp->g; @@ -556,9 +564,9 @@ static void vcpu_setup(struct kvm_vm *vm, int vcpuid, int pgd_memslot, int gdt_m sregs.efer |= (EFER_LME | EFER_LMA | EFER_NX); kvm_seg_set_unusable(&sregs.ldt); - kvm_seg_set_kernel_code_64bit(vm, 0x8, &sregs.cs); - kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.ds); - kvm_seg_set_kernel_data_64bit(vm, 0x10, &sregs.es); + kvm_seg_set_kernel_code_64bit(vm, DEFAULT_CODE_SELECTOR, &sregs.cs); + kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.ds); + kvm_seg_set_kernel_data_64bit(vm, DEFAULT_DATA_SELECTOR, &sregs.es); kvm_setup_tss_64bit(vm, &sregs.tr, 0x18, gdt_memslot, pgd_memslot); break; @@ -843,6 +851,71 @@ void vcpu_set_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index, " rc: %i errno: %i", r, errno); } +/* + * _KVM Set Exit MSR + * + * Input Args: + * vm - Virtual Machine + * nmsrs - Number of msrs in msr_indices + * msr_indices[] - List of msrs. + * + * Output Args: None + * + * Return: The result of KVM_SET_EXIT_MSRS. + * + * Sets a list of MSRs that will force an exit to userspace when + * any of them are read from or written to by the guest. + */ +int _kvm_set_exit_msrs(struct kvm_vm *vm, uint32_t nmsrs, + uint32_t msr_indices[]) +{ + const uint32_t max_nmsrs = 256; + struct kvm_msr_list *msr_list; + uint32_t i; + int r; + + TEST_ASSERT(nmsrs <= max_nmsrs, + "'nmsrs' is too large. Max is %u, currently %u.\n", + max_nmsrs, nmsrs); + uint32_t msr_list_byte_size = sizeof(struct kvm_msr_list) + + (sizeof(msr_list->indices[0]) * nmsrs); + msr_list = alloca(msr_list_byte_size); + memset(msr_list, 0, msr_list_byte_size); + + msr_list->nmsrs = nmsrs; + for (i = 0; i < nmsrs; i++) + msr_list->indices[i] = msr_indices[i]; + + r = ioctl(vm->fd, KVM_SET_EXIT_MSRS, msr_list); + + return r; +} + +/* + * KVM Set Exit MSR + * + * Input Args: + * vm - Virtual Machine + * nmsrs - Number of msrs in msr_indices + * msr_indices[] - List of msrs. + * + * Output Args: None + * + * Return: None + * + * Sets a list of MSRs that will force an exit to userspace when + * any of them are read from or written to by the guest. + */ +void kvm_set_exit_msrs(struct kvm_vm *vm, uint32_t nmsrs, + uint32_t msr_indices[]) +{ + int r; + + r = _kvm_set_exit_msrs(vm, nmsrs, msr_indices); + TEST_ASSERT(r == 0, "KVM_SET_EXIT_MSRS IOCTL failed,\n" + " rc: %i errno: %i", r, errno); +} + void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) { va_list ap; @@ -1118,3 +1191,90 @@ void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits) *va_bits = (entry->eax >> 8) & 0xff; } } + +struct idt_entry { + uint16_t offset0; + uint16_t selector; + uint16_t ist : 3; + uint16_t : 5; + uint16_t type : 4; + uint16_t : 1; + uint16_t dpl : 2; + uint16_t p : 1; + uint16_t offset1; + uint32_t offset2; uint32_t reserved; +}; + +static void set_idt_entry(struct kvm_vm *vm, int vector, unsigned long addr, + int dpl, unsigned short selector) +{ + struct idt_entry *base = + (struct idt_entry *)addr_gva2hva(vm, vm->idt); + struct idt_entry *e = &base[vector]; + + memset(e, 0, sizeof(*e)); + e->offset0 = addr; + e->selector = selector; + e->ist = 0; + e->type = 14; + e->dpl = dpl; + e->p = 1; + e->offset1 = addr >> 16; + e->offset2 = addr >> 32; +} + +void kvm_exit_unexpected_vector(uint32_t value) +{ + outl(UNEXPECTED_VECTOR_PORT, value); +} + +void route_exception(struct ex_regs *regs) +{ + typedef void(*handler)(struct ex_regs *); + handler *handlers; + + handlers = (handler *)rdmsr(MSR_GS_BASE); + + if (handlers[regs->vector]) { + handlers[regs->vector](regs); + return; + } + + kvm_exit_unexpected_vector(regs->vector); +} + +void vm_init_descriptor_tables(struct kvm_vm *vm) +{ + extern void *idt_handlers; + int i; + + vm->idt = vm_vaddr_alloc(vm, getpagesize(), 0x2000, 0, 0); + vm->handlers = vm_vaddr_alloc(vm, 256 * sizeof(void *), 0x2000, 0, 0); + /* Handlers have the same address in both address spaces.*/ + for (i = 0; i < NUM_INTERRUPTS; i++) + set_idt_entry(vm, i, (unsigned long)(&idt_handlers)[i], 0, + DEFAULT_CODE_SELECTOR); +} + +void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid) +{ + struct kvm_sregs sregs; + + vcpu_sregs_get(vm, vcpuid, &sregs); + sregs.idt.base = vm->idt; + sregs.idt.limit = NUM_INTERRUPTS * sizeof(struct idt_entry) - 1; + sregs.gdt.base = vm->gdt; + sregs.gdt.limit = getpagesize() - 1; + /* Use GS Base to pass the pointer to the handlers to the guest.*/ + kvm_seg_set_kernel_data_64bit(NULL, DEFAULT_DATA_SELECTOR, &sregs.gs); + sregs.gs.base = (unsigned long) vm->handlers; + vcpu_sregs_set(vm, vcpuid, &sregs); +} + +void vm_handle_exception(struct kvm_vm *vm, int vector, + void (*handler)(struct ex_regs *)) +{ + vm_vaddr_t *handlers = (vm_vaddr_t *)addr_gva2hva(vm, vm->handlers); + + handlers[vector] = (vm_vaddr_t)handler; +} diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c index da4d89ad5419..a3489973e290 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c +++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c @@ -40,6 +40,9 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) struct kvm_run *run = vcpu_state(vm, vcpu_id); struct ucall ucall = {}; + if (uc) + memset(uc, 0, sizeof(*uc)); + if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) { struct kvm_regs regs; diff --git a/tools/testing/selftests/kvm/x86_64/userspace_msr_exit.c b/tools/testing/selftests/kvm/x86_64/userspace_msr_exit.c new file mode 100644 index 000000000000..44ddcd53a583 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/userspace_msr_exit.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020, Google LLC. + * + * Tests for exiting into userspace on registered MSRs + */ + +#define _GNU_SOURCE /* for program_invocation_short_name */ +#include + +#include "test_util.h" +#include "kvm_util.h" +#include "vmx.h" + +#define VCPU_ID 1 + +#define MSR_NON_EXISTENT 0x474f4f00 + +uint32_t msrs[] = { + /* Test an MSR the kernel knows about. */ + MSR_IA32_XSS, + /* Test an MSR the kernel doesn't know about. */ + MSR_IA32_FLUSH_CMD, + /* Test a fabricated MSR that no one knows about. */ + MSR_NON_EXISTENT, +}; +uint32_t nmsrs = ARRAY_SIZE(msrs); + +uint64_t msr_non_existent_data; +int guest_exception_count; + +/* + * Note: Force test_rdmsr() to not be inlined to prevent the labels, + * rdmsr_start and rdmsr_end, from being defined multiple times. + */ +static noinline uint64_t test_rdmsr(uint32_t msr) +{ + uint32_t a, d; + + guest_exception_count = 0; + + __asm__ __volatile__("rdmsr_start: rdmsr; rdmsr_end:" : + "=a"(a), "=d"(d) : "c"(msr) : "memory"); + + return a | ((uint64_t) d << 32); +} + +/* + * Note: Force test_wrmsr() to not be inlined to prevent the labels, + * wrmsr_start and wrmsr_end, from being defined multiple times. + */ +static noinline void test_wrmsr(uint32_t msr, uint64_t value) +{ + uint32_t a = value; + uint32_t d = value >> 32; + + guest_exception_count = 0; + + __asm__ __volatile__("wrmsr_start: wrmsr; wrmsr_end:" :: + "a"(a), "d"(d), "c"(msr) : "memory"); +} + +extern char rdmsr_start, rdmsr_end; +extern char wrmsr_start, wrmsr_end; + + +static void guest_code(void) +{ + uint64_t data; + + /* + * Test userspace intercepting rdmsr / wrmsr for MSR_IA32_XSS. + * + * A GP is thrown if anything other than 0 is written to + * MSR_IA32_XSS. + */ + data = test_rdmsr(MSR_IA32_XSS); + GUEST_ASSERT(data == 0); + GUEST_ASSERT(guest_exception_count == 0); + + test_wrmsr(MSR_IA32_XSS, 0); + GUEST_ASSERT(guest_exception_count == 0); + + test_wrmsr(MSR_IA32_XSS, 1); + GUEST_ASSERT(guest_exception_count == 1); + + /* + * Test userspace intercepting rdmsr / wrmsr for MSR_IA32_FLUSH_CMD. + * + * A GP is thrown if MSR_IA32_FLUSH_CMD is read + * from or if a value other than 1 is written to it. + */ + test_rdmsr(MSR_IA32_FLUSH_CMD); + GUEST_ASSERT(guest_exception_count == 1); + + test_wrmsr(MSR_IA32_FLUSH_CMD, 0); + GUEST_ASSERT(guest_exception_count == 1); + + test_wrmsr(MSR_IA32_FLUSH_CMD, 1); + GUEST_ASSERT(guest_exception_count == 0); + + /* + * Test userspace intercepting rdmsr / wrmsr for MSR_NON_EXISTENT. + * + * Test that a fabricated MSR can pass through the kernel + * and be handled in userspace. + */ + test_wrmsr(MSR_NON_EXISTENT, 2); + GUEST_ASSERT(guest_exception_count == 0); + + data = test_rdmsr(MSR_NON_EXISTENT); + GUEST_ASSERT(data == 2); + GUEST_ASSERT(guest_exception_count == 0); + + GUEST_DONE(); +} + +static void guest_gp_handler(struct ex_regs *regs) +{ + if (regs->rip == (uintptr_t)&rdmsr_start) { + regs->rip = (uintptr_t)&rdmsr_end; + regs->rax = 0; + regs->rdx = 0; + } else if (regs->rip == (uintptr_t)&wrmsr_start) { + regs->rip = (uintptr_t)&wrmsr_end; + } else { + GUEST_ASSERT(!"RIP is at an unknown location!"); + } + + ++guest_exception_count; +} + +static void run_guest(struct kvm_vm *vm) +{ + int rc; + + rc = _vcpu_run(vm, VCPU_ID); + TEST_ASSERT(rc == 0, "vcpu_run failed: %d\n", rc); +} + +static void check_for_guest_assert(struct kvm_vm *vm) +{ + struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct ucall uc; + + if (run->exit_reason == KVM_EXIT_IO && + get_ucall(vm, VCPU_ID, &uc) == UCALL_ABORT) { + TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], + __FILE__, uc.args[1]); + } +} + +static void process_rdmsr(struct kvm_vm *vm, uint32_t msr_index) +{ + struct kvm_run *run = vcpu_state(vm, VCPU_ID); + + check_for_guest_assert(vm); + + TEST_ASSERT(run->exit_reason == KVM_EXIT_X86_RDMSR, + "Unexpected exit reason: %u (%s),\n", + run->exit_reason, + exit_reason_str(run->exit_reason)); + TEST_ASSERT(run->msr.index == msr_index, + "Unexpected msr (0x%04x), expected 0x%04x", + run->msr.index, msr_index); + + switch (run->msr.index) { + case MSR_IA32_XSS: + run->msr.data = 0; + break; + case MSR_IA32_FLUSH_CMD: + run->msr.inject_gp = 1; + break; + case MSR_NON_EXISTENT: + run->msr.data = msr_non_existent_data; + break; + default: + TEST_ASSERT(false, "Unexpected MSR: 0x%04x", run->msr.index); + } +} + +static void process_wrmsr(struct kvm_vm *vm, uint32_t msr_index) +{ + struct kvm_run *run = vcpu_state(vm, VCPU_ID); + + check_for_guest_assert(vm); + + TEST_ASSERT(run->exit_reason == KVM_EXIT_X86_WRMSR, + "Unexpected exit reason: %u (%s),\n", + run->exit_reason, + exit_reason_str(run->exit_reason)); + TEST_ASSERT(run->msr.index == msr_index, + "Unexpected msr (0x%04x), expected 0x%04x", + run->msr.index, msr_index); + + switch (run->msr.index) { + case MSR_IA32_XSS: + if (run->msr.data != 0) + run->msr.inject_gp = 1; + break; + case MSR_IA32_FLUSH_CMD: + if (run->msr.data != 1) + run->msr.inject_gp = 1; + break; + case MSR_NON_EXISTENT: + msr_non_existent_data = run->msr.data; + break; + default: + TEST_ASSERT(false, "Unexpected MSR: 0x%04x", run->msr.index); + } +} + +static void process_ucall_done(struct kvm_vm *vm) +{ + struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct ucall uc; + + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "Unexpected exit reason: %u (%s)", + run->exit_reason, + exit_reason_str(run->exit_reason)); + + TEST_ASSERT(get_ucall(vm, VCPU_ID, &uc) == UCALL_DONE, + "Unexpected ucall command: %lu, expected UCALL_DONE (%d)", + uc.cmd, UCALL_DONE); +} + +static void run_guest_then_process_rdmsr(struct kvm_vm *vm, uint32_t msr_index) +{ + run_guest(vm); + process_rdmsr(vm, msr_index); +} + +static void run_guest_then_process_wrmsr(struct kvm_vm *vm, uint32_t msr_index) +{ + run_guest(vm); + process_wrmsr(vm, msr_index); +} + +static void run_guest_then_process_ucall_done(struct kvm_vm *vm) +{ + run_guest(vm); + process_ucall_done(vm); +} + +int main(int argc, char *argv[]) +{ + struct kvm_vm *vm; + + vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); + kvm_vm_elf_load(vm, program_invocation_name, 0, 0); + vm_create_irqchip(vm); + + kvm_set_exit_msrs(vm, nmsrs, msrs); + + vm_vcpu_add_default(vm, VCPU_ID, guest_code); + + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(vm, VCPU_ID); + + vm_handle_exception(vm, GP_VECTOR, guest_gp_handler); + + /* Process guest code userspace exits */ + run_guest_then_process_rdmsr(vm, MSR_IA32_XSS); + run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); + run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); + + run_guest_then_process_rdmsr(vm, MSR_IA32_FLUSH_CMD); + run_guest_then_process_wrmsr(vm, MSR_IA32_FLUSH_CMD); + run_guest_then_process_wrmsr(vm, MSR_IA32_FLUSH_CMD); + + run_guest_then_process_wrmsr(vm, MSR_NON_EXISTENT); + run_guest_then_process_rdmsr(vm, MSR_NON_EXISTENT); + + run_guest_then_process_ucall_done(vm); + + kvm_vm_free(vm); + return 0; +} From patchwork Mon Aug 10 20:11:34 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aaron Lewis X-Patchwork-Id: 11708119 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4B9FB1392 for ; Mon, 10 Aug 2020 20:12:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 231042065D for ; Mon, 10 Aug 2020 20:12:12 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="RqFCi1IO" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726606AbgHJUMK (ORCPT ); Mon, 10 Aug 2020 16:12:10 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51498 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726640AbgHJUMG (ORCPT ); Mon, 10 Aug 2020 16:12:06 -0400 Received: from mail-pg1-x54a.google.com (mail-pg1-x54a.google.com [IPv6:2607:f8b0:4864:20::54a]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6004CC061756 for ; Mon, 10 Aug 2020 13:12:05 -0700 (PDT) Received: by mail-pg1-x54a.google.com with SMTP id e4so7262720pgv.7 for ; Mon, 10 Aug 2020 13:12:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=ZxFFFXrx2tGXqpsPuWsQeJ3BtZLBxHQDGl2m77pFjXs=; b=RqFCi1IOB8UTNhQN6jXDkkSUjhwQE0VNUfAnS9LehubhtSIUIbEUF1t6ymY2xpx5vv uKz8tucfXV66jT2esNjgZGZkZyowR1UOYPkbx8X6/MNd5zPJkRahHPeLzl3Qd/nuMvlB zngzei6HygIPYFLfIdLnBbvQowQ4aG6cYykuuvVUgSVDQ+5XFpdwCemUsPotDJKYOF/2 dAeMK1AJwgiMVjGjw4agPdbFEu5LMiegaYB1SIhKUVXd9pRXc2tJIICsWDaV1nRcqpkT QKkg+v7u3RYrE3xFMB7G3DI6w8EsUYB+KKNaKxigc8nDQgPJtTKl+UmWetfhcQsV7rWL 4t5w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=ZxFFFXrx2tGXqpsPuWsQeJ3BtZLBxHQDGl2m77pFjXs=; b=SXmkJX9QRHI9f+uRbub564BOCLsMDpepeO3QuB8jUMf1xkJIiewLSivWsyk4U0f02d uq29KwdgKircWp922HC5mBL87pzW0xYo5JXNwtUd1pvpkNba5urKA2Pq4AGQuj9P90J8 237AZA+42OVYOXk024jwit9YTNsRPBOsKtQafrKsAHHQ6EkZwOjfi9uzsRHroYy1lYuS SCqATitopmPdstSOOIZD99B8tw4M21bC7jqfsSEXmm9PWa2O2PzTVlHk3CL/gwLqTgA6 Ev8DO6VHVCEsWw5CvjCDhjM1FzDM3J8tzeHRkAzpqoRzbn1fxwdKFX2ITkrXo5vjmJXz 3OAw== X-Gm-Message-State: AOAM533/yM+vH7nkH8D0DLpNxg8kPaRp/5YlvLEFhw7nyB++B3kgAlkQ uMi50nTsx1ARS+12Rv2JgzBd4TRRf9hv07zZ X-Google-Smtp-Source: ABdhPJwBGAMRlnABY04i2CCAKtXgQtShM7VWlRBKeyP7LmIY9P9OeAcJLi7g1zlhT5sQg1PtzEL/SdvVRK29GKnU X-Received: by 2002:a65:668e:: with SMTP id b14mr23265134pgw.153.1597090324653; Mon, 10 Aug 2020 13:12:04 -0700 (PDT) Date: Mon, 10 Aug 2020 13:11:34 -0700 In-Reply-To: <20200810201134.2031613-1-aaronlewis@google.com> Message-Id: <20200810201134.2031613-9-aaronlewis@google.com> Mime-Version: 1.0 References: <20200810201134.2031613-1-aaronlewis@google.com> X-Mailer: git-send-email 2.28.0.236.gb10cc79966-goog Subject: [PATCH v2 8/8] selftests: kvm: Add emulated rdmsr, wrmsr tests From: Aaron Lewis To: jmattson@google.com, graf@amazon.com Cc: pshier@google.com, oupton@google.com, kvm@vger.kernel.org, Aaron Lewis Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org Add tests to exercise the code paths for em_{rdmsr,wrmsr} and emulator_{get,set}_msr. For the generic instruction emulator to work the module parameter kvm.force_emulation_prefix=1 has to be enabled. If it isn't the tests will be skipped. Signed-off-by: Aaron Lewis --- .../selftests/kvm/x86_64/userspace_msr_exit.c | 158 +++++++++++++++++- 1 file changed, 150 insertions(+), 8 deletions(-) diff --git a/tools/testing/selftests/kvm/x86_64/userspace_msr_exit.c b/tools/testing/selftests/kvm/x86_64/userspace_msr_exit.c index 44ddcd53a583..ae96022b6ae8 100644 --- a/tools/testing/selftests/kvm/x86_64/userspace_msr_exit.c +++ b/tools/testing/selftests/kvm/x86_64/userspace_msr_exit.c @@ -12,8 +12,12 @@ #include "kvm_util.h" #include "vmx.h" -#define VCPU_ID 1 +/* Forced emulation prefix, used to invoke the emulator unconditionally. */ +#define KVM_FEP "ud2; .byte 'k', 'v', 'm';" +#define KVM_FEP_LENGTH 5 +static int fep_available = 1; +#define VCPU_ID 1 #define MSR_NON_EXISTENT 0x474f4f00 uint32_t msrs[] = { @@ -63,6 +67,39 @@ static noinline void test_wrmsr(uint32_t msr, uint64_t value) extern char rdmsr_start, rdmsr_end; extern char wrmsr_start, wrmsr_end; +/* + * Note: Force test_em_rdmsr() to not be inlined to prevent the labels, + * rdmsr_start and rdmsr_end, from being defined multiple times. + */ +static noinline uint64_t test_em_rdmsr(uint32_t msr) +{ + uint32_t a, d; + + guest_exception_count = 0; + + __asm__ __volatile__(KVM_FEP "em_rdmsr_start: rdmsr; em_rdmsr_end:" : + "=a"(a), "=d"(d) : "c"(msr) : "memory"); + + return a | ((uint64_t) d << 32); +} + +/* + * Note: Force test_em_wrmsr() to not be inlined to prevent the labels, + * wrmsr_start and wrmsr_end, from being defined multiple times. + */ +static noinline void test_em_wrmsr(uint32_t msr, uint64_t value) +{ + uint32_t a = value; + uint32_t d = value >> 32; + + guest_exception_count = 0; + + __asm__ __volatile__(KVM_FEP "em_wrmsr_start: wrmsr; em_wrmsr_end:" :: + "a"(a), "d"(d), "c"(msr) : "memory"); +} + +extern char em_rdmsr_start, em_rdmsr_end; +extern char em_wrmsr_start, em_wrmsr_end; static void guest_code(void) { @@ -112,17 +149,55 @@ static void guest_code(void) GUEST_ASSERT(data == 2); GUEST_ASSERT(guest_exception_count == 0); + /* + * Test to see if the instruction emulator is available (ie: the module + * parameter 'kvm.force_emulation_prefix=1' is set). This instruction + * will #UD if it isn't available. + */ + __asm__ __volatile__(KVM_FEP "nop"); + + if (fep_available) { + /* Let userspace know we aren't done. */ + GUEST_SYNC(0); + + /* + * Now run the same tests with the instruction emulator. + */ + data = test_em_rdmsr(MSR_IA32_XSS); + GUEST_ASSERT(data == 0); + GUEST_ASSERT(guest_exception_count == 0); + test_em_wrmsr(MSR_IA32_XSS, 0); + GUEST_ASSERT(guest_exception_count == 0); + test_em_wrmsr(MSR_IA32_XSS, 1); + GUEST_ASSERT(guest_exception_count == 1); + + test_em_rdmsr(MSR_IA32_FLUSH_CMD); + GUEST_ASSERT(guest_exception_count == 1); + test_em_wrmsr(MSR_IA32_FLUSH_CMD, 0); + GUEST_ASSERT(guest_exception_count == 1); + test_em_wrmsr(MSR_IA32_FLUSH_CMD, 1); + GUEST_ASSERT(guest_exception_count == 0); + + test_em_wrmsr(MSR_NON_EXISTENT, 2); + GUEST_ASSERT(guest_exception_count == 0); + data = test_em_rdmsr(MSR_NON_EXISTENT); + GUEST_ASSERT(data == 2); + GUEST_ASSERT(guest_exception_count == 0); + } + GUEST_DONE(); } -static void guest_gp_handler(struct ex_regs *regs) +static void __guest_gp_handler(struct ex_regs *regs, + char *r_start, char *r_end, + char *w_start, char *w_end) { - if (regs->rip == (uintptr_t)&rdmsr_start) { - regs->rip = (uintptr_t)&rdmsr_end; + if (regs->rip == (uintptr_t)r_start) { + regs->rip = (uintptr_t)r_end; regs->rax = 0; regs->rdx = 0; - } else if (regs->rip == (uintptr_t)&wrmsr_start) { - regs->rip = (uintptr_t)&wrmsr_end; + } else if (regs->rip == (uintptr_t)w_start) { + regs->rip = (uintptr_t)w_end; } else { GUEST_ASSERT(!"RIP is at an unknown location!"); } @@ -130,6 +205,24 @@ static void guest_gp_handler(struct ex_regs *regs) ++guest_exception_count; } +static void guest_gp_handler(struct ex_regs *regs) +{ + __guest_gp_handler(regs, &rdmsr_start, &rdmsr_end, + &wrmsr_start, &wrmsr_end); +} + +static void guest_fep_gp_handler(struct ex_regs *regs) +{ + __guest_gp_handler(regs, &em_rdmsr_start, &em_rdmsr_end, + &em_wrmsr_start, &em_wrmsr_end); +} + +static void guest_ud_handler(struct ex_regs *regs) +{ + fep_available = 0; + regs->rip += KVM_FEP_LENGTH; +} + static void run_guest(struct kvm_vm *vm) { int rc; @@ -225,6 +318,32 @@ static void process_ucall_done(struct kvm_vm *vm) uc.cmd, UCALL_DONE); } +static uint64_t process_ucall(struct kvm_vm *vm) +{ + struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct ucall uc = {}; + + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "Unexpected exit reason: %u (%s)", + run->exit_reason, + exit_reason_str(run->exit_reason)); + + switch (get_ucall(vm, VCPU_ID, &uc)) { + case UCALL_SYNC: + break; + case UCALL_ABORT: + check_for_guest_assert(vm); + break; + case UCALL_DONE: + process_ucall_done(vm); + break; + default: + TEST_ASSERT(false, "Unexpected ucall"); + } + + return uc.cmd; +} + static void run_guest_then_process_rdmsr(struct kvm_vm *vm, uint32_t msr_index) { run_guest(vm); @@ -260,7 +379,7 @@ int main(int argc, char *argv[]) vm_handle_exception(vm, GP_VECTOR, guest_gp_handler); - /* Process guest code userspace exits */ + /* Process guest code userspace exits. */ run_guest_then_process_rdmsr(vm, MSR_IA32_XSS); run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); @@ -272,7 +391,30 @@ int main(int argc, char *argv[]) run_guest_then_process_wrmsr(vm, MSR_NON_EXISTENT); run_guest_then_process_rdmsr(vm, MSR_NON_EXISTENT); - run_guest_then_process_ucall_done(vm); + vm_handle_exception(vm, UD_VECTOR, guest_ud_handler); + run_guest(vm); + vm_handle_exception(vm, UD_VECTOR, NULL); + + if (process_ucall(vm) != UCALL_DONE) { + vm_handle_exception(vm, GP_VECTOR, guest_fep_gp_handler); + + /* Process emulated rdmsr and wrmsr instructions. */ + run_guest_then_process_rdmsr(vm, MSR_IA32_XSS); + run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); + run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); + + run_guest_then_process_rdmsr(vm, MSR_IA32_FLUSH_CMD); + run_guest_then_process_wrmsr(vm, MSR_IA32_FLUSH_CMD); + run_guest_then_process_wrmsr(vm, MSR_IA32_FLUSH_CMD); + + run_guest_then_process_wrmsr(vm, MSR_NON_EXISTENT); + run_guest_then_process_rdmsr(vm, MSR_NON_EXISTENT); + + /* Confirm the guest completed without issues. */ + run_guest_then_process_ucall_done(vm); + } else { + printf("To run the instruction emulated tests set the module parameter 'kvm.force_emulation_prefix=1'\n"); + } kvm_vm_free(vm); return 0;