lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250228093024.114983-20-Neeraj.Upadhyay@amd.com>
Date: Fri, 28 Feb 2025 15:00:12 +0530
From: Neeraj Upadhyay <Neeraj.Upadhyay@....com>
To: <kvm@...r.kernel.org>, <seanjc@...gle.com>, <pbonzini@...hat.com>
CC: <linux-kernel@...r.kernel.org>, <Thomas.Lendacky@....com>,
	<nikunj@....com>, <Santosh.Shukla@....com>, <Vasant.Hegde@....com>,
	<Suravee.Suthikulpanit@....com>, <bp@...en8.de>, <David.Kaplan@....com>,
	<huibo.wang@....com>, <naveen.rao@....com>, <pgonda@...gle.com>,
	<linux-kselftest@...r.kernel.org>, <shuah@...nel.org>
Subject: [RFC PATCH 19/31] KVM: selftests: Add  Secure AVIC lib

Add Secure AVIC lib which provides apis to enable
Secure AVIC for a vCPU. In addition, add guest APIC
backing page initialization support and helper
functions to access APIC regs from guest APIC backing
page and from hv.

Signed-off-by: Neeraj Upadhyay <Neeraj.Upadhyay@....com>
---
 tools/testing/selftests/kvm/Makefile.kvm      |   1 +
 .../testing/selftests/kvm/include/x86/apic.h  |   6 +
 .../testing/selftests/kvm/include/x86/savic.h |  19 ++
 tools/testing/selftests/kvm/include/x86/svm.h |   3 +
 .../testing/selftests/kvm/lib/x86/processor.c |   7 +-
 tools/testing/selftests/kvm/lib/x86/savic.c   | 206 ++++++++++++++++++
 tools/testing/selftests/kvm/lib/x86/sev.c     |  15 ++
 7 files changed, 255 insertions(+), 2 deletions(-)
 create mode 100644 tools/testing/selftests/kvm/include/x86/savic.h
 create mode 100644 tools/testing/selftests/kvm/lib/x86/savic.c

diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 5a67e79ae848..50bd78e03d9f 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -25,6 +25,7 @@ LIBKVM_x86 += lib/x86/insn-eval.c
 LIBKVM_x86 += lib/x86/memstress.c
 LIBKVM_x86 += lib/x86/pmu.c
 LIBKVM_x86 += lib/x86/processor.c
+LIBKVM_x86 += lib/x86/savic.c
 LIBKVM_x86 += lib/x86/sev.c
 LIBKVM_x86 += lib/x86/svm.c
 LIBKVM_x86 += lib/x86/ucall.c
diff --git a/tools/testing/selftests/kvm/include/x86/apic.h b/tools/testing/selftests/kvm/include/x86/apic.h
index 80fe9f69b38d..6ba5d0545bf8 100644
--- a/tools/testing/selftests/kvm/include/x86/apic.h
+++ b/tools/testing/selftests/kvm/include/x86/apic.h
@@ -29,6 +29,7 @@
 #define	APIC_TASKPRI	0x80
 #define	APIC_PROCPRI	0xA0
 #define	APIC_EOI	0xB0
+#define APIC_LDR	0xD0
 #define	APIC_SPIV	0xF0
 #define		APIC_SPIV_FOCUS_DISABLED	(1 << 9)
 #define		APIC_SPIV_APIC_ENABLED		(1 << 8)
@@ -60,10 +61,15 @@
 #define	APIC_ICR2	0x310
 #define		SET_APIC_DEST_FIELD(x)	((x) << 24)
 #define APIC_LVTT	0x320
+#define APIC_LVTTHMR    0x330
+#define APIC_LVTPC      0x340
+#define APIC_LVT0       0x350
 #define		APIC_LVT_TIMER_ONESHOT		(0 << 17)
 #define		APIC_LVT_TIMER_PERIODIC		(1 << 17)
 #define		APIC_LVT_TIMER_TSCDEADLINE	(2 << 17)
 #define		APIC_LVT_MASKED			(1 << 16)
+#define APIC_LVT1       0x360
+#define APIC_LVTERR     0x370
 #define	APIC_TMICT	0x380
 #define	APIC_TMCCT	0x390
 #define	APIC_TDCR	0x3E0
