linux-kselftest.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michael Roth <michael.roth@amd.com>
To: <linux-kselftest@vger.kernel.org>
Cc: <kvm@vger.kernel.org>, <linux-kernel@vger.kernel.org>,
	<x86@kernel.org>, Nathan Tempelman <natet@google.com>,
	Marc Orr <marcorr@google.com>,
	"Steve Rutherford" <srutherford@google.com>,
	Sean Christopherson <seanjc@google.com>,
	Mingwei Zhang <mizhang@google.com>,
	Brijesh Singh <brijesh.singh@amd.com>,
	Tom Lendacky <thomas.lendacky@amd.com>,
	Varad Gautam <varad.gautam@suse.com>,
	Shuah Khan <shuah@kernel.org>,
	Vitaly Kuznetsov <vkuznets@redhat.com>,
	"David Woodhouse" <dwmw@amazon.co.uk>,
	Ricardo Koller <ricarkol@google.com>,
	"Jim Mattson" <jmattson@google.com>,
	Joerg Roedel <joro@8bytes.org>,
	"Thomas Gleixner" <tglx@linutronix.de>,
	Ingo Molnar <mingo@redhat.com>, "Borislav Petkov" <bp@alien8.de>,
	"H . Peter Anvin" <hpa@zytor.com>,
	Krish Sadhukhan <krish.sadhukhan@oracle.com>,
	Peter Gonda <pgonda@google.com>
Subject: [PATCH v2 12/13] KVM: selftests: add library for handling SEV-ES-related exits
Date: Thu, 16 Dec 2021 11:13:57 -0600	[thread overview]
Message-ID: <20211216171358.61140-13-michael.roth@amd.com> (raw)
In-Reply-To: <20211216171358.61140-1-michael.roth@amd.com>

Add (or copy from kernel) routines related to handling #VC exceptions
(only for cpuid currently) or issuing vmgexits. These will be used
mostly by guest code.

Some of this copied code, like DEFINE_GHCB_ACCESSORS, generate
GCC/clang warnings due to -Waddress-of-packed-member, so compile with
-Wno-address-of-packed-member like the kernel does.

Signed-off-by: Michael Roth <michael.roth@amd.com>
---
 tools/testing/selftests/kvm/Makefile          |   4 +-
 .../kvm/include/x86_64/sev_exitlib.h          |  14 +
 .../selftests/kvm/include/x86_64/svm.h        |  35 +++
 .../selftests/kvm/include/x86_64/svm_util.h   |   1 +
 .../selftests/kvm/lib/x86_64/sev_exitlib.c    | 249 ++++++++++++++++++
 5 files changed, 301 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/kvm/include/x86_64/sev_exitlib.h
 create mode 100644 tools/testing/selftests/kvm/lib/x86_64/sev_exitlib.c

diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 6f250e190fde..56f845523a03 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -40,7 +40,7 @@ SEV_PATH=/dev/sev
 
 LIBKVM = lib/assert.c lib/elf.c lib/io.c lib/kvm_util.c lib/rbtree.c lib/sparsebit.c lib/test_util.c lib/guest_modes.c lib/perf_test_util.c lib/ucall_common.c
 LIBKVM_x86_64 = lib/x86_64/apic.c 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_x86_64 += lib/x86_64/sev.c
+LIBKVM_x86_64 += lib/x86_64/sev.c lib/x86_64/sev_exitlib.c
 LIBKVM_aarch64 = lib/aarch64/processor.c lib/aarch64/ucall.c lib/aarch64/handlers.S lib/aarch64/spinlock.c lib/aarch64/gic.c lib/aarch64/gic_v3.c lib/aarch64/vgic.c
 LIBKVM_s390x = lib/s390x/processor.c lib/s390x/ucall.c lib/s390x/diag318_test_handler.c
 
@@ -142,7 +142,7 @@ CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
 	-fno-stack-protector -fno-PIE -I$(LINUX_TOOL_INCLUDE) \
 	-I$(LINUX_TOOL_ARCH_INCLUDE) -I$(LINUX_HDR_PATH) -Iinclude \
 	-I$(<D) -Iinclude/$(UNAME_M) -I.. \
-	-DSEV_DEV_PATH=\"$(SEV_PATH)\"
+	-DSEV_DEV_PATH=\"$(SEV_PATH)\" -Wno-address-of-packed-member
 
 no-pie-option := $(call try-run, echo 'int main() { return 0; }' | \
         $(CC) -Werror -no-pie -x c - -o "$$TMP", -no-pie)
