[<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