Extend the SEV library to include support for SNP ioctl() wrappers,
which aid in launching and interacting with a SEV-SNP guest.

Signed-off-by: Pratik R. Sampat <prsam...@amd.com>
---
 arch/x86/include/uapi/asm/kvm.h               |  1 +
 tools/arch/x86/include/uapi/asm/kvm.h         |  1 +
 tools/testing/selftests/kvm/include/x86/sev.h | 33 ++++++++-
 tools/testing/selftests/kvm/lib/x86/sev.c     | 68 +++++++++++++++++--
 4 files changed, 97 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h
index 9e75da97bce0..565e4d054627 100644
--- a/arch/x86/include/uapi/asm/kvm.h
+++ b/arch/x86/include/uapi/asm/kvm.h
@@ -841,6 +841,7 @@ struct kvm_sev_snp_launch_start {
 };
 
 /* Kept in sync with firmware values for simplicity. */
+#define KVM_SEV_PAGE_TYPE_INVALID              0x0
 #define KVM_SEV_SNP_PAGE_TYPE_NORMAL           0x1
 #define KVM_SEV_SNP_PAGE_TYPE_ZERO             0x3
 #define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED       0x4
diff --git a/tools/arch/x86/include/uapi/asm/kvm.h 
b/tools/arch/x86/include/uapi/asm/kvm.h
index 88585c1de416..17e44fbdc2a7 100644
--- a/tools/arch/x86/include/uapi/asm/kvm.h
+++ b/tools/arch/x86/include/uapi/asm/kvm.h
@@ -841,6 +841,7 @@ struct kvm_sev_snp_launch_start {
 };
 
 /* Kept in sync with firmware values for simplicity. */
+#define KVM_SEV_PAGE_TYPE_INVALID              0x0
 #define KVM_SEV_SNP_PAGE_TYPE_NORMAL           0x1
 #define KVM_SEV_SNP_PAGE_TYPE_ZERO             0x3
 #define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED       0x4
diff --git a/tools/testing/selftests/kvm/include/x86/sev.h 
b/tools/testing/selftests/kvm/include/x86/sev.h
index b112f7664534..c696d10f9332 100644
--- a/tools/testing/selftests/kvm/include/x86/sev.h
+++ b/tools/testing/selftests/kvm/include/x86/sev.h
@@ -25,6 +25,10 @@ enum sev_guest_state {
 #define SEV_POLICY_NO_DBG      (1UL << 0)
 #define SEV_POLICY_ES          (1UL << 2)
 
+#define SNP_POLICY_SMT         (1ULL << 16)
+#define SNP_POLICY_RSVD_MBO    (1ULL << 17)
+#define SNP_POLICY_DBG         (1ULL << 19)
+
 #define GHCB_MSR_TERM_REQ      0x100
 
 bool is_sev_vm(struct kvm_vm *vm);
@@ -34,13 +38,26 @@ bool is_sev_snp_vm(struct kvm_vm *vm);
 void sev_vm_launch(struct kvm_vm *vm, uint32_t policy);
 void sev_vm_launch_measure(struct kvm_vm *vm, uint8_t *measurement);
 void sev_vm_launch_finish(struct kvm_vm *vm);
+void snp_vm_launch_start(struct kvm_vm *vm, uint64_t policy);
+void snp_vm_launch_update(struct kvm_vm *vm);
+void snp_vm_launch_finish(struct kvm_vm *vm);
 
 struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, void *guest_code,
                                           struct kvm_vcpu **cpu);
-void vm_sev_launch(struct kvm_vm *vm, uint32_t policy, uint8_t *measurement);
+void vm_sev_launch(struct kvm_vm *vm, uint64_t policy, uint8_t *measurement);
 
 kvm_static_assert(SEV_RET_SUCCESS == 0);
 
+/*
+ * A SEV-SNP VM requires the policy reserved bit to always be set.
+ * The SMT policy bit is also required to be set based on SMT being
+ * available and active on the system.
+ */
+static inline u64 snp_default_policy(void)
+{
+       return SNP_POLICY_RSVD_MBO | (smt_on() ? SNP_POLICY_SMT : 0);
+}
+
 /*
  * The KVM_MEMORY_ENCRYPT_OP uAPI is utter garbage and takes an "unsigned long"
  * instead of a proper struct.  The size of the parameter is embedded in the
@@ -74,6 +91,7 @@ kvm_static_assert(SEV_RET_SUCCESS == 0);
 
 void sev_vm_init(struct kvm_vm *vm);
 void sev_es_vm_init(struct kvm_vm *vm);
+void snp_vm_init(struct kvm_vm *vm);
 
 static inline void vmgexit(void)
 {
@@ -102,4 +120,17 @@ static inline void sev_launch_update_data(struct kvm_vm 
*vm, vm_paddr_t gpa,
        vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_DATA, &update_data);
 }
 
+static inline void snp_launch_update_data(struct kvm_vm *vm, vm_paddr_t gpa,
+                                         uint64_t hva, uint64_t size, uint8_t 
type)
+{
+       struct kvm_sev_snp_launch_update update_data = {
+               .uaddr = hva,
+               .gfn_start = gpa >> PAGE_SHIFT,
+               .len = size,
+               .type = type,
+       };
+
+       vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_UPDATE, &update_data);
+}
+
 #endif /* SELFTEST_KVM_SEV_H */
