]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: selftests: Add library support for interacting with SNP
authorPratik R. Sampat <prsampat@amd.com>
Wed, 5 Mar 2025 22:59:57 +0000 (16:59 -0600)
committerSean Christopherson <seanjc@google.com>
Fri, 2 May 2025 19:32:33 +0000 (12:32 -0700)
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 <prsampat@amd.com>
Link: https://lore.kernel.org/r/20250305230000.231025-8-prsampat@amd.com
[sean: use BIT()]
Signed-off-by: Sean Christopherson <seanjc@google.com>
arch/x86/include/uapi/asm/kvm.h
tools/arch/x86/include/uapi/asm/kvm.h
tools/testing/selftests/kvm/include/x86/sev.h
tools/testing/selftests/kvm/lib/x86/sev.c

index 225a12e0d5d647d0a36b7e8d8bc0f2c92a81a699..6f3499507c5efb560c94a58d053144f04671140b 100644 (file)
@@ -845,6 +845,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
index 88585c1de416fa6f81bf0d1e12dff3fec89f0ecc..17e44fbdc2a7946d55ce267d6b6aceb5a5dd323b 100644 (file)
@@ -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
index 4a4e763f1b7b161935ed0c6e6f821594400879e5..008b4169f5e234a401c4f0bf7b35046a41bcc399 100644 (file)
@@ -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
 
 static inline bool is_sev_snp_vm(struct kvm_vm *vm)
@@ -45,13 +49,26 @@ static inline bool is_sev_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 | (is_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
@@ -85,6 +102,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)
 {
@@ -113,4 +131,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 */
index 60d7a03dc1c250a14295fd6514987330fe2de88c..c3a9838f4806a5d5730bbbfc5b6b10439cf4aef1 100644 (file)
@@ -14,7 +14,8 @@
  * 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;
@@ -24,13 +25,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);
+
        }
 }
 
@@ -60,6 +71,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 = {
@@ -76,7 +95,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);
@@ -112,6 +131,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)
 {
@@ -128,8 +174,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, BIT(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)