diff --git a/tools/testing/selftests/kvm/include/x86_64/sev_exitlib.h b/tools/testing/selftests/kvm/include/x86_64/sev_exitlib.h
new file mode 100644
index 000000000000..4b67b4004dfa
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/x86_64/sev_exitlib.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * VC/vmgexit/GHCB-related helpers for SEV-ES/SEV-SNP guests.
+ *
+ * Copyright (C) 2021 Advanced Micro Devices
+ */
+
+#ifndef SELFTEST_KVM_SEV_EXITLIB_H
+#define SELFTEST_KVM_SEV_EXITLIB_H
+
+int sev_es_handle_vc(void *ghcb, u64 ghcb_gpa, struct ex_regs *regs);
+void sev_es_terminate(int reason);
+
+#endif /* SELFTEST_KVM_SEV_EXITLIB_H */
diff --git a/tools/testing/selftests/kvm/include/x86_64/svm.h b/tools/testing/selftests/kvm/include/x86_64/svm.h
index f4ea2355dbc2..d633caea4b7d 100644
--- a/tools/testing/selftests/kvm/include/x86_64/svm.h
+++ b/tools/testing/selftests/kvm/include/x86_64/svm.h
@@ -204,6 +204,41 @@ struct __attribute__ ((__packed__)) vmcb_save_area {
 	u64 br_to;
 	u64 last_excp_from;
 	u64 last_excp_to;
+
+	/*
+	 * The following part of the save area is valid only for
+	 * SEV-ES guests when referenced through the GHCB or for
+	 * saving to the host save area.
+	 */
+	u8 reserved_7[80];
+	u32 pkru;
+	u8 reserved_7a[20];
+	u64 reserved_8;		/* rax already available at 0x01f8 */
+	u64 rcx;
+	u64 rdx;
+	u64 rbx;
+	u64 reserved_9;		/* rsp already available at 0x01d8 */
+	u64 rbp;
+	u64 rsi;
+	u64 rdi;
+	u64 r8;
+	u64 r9;
+	u64 r10;
+	u64 r11;
+	u64 r12;
+	u64 r13;
+	u64 r14;
+	u64 r15;
+	u8 reserved_10[16];
+	u64 sw_exit_code;
+	u64 sw_exit_info_1;
+	u64 sw_exit_info_2;
+	u64 sw_scratch;
+	u64 sev_features;
+	u8 reserved_11[48];
+	u64 xcr0;
+	u8 valid_bitmap[16];
+	u64 x87_state_gpa;
 };
 
 struct __attribute__ ((__packed__)) vmcb {
diff --git a/tools/testing/selftests/kvm/include/x86_64/svm_util.h b/tools/testing/selftests/kvm/include/x86_64/svm_util.h
index 587fbe408b99..29f586775c86 100644
--- a/tools/testing/selftests/kvm/include/x86_64/svm_util.h
+++ b/tools/testing/selftests/kvm/include/x86_64/svm_util.h
@@ -16,6 +16,7 @@
 #define CPUID_SVM_BIT		2
 #define CPUID_SVM		BIT_ULL(CPUID_SVM_BIT)
 
+#define SVM_EXIT_CPUID		0x072
 #define SVM_EXIT_VMMCALL	0x081
 
 struct svm_test_data {
diff --git a/tools/testing/selftests/kvm/lib/x86_64/sev_exitlib.c b/tools/testing/selftests/kvm/lib/x86_64/sev_exitlib.c
new file mode 100644
index 000000000000..b3f7b0297e5b
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86_64/sev_exitlib.c
@@ -0,0 +1,249 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * GHCB/#VC/instruction helpers for use with SEV-ES/SEV-SNP guests.
+ *
+ * Partially copied from arch/x86/kernel/sev*.c
+ *
+ * Copyright (C) 2021 Advanced Micro Devices
+ */
+
+#include <linux/bitops.h>
+#include <kvm_util.h>			/* needed by kvm_util_internal.h */
+#include "../kvm_util_internal.h"	/* needed by processor.h */
+#include "processor.h"			/* for struct ex_regs */
+#include "svm_util.h"			/* for additional SVM_EXIT_* definitions */
+#include "svm.h"			/* for VMCB/VMSA layout */
+#include "sev_exitlib.h"
+
+#define PAGE_SHIFT 12
+
+#define MSR_SEV_ES_GHCB 0xc0010130
+
+#define VMGEXIT() { asm volatile("rep; vmmcall\n\r"); }
+
+#define GHCB_PROTOCOL_MAX	1
+#define GHCB_DEFAULT_USAGE	0
+
+/* Guest-requested termination codes */
+#define GHCB_TERMINATE 0x100UL
+#define GHCB_TERMINATE_REASON(reason_set, reason_val)	\
+	(((((u64)reason_set) &  0x7) << 12) |			\
+	 ((((u64)reason_val) & 0xff) << 16))
+
+#define GHCB_TERMINATE_REASON_UNSPEC 0
+
+/* GHCB MSR protocol for CPUID */
+#define GHCB_CPUID_REQ_EAX 0
+#define GHCB_CPUID_REQ_EBX 1
+#define GHCB_CPUID_REQ_ECX 2
+#define GHCB_CPUID_REQ_EDX 3
+#define GHCB_CPUID_REQ_CODE 0x4UL
+#define GHCB_CPUID_REQ(fn, reg) \
+	(GHCB_CPUID_REQ_CODE | (((uint64_t)reg & 3) << 30) | (((uint64_t)fn) << 32))
+#define GHCB_CPUID_RESP_CODE 0x5UL
+#define GHCB_CPUID_RESP(resp) ((resp) & 0xfff)
+
+/* GHCB MSR protocol for GHCB registration */
+#define GHCB_REG_GPA_REQ_CODE 0x12UL
+#define GHCB_REG_GPA_REQ(gfn) \
+	(((unsigned long)((gfn) & GENMASK_ULL(51, 0)) << 12) | GHCB_REG_GPA_REQ_CODE)
+#define GHCB_REG_GPA_RESP_CODE 0x13UL
+#define GHCB_REG_GPA_RESP(resp) ((resp) & GENMASK_ULL(11, 0))
+#define GHCB_REG_GPA_RESP_VAL(resp) ((resp) >> 12)
+
+/* GHCB format/accessors */
+
+struct ghcb {
+	struct vmcb_save_area save;
+	u8 reserved_save[2048 - sizeof(struct vmcb_save_area)];
+	u8 shared_buffer[2032];
+	u8 reserved_1[10];
+	u16 protocol_version;
+	u32 ghcb_usage;
+};
+
+#define GHCB_BITMAP_IDX(field)							\
+	(offsetof(struct vmcb_save_area, field) / sizeof(u64))
+
+#define DEFINE_GHCB_ACCESSORS(field)						\
+	static inline bool ghcb_##field##_is_valid(const struct ghcb *ghcb)	\
+	{									\
+		return test_bit(GHCB_BITMAP_IDX(field),				\
+				(unsigned long *)&ghcb->save.valid_bitmap);	\
+	}									\
+										\
+	static inline u64 ghcb_get_##field(struct ghcb *ghcb)			\
+	{									\
+		return ghcb->save.field;					\
+	}									\
+										\
+	static inline u64 ghcb_get_##field##_if_valid(struct ghcb *ghcb)	\
+	{									\
+		return ghcb_##field##_is_valid(ghcb) ? ghcb->save.field : 0;	\
+	}									\
+										\
+	static inline void ghcb_set_##field(struct ghcb *ghcb, u64 value)	\
+	{									\
+		__set_bit(GHCB_BITMAP_IDX(field),				\
+			  (unsigned long *)&ghcb->save.valid_bitmap);		\
+		ghcb->save.field = value;					\
+	}
+
+DEFINE_GHCB_ACCESSORS(cpl)
+DEFINE_GHCB_ACCESSORS(rip)
+DEFINE_GHCB_ACCESSORS(rsp)
+DEFINE_GHCB_ACCESSORS(rax)
+DEFINE_GHCB_ACCESSORS(rcx)
+DEFINE_GHCB_ACCESSORS(rdx)
+DEFINE_GHCB_ACCESSORS(rbx)
+DEFINE_GHCB_ACCESSORS(rbp)
+DEFINE_GHCB_ACCESSORS(rsi)
+DEFINE_GHCB_ACCESSORS(rdi)
+DEFINE_GHCB_ACCESSORS(r8)
+DEFINE_GHCB_ACCESSORS(r9)
+DEFINE_GHCB_ACCESSORS(r10)
+DEFINE_GHCB_ACCESSORS(r11)
+DEFINE_GHCB_ACCESSORS(r12)
+DEFINE_GHCB_ACCESSORS(r13)
+DEFINE_GHCB_ACCESSORS(r14)
+DEFINE_GHCB_ACCESSORS(r15)
+DEFINE_GHCB_ACCESSORS(sw_exit_code)
+DEFINE_GHCB_ACCESSORS(sw_exit_info_1)
+DEFINE_GHCB_ACCESSORS(sw_exit_info_2)
+DEFINE_GHCB_ACCESSORS(sw_scratch)
+DEFINE_GHCB_ACCESSORS(xcr0)
+
+static uint64_t sev_es_rdmsr_ghcb(void)
+{
+	uint64_t lo, hi;
+
+	asm volatile("rdmsr"
+		     : "=a" (lo), "=d" (hi)
+		     : "c" (MSR_SEV_ES_GHCB));
+
+	return ((hi << 32) | lo);
+}
+
+static void sev_es_wrmsr_ghcb(uint64_t val)
+{
+	uint64_t lo, hi;
+
+	lo = val & 0xFFFFFFFF;
+	hi = val >> 32;
+
+	asm volatile("wrmsr"
+		     :: "c" (MSR_SEV_ES_GHCB), "a" (lo), "d" (hi)
+		     : "memory");
+}
+
+void sev_es_terminate(int reason)
+{
+	uint64_t val = GHCB_TERMINATE;
+
+	val |= GHCB_TERMINATE_REASON(2, reason);
+
+	sev_es_wrmsr_ghcb(val);
+	VMGEXIT();
+
+	while (true)
+		asm volatile("hlt" : : : "memory");
+}
+
+static int sev_es_ghcb_hv_call(struct ghcb *ghcb, u64 ghcb_gpa, u64 exit_code)
+{
+	ghcb->protocol_version = GHCB_PROTOCOL_MAX;
+	ghcb->ghcb_usage = GHCB_DEFAULT_USAGE;
+
+	ghcb_set_sw_exit_code(ghcb, exit_code);
+	ghcb_set_sw_exit_info_1(ghcb, 0);
+	ghcb_set_sw_exit_info_2(ghcb, 0);
+
+	sev_es_wrmsr_ghcb(ghcb_gpa);
+
+	VMGEXIT();
+
+	/* Only #VC exceptions are currently handled. */
+	if ((ghcb->save.sw_exit_info_1 & 0xffffffff) == 1)
+		sev_es_terminate(GHCB_TERMINATE_REASON_UNSPEC);
+
+	return 0;
+}
+
+static int handle_vc_cpuid(struct ghcb *ghcb, u64 ghcb_gpa, struct ex_regs *regs)
+{
+	int ret;
+
+	ghcb_set_rax(ghcb, regs->rax);
+	ghcb_set_rcx(ghcb, regs->rcx);
+
+	/* ignore additional XSAVE states for now */
+	ghcb_set_xcr0(ghcb, 1);
+
+	ret = sev_es_ghcb_hv_call(ghcb, ghcb_gpa, SVM_EXIT_CPUID);
+	if (ret)
+		return ret;
+
+	if (!(ghcb_rax_is_valid(ghcb) &&
+	      ghcb_rbx_is_valid(ghcb) &&
+	      ghcb_rcx_is_valid(ghcb) &&
+	      ghcb_rdx_is_valid(ghcb)))
+		return 1;
+
+	regs->rax = ghcb->save.rax;
+	regs->rbx = ghcb->save.rbx;
+	regs->rcx = ghcb->save.rcx;
+	regs->rdx = ghcb->save.rdx;
+
+	regs->rip += 2;
+
+	return 0;
+}
+
+static int handle_msr_vc_cpuid(struct ex_regs *regs)
+{
+	uint32_t fn = regs->rax & 0xFFFFFFFF;
+	uint64_t resp;
+
+	sev_es_wrmsr_ghcb(GHCB_CPUID_REQ(fn, GHCB_CPUID_REQ_EAX));
+	VMGEXIT();
+	resp = sev_es_rdmsr_ghcb();
+	if (GHCB_CPUID_RESP(resp) != GHCB_CPUID_RESP_CODE)
+		return 1;
+	regs->rax = resp >> 32;
+
+	sev_es_wrmsr_ghcb(GHCB_CPUID_REQ(fn, GHCB_CPUID_REQ_EBX));
+	VMGEXIT();
+	resp = sev_es_rdmsr_ghcb();
+	if (GHCB_CPUID_RESP(resp) != GHCB_CPUID_RESP_CODE)
+		return 1;
+	regs->rbx = resp >> 32;
+
+	sev_es_wrmsr_ghcb(GHCB_CPUID_REQ(fn, GHCB_CPUID_REQ_ECX));
+	VMGEXIT();
+	resp = sev_es_rdmsr_ghcb();
+	if (GHCB_CPUID_RESP(resp) != GHCB_CPUID_RESP_CODE)
+		return 1;
+	regs->rcx = resp >> 32;
+
+	sev_es_wrmsr_ghcb(GHCB_CPUID_REQ(fn, GHCB_CPUID_REQ_EDX));
+	VMGEXIT();
+	resp = sev_es_rdmsr_ghcb();
+	if (GHCB_CPUID_RESP(resp) != GHCB_CPUID_RESP_CODE)
+		return 1;
+	regs->rdx = resp >> 32;
+
+	regs->rip += 2;
+
+	return 0;
+}
+
+int sev_es_handle_vc(void *ghcb, u64 ghcb_gpa, struct ex_regs *regs)
+{
+	if (regs->error_code != SVM_EXIT_CPUID)
+		return 1;
+
+	if (!ghcb)
+		return handle_msr_vc_cpuid(regs);
+
+	return handle_vc_cpuid(ghcb, ghcb_gpa, regs);
+}
-- 
2.25.1


  parent reply	other threads:[~2021-12-16 17:15 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-16 17:13 [PATCH v2 00/13] KVM: selftests: Add tests for SEV and SEV-ES guests Michael Roth
2021-12-16 17:13 ` [PATCH v2 01/13] KVM: selftests: move vm_phy_pages_alloc() earlier in file Michael Roth
2021-12-16 17:13 ` [PATCH v2 02/13] KVM: selftests: sparsebit: add const where appropriate Michael Roth
2021-12-16 17:13 ` [PATCH v2 03/13] KVM: selftests: add hooks for managing encrypted guest memory Michael Roth
2021-12-16 17:13 ` [PATCH v2 04/13] KVM: selftests: handle encryption bits in page tables Michael Roth
2021-12-16 17:13 ` [PATCH v2 05/13] KVM: selftests: add support for encrypted vm_vaddr_* allocations Michael Roth
2021-12-16 17:13 ` [PATCH v2 06/13] KVM: selftests: ensure ucall_shared_alloc() allocates shared memory Michael Roth
2021-12-16 17:13 ` [PATCH v2 07/13] KVM: selftests: add library for creating/interacting with SEV guests Michael Roth
2021-12-16 20:35   ` Peter Gonda
2021-12-17 16:17     ` Michael Roth
2021-12-22 14:52       ` Paolo Bonzini
2021-12-16 17:13 ` [PATCH v2 08/13] KVM: selftests: add SEV boot tests Michael Roth
2021-12-20  1:49   ` Mingwei Zhang
2021-12-21 15:40     ` Michael Roth
2021-12-21 17:26       ` Michael Roth
2021-12-22 14:55       ` Paolo Bonzini
2021-12-16 17:13 ` [PATCH v2 09/13] KVM: SVM: include CR3 in initial VMSA state for SEV-ES guests Michael Roth
2021-12-22 14:25   ` Paolo Bonzini
2021-12-16 17:13 ` [PATCH v2 10/13] KVM: selftests: account for error code in #VC exception frame Michael Roth
2021-12-16 17:13 ` [PATCH v2 11/13] KVM: selftests: add support for creating SEV-ES guests Michael Roth
2021-12-16 17:13 ` Michael Roth [this message]
2021-12-16 17:13 ` [PATCH v2 13/13] KVM: selftests: add SEV-ES boot tests Michael Roth
2021-12-22 14:56 ` [PATCH v2 00/13] KVM: selftests: Add tests for SEV and SEV-ES guests Paolo Bonzini
2022-01-04 23:41   ` Michael Roth

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211216171358.61140-13-michael.roth@amd.com \
    --to=michael.roth@amd.com \
    --cc=bp@alien8.de \
    --cc=brijesh.singh@amd.com \
    --cc=dwmw@amazon.co.uk \
    --cc=hpa@zytor.com \
    --cc=jmattson@google.com \
    --cc=joro@8bytes.org \
    --cc=krish.sadhukhan@oracle.com \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=marcorr@google.com \
    --cc=mingo@redhat.com \
    --cc=mizhang@google.com \
    --cc=natet@google.com \
    --cc=pgonda@google.com \
    --cc=ricarkol@google.com \
    --cc=seanjc@google.com \
    --cc=shuah@kernel.org \
    --cc=srutherford@google.com \
    --cc=tglx@linutronix.de \
    --cc=thomas.lendacky@amd.com \
    --cc=varad.gautam@suse.com \
    --cc=vkuznets@redhat.com \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).