diff --git a/tools/testing/selftests/kvm/lib/x86/sev.c 
b/tools/testing/selftests/kvm/lib/x86/sev.c
index 4587f2b6bc39..a56f5164b0a6 100644
--- a/tools/testing/selftests/kvm/lib/x86/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86/sev.c
@@ -31,7 +31,8 @@ bool is_sev_vm(struct kvm_vm *vm)
  * and find the first range, but that's correct because the condition
  * expression would cause us to quit the loop.
  */
-static void encrypt_region(struct kvm_vm *vm, struct userspace_mem_region 
*region)
+static void encrypt_region(struct kvm_vm *vm, struct userspace_mem_region 
*region,
+                          uint8_t page_type, bool private)
 {
        const struct sparsebit *protected_phy_pages = 
region->protected_phy_pages;
        const vm_paddr_t gpa_base = region->region.guest_phys_addr;
@@ -41,13 +42,23 @@ static void encrypt_region(struct kvm_vm *vm, struct 
userspace_mem_region *regio
        if (!sparsebit_any_set(protected_phy_pages))
                return;
 
-       sev_register_encrypted_memory(vm, region);
+       if (!is_sev_snp_vm(vm))
+               sev_register_encrypted_memory(vm, region);
 
        sparsebit_for_each_set_range(protected_phy_pages, i, j) {
                const uint64_t size = (j - i + 1) * vm->page_size;
                const uint64_t offset = (i - lowest_page_in_region) * 
vm->page_size;
 
-               sev_launch_update_data(vm, gpa_base + offset, size);
+               if (private)
+                       vm_mem_set_private(vm, gpa_base + offset, size);
+
+               if (is_sev_snp_vm(vm))
+                       snp_launch_update_data(vm, gpa_base + offset,
+                                              (uint64_t)addr_gpa2hva(vm, 
gpa_base + offset),
+                                              size, page_type);
+               else
+                       sev_launch_update_data(vm, gpa_base + offset, size);
+
        }
 }
 
@@ -77,6 +88,14 @@ void sev_es_vm_init(struct kvm_vm *vm)
        }
 }
 
+void snp_vm_init(struct kvm_vm *vm)
+{
+       struct kvm_sev_init init = { 0 };
+
+       TEST_ASSERT_EQ(vm->type, KVM_X86_SNP_VM);
+       vm_sev_ioctl(vm, KVM_SEV_INIT2, &init);
+}
+
 void sev_vm_launch(struct kvm_vm *vm, uint32_t policy)
 {
        struct kvm_sev_launch_start launch_start = {
@@ -93,7 +112,7 @@ void sev_vm_launch(struct kvm_vm *vm, uint32_t policy)
        TEST_ASSERT_EQ(status.state, SEV_GUEST_STATE_LAUNCH_UPDATE);
 
        hash_for_each(vm->regions.slot_hash, ctr, region, slot_node)
-               encrypt_region(vm, region);
+               encrypt_region(vm, region, KVM_SEV_PAGE_TYPE_INVALID, false);
 
        if (policy & SEV_POLICY_ES)
                vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL);
@@ -129,6 +148,33 @@ void sev_vm_launch_finish(struct kvm_vm *vm)
        TEST_ASSERT_EQ(status.state, SEV_GUEST_STATE_RUNNING);
 }
 
+void snp_vm_launch_start(struct kvm_vm *vm, uint64_t policy)
+{
+       struct kvm_sev_snp_launch_start launch_start = {
+               .policy = policy,
+       };
+
+       vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_START, &launch_start);
+}
+
+void snp_vm_launch_update(struct kvm_vm *vm)
+{
+       struct userspace_mem_region *region;
+       int ctr;
+
+       hash_for_each(vm->regions.slot_hash, ctr, region, slot_node)
+               encrypt_region(vm, region, KVM_SEV_SNP_PAGE_TYPE_NORMAL, true);
+
+       vm->arch.is_pt_protected = true;
+}
+
+void snp_vm_launch_finish(struct kvm_vm *vm)
+{
+       struct kvm_sev_snp_launch_finish launch_finish = { 0 };
+
+       vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_FINISH, &launch_finish);
+}
+
 struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, void *guest_code,
                                           struct kvm_vcpu **cpu)
 {
@@ -145,8 +191,20 @@ struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, 
void *guest_code,
        return vm;
 }
 
-void vm_sev_launch(struct kvm_vm *vm, uint32_t policy, uint8_t *measurement)
+void vm_sev_launch(struct kvm_vm *vm, uint64_t policy, uint8_t *measurement)
 {
+       if (is_sev_snp_vm(vm)) {
+               vm_enable_cap(vm, KVM_CAP_EXIT_HYPERCALL, (1 << 
KVM_HC_MAP_GPA_RANGE));
+
+               snp_vm_launch_start(vm, policy);
+
+               snp_vm_launch_update(vm);
+
+               snp_vm_launch_finish(vm);
+
+               return;
+       }
+
        sev_vm_launch(vm, policy);
 
        if (!measurement)
-- 
2.43.0


Reply via email to