diff --git a/tools/testing/selftests/kvm/include/x86/savic.h b/tools/testing/selftests/kvm/include/x86/savic.h
new file mode 100644
index 000000000000..1ab92dad00c1
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/x86/savic.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Helpers used for Secure AVIC guests
+ *
+ */
+#ifndef SELFTEST_KVM_SAVIC_H
+#define SELFTEST_KVM_SAVIC_H
+
+struct guest_apic_page;
+
+void guest_apic_pages_init(struct kvm_vm *vm);
+void set_savic_control_msr(struct guest_apic_page *apic_page, bool enable, bool enable_nmi);
+void savic_write_reg(struct guest_apic_page *apic_page, uint32_t reg, uint64_t val);
+uint64_t savic_read_reg(struct guest_apic_page *apic_page, uint32_t reg);
+void savic_hv_write_reg(uint32_t reg, uint64_t val);
+uint64_t savic_hv_read_reg(uint32_t reg);
+void savic_enable(void);
+int savic_nr_pages_required(uint64_t page_size);
+#endif
diff --git a/tools/testing/selftests/kvm/include/x86/svm.h b/tools/testing/selftests/kvm/include/x86/svm.h
index 66dd4eaf23b9..689fefb72d06 100644
--- a/tools/testing/selftests/kvm/include/x86/svm.h
+++ b/tools/testing/selftests/kvm/include/x86/svm.h
@@ -179,6 +179,9 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
 #define SVM_NESTED_CTL_NP_ENABLE	BIT(0)
 #define SVM_NESTED_CTL_SEV_ENABLE	BIT(1)
 
+#define SVM_FEAT_SECURE_AVIC		16
+#define SVM_FEAT_ALLOWED_SEV_FEATURES_VALID	63
+
 struct __attribute__ ((__packed__)) vmcb_seg {
 	u16 selector;
 	u16 attrib;
diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testing/selftests/kvm/lib/x86/processor.c
index 197110ff1380..2d6105b1f610 100644
--- a/tools/testing/selftests/kvm/lib/x86/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86/processor.c
@@ -659,10 +659,13 @@ void kvm_arch_vm_post_create(struct kvm_vm *vm)
 
 int kvm_arch_vm_additional_pages_required(struct vm_shape shape, uint64_t page_size)
 {
-	if (shape.type == KVM_X86_SEV_ES_VM ||
-	    shape.type == KVM_X86_SNP_VM)
+	if (shape.type == KVM_X86_SEV_ES_VM)
 		return  ghcb_nr_pages_required(page_size);
 
+	if (shape.type == KVM_X86_SNP_VM)
+		return ghcb_nr_pages_required(page_size) +
+			savic_nr_pages_required(page_size);
+
 	return 0;
 }
 
diff --git a/tools/testing/selftests/kvm/lib/x86/savic.c b/tools/testing/selftests/kvm/lib/x86/savic.c
new file mode 100644
index 000000000000..f4a765b6040a
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86/savic.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ *  Copyright (C) 2024 Advanced Micro Devices, Inc.
+ *
+ */
+
+#include "apic.h"
+#include "kvm_util.h"
+#include "sev.h"
+#include "ex_regs.h"
+
+struct apic_page {
+	u8 apic_regs[PAGE_SIZE];
+} __packed;
+
+struct guest_apic_page {
+	struct apic_page apic_page;
+	uint64_t gpa;
+	uint64_t hva;
+} __attribute__((__aligned__(PAGE_SIZE)));
+
+struct guest_apic_pages {
+	struct guest_apic_page guest_apic_page[KVM_MAX_VCPUS];
+};
+
+static struct guest_apic_pages *apic_page_pool;
+
+enum lapic_lvt_entry {
+	LVT_TIMER,
+	LVT_THERMAL_MONITOR,
+	LVT_PERFORMANCE_COUNTER,
+	LVT_LINT0,
+	LVT_LINT1,
+	LVT_ERROR,
+	APIC_MAX_NR_LVT_ENTRIES,
+};
+
+#define MSR_AMD64_SECURE_AVIC_CONTROL      0xc0010138
+
+#define APIC_LVTx(x) (APIC_LVTT + 0x10 * (x))
+#define MSR_AMD64_SECURE_AVIC_EN_BIT       0
+#define MSR_AMD64_SECURE_AVIC_ALLOWED_NMI_BIT       1
+
+/*
+ * Initial pool of guest apic backing page.
+ */
+void guest_apic_pages_init(struct kvm_vm *vm)
+{
+	struct guest_apic_pages *g_pages;
+	struct guest_apic_page *entry;
+	vm_vaddr_t vaddr;
+	int i;
+	size_t sz = align_up(sizeof(struct guest_apic_pages),
+			     vm_guest_mode_params[vm->mode].page_size);
+
+	vaddr = vm_vaddr_alloc(vm, sz, KVM_UTIL_MIN_VADDR);
+
+	g_pages = (struct guest_apic_pages *)addr_gva2hva(vm, vaddr);
+	memset(g_pages, 0, sz);
+
+	for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+		entry = &g_pages->guest_apic_page[i];
+		entry->hva = (uint64_t)entry;
+		entry->gpa = (uint64_t)addr_hva2gpa(vm, &entry->apic_page);
+	}
+
+	apic_page_pool = (struct guest_apic_pages *)vaddr;
+	sync_global_to_guest(vm, apic_page_pool);
+}
+
+int savic_nr_pages_required(uint64_t page_size)
+{
+	return align_up(sizeof(struct guest_apic_pages), page_size) / page_size;
+}
+
+/*
+ * Enable/disable Secure AVIC in control msr.
+ *
+ * @apic_page  : Guest APIC backing page for the CPU on which
+ *	       this function is called.
+ * @enable     : Enable/Disable Secure AVIC.
+ * @enable_nmi : Allow host to send NMI to the guest.
+ */
+void set_savic_control_msr(struct guest_apic_page *apic_page, bool enable, bool enable_nmi)
+{
+	uint64_t val = apic_page->gpa | BIT_ULL(MSR_AMD64_SECURE_AVIC_EN_BIT);
+
+	if (!enable) {
+		wrmsr(MSR_AMD64_SECURE_AVIC_CONTROL, 0);
+		return;
+	}
+
+	if (enable_nmi)
+		val |= BIT_ULL(MSR_AMD64_SECURE_AVIC_ALLOWED_NMI_BIT);
+
+	wrmsr(MSR_AMD64_SECURE_AVIC_CONTROL, val);
+}
+
+/*
+ * Write APIC reg offset in the guest APIC backing page.
+ *
+ * @apage : Backing page address.
+ * @reg   : APIC reg offset corresponding to the xapic MMIO
+ *	  offset.
+ * @val   : New value to be set for the APIC reg.
+ */
+void savic_write_reg(struct guest_apic_page *apic_page, uint32_t reg, uint64_t val)
+{
+	*(volatile uint64_t *)((uint64_t)apic_page + reg) = val;
+}
+
+/*
+ * Read APIC reg offset from the guest APIC backing page.
+ *
+ * @apage : Backing page address.
+ * @reg   : APIC reg offset corresponding to the xapic MMIO
+ *	  offset.
+ *
+ * @ret   : APIC register value in the guest APIC backing page.
+ */
+uint64_t savic_read_reg(struct guest_apic_page *apic_page, uint32_t reg)
+{
+	return *(volatile uint64_t *)((uint64_t)apic_page + reg);
+}
+
+/*
+ * Write APIC reg value to hypervisor.
+ *
+ * @reg   : APIC reg offset corresponding to the xapic MMIO
+ *	  offset.
+ * @val   : Value to be set for the APIC reg.
+ */
+void savic_hv_write_reg(uint32_t reg, uint64_t val)
+{
+	sev_es_pv_msr_rw(APIC_BASE_MSR + (reg >> 4), &val, true);
+}
+
+/*
+ * Read APIC reg offset from hypervisor.
+ *
+ * @reg   : APIC reg offset corresponding to the xapic MMIO
+ *	  offset.
+ *
+ * @ret   : APIC register value in the hypervisor's APIC state.
+ */
+uint64_t savic_hv_read_reg(uint32_t reg)
+{
+	uint64_t val;
+
+	sev_es_pv_msr_rw(APIC_BASE_MSR + (reg >> 4), &val, false);
+
+	return val;
+}
+
+static void savic_init_backing_page(struct guest_apic_page *apic_page, uint32_t apic_id)
+{
+	uint64_t regval;
+	enum lapic_lvt_entry i;
+
+	/* Update APIC ID in the backing page */
+	savic_write_reg(apic_page, APIC_ID, apic_id);
+
+	/* Set LVR, LDR, LVT* in backing page from host values */
+	regval = savic_hv_read_reg(APIC_LVR);
+	savic_write_reg(apic_page, APIC_LVR, regval);
+
+	regval = savic_hv_read_reg(APIC_LDR);
+	savic_write_reg(apic_page, APIC_LDR, regval);
+
+	for (i = LVT_THERMAL_MONITOR; i < APIC_MAX_NR_LVT_ENTRIES; i++) {
+		regval = savic_hv_read_reg(APIC_LVTx(i));
+		savic_write_reg(apic_page, APIC_LVTx(i), regval);
+	}
+
+	regval = savic_hv_read_reg(APIC_LVT0);
+	savic_write_reg(apic_page, APIC_LVT0, regval);
+
+	regval = savic_hv_read_reg(APIC_LVT1);
+	savic_write_reg(apic_page, APIC_LVT1, regval);
+}
+
+/*
+ * Initialize and enable Secure AVIC on a CPU.
+ *
+ * @context: Called from x2apic enabled context and Secure AVIC disabled.
+ */
+void savic_enable(void)
+{
+	uint64_t savic_ctrl_msr_val, exp_msr_val;
+	struct guest_apic_page *apic_page;
+	uint32_t apic_id;
+
+	__GUEST_ASSERT(apic_page_pool, "Guest APIC pages pool is not initialized");
+	apic_id = x2apic_read_reg(APIC_ID);
+	apic_page = &apic_page_pool->guest_apic_page[apic_id];
+
+	savic_init_backing_page(apic_page, apic_id);
+	set_savic_control_msr(apic_page, true, true);
+	savic_ctrl_msr_val = rdmsr(MSR_AMD64_SECURE_AVIC_CONTROL);
+	exp_msr_val = apic_page->gpa | BIT_ULL(MSR_AMD64_SECURE_AVIC_EN_BIT) |
+			BIT_ULL(MSR_AMD64_SECURE_AVIC_ALLOWED_NMI_BIT);
+	__GUEST_ASSERT(savic_ctrl_msr_val == exp_msr_val,
+			"SAVIC Control msr unexpected val : 0x%lx, expected : 0x%lx",
+			savic_ctrl_msr_val, exp_msr_val);
+}
diff --git a/tools/testing/selftests/kvm/lib/x86/sev.c b/tools/testing/selftests/kvm/lib/x86/sev.c
index 24aaa75ec450..518e30275960 100644
--- a/tools/testing/selftests/kvm/lib/x86/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86/sev.c
@@ -307,12 +307,27 @@ struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, void *guest_code,
 	return vm;
 }
 
+static bool is_savic_enabled(void)
+{
+	u64 supported_vmsa_features;
+	int kvm_fd = open_kvm_dev_path_or_exit();
+
+	kvm_device_attr_get(kvm_fd, KVM_X86_GRP_SEV,
+			    KVM_X86_SEV_VMSA_FEATURES,
+			    &supported_vmsa_features);
+
+	return supported_vmsa_features & BIT_ULL(SVM_FEAT_SECURE_AVIC);
+}
+
 void vm_sev_launch(struct kvm_vm *vm, uint64_t policy, uint8_t *measurement)
 {
 	if (is_sev_es_vm(vm))
 		ghcb_init(vm);
 
 	if (is_sev_snp_vm(vm)) {
+		if (is_savic_enabled())
+			guest_apic_pages_init(vm);
+
 		vm_enable_cap(vm, KVM_CAP_EXIT_HYPERCALL, (1 << KVM_HC_MAP_GPA_RANGE));
 
 		snp_vm_launch_start(vm, policy);
-- 
2.34.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