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>
---
v4..v5
* encrypt_region() cleanup of code flow
* minor changes to comments
---
---
 tools/testing/selftests/kvm/include/x86/sev.h | 49 ++++++++++-
 tools/testing/selftests/kvm/lib/x86/sev.c     | 82 +++++++++++++++++--
 2 files changed, 125 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/x86/sev.h 
b/tools/testing/selftests/kvm/include/x86/sev.h
index faed91435963..fd5d5261e10e 100644
--- a/tools/testing/selftests/kvm/include/x86/sev.h
+++ b/tools/testing/selftests/kvm/include/x86/sev.h
@@ -22,9 +22,20 @@ enum sev_guest_state {
        SEV_GUEST_STATE_RUNNING,
 };
 
+/* Minimum firmware version required for the SEV-SNP support */
+#define SNP_MIN_API_MAJOR      1
+#define SNP_MIN_API_MINOR      51
+
 #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 SNP_FW_VER_MINOR(min)  ((uint8_t)(min) << 0)
+#define SNP_FW_VER_MAJOR(maj)  ((uint8_t)(maj) << 8)
+
 #define GHCB_MSR_TERM_REQ      0x100
 
 #define VMGEXIT()              { __asm__ __volatile__("rep; vmmcall"); }
@@ -36,13 +47,35 @@ 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)
+{
+       bool smt_active = false;
+       FILE *f;
+
+       f = fopen("/sys/devices/system/cpu/smt/active", "r");
+       if (f) {
+               smt_active = fgetc(f) - '0';
+               fclose(f);
+       }
+
+       return SNP_POLICY_RSVD_MBO | (smt_active ? 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
@@ -76,6 +109,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 sev_register_encrypted_memory(struct kvm_vm *vm,
                                                 struct userspace_mem_region 
*region)
@@ -99,4 +133,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 280ec42e281b..17d493e9907a 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)
 {
        const struct sparsebit *protected_phy_pages = 
region->protected_phy_pages;
        const vm_paddr_t gpa_base = region->region.guest_phys_addr;
@@ -41,13 +42,35 @@ 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 (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);
+               }
+       }
+}
+
+static void privatize_region(struct kvm_vm *vm, struct userspace_mem_region 
*region)
+{
+       const struct sparsebit *protected_phy_pages = 
region->protected_phy_pages;
+       const vm_paddr_t gpa_base = region->region.guest_phys_addr;
+       const sparsebit_idx_t lowest_page_in_region = gpa_base >> 
vm->page_shift;
+       sparsebit_idx_t i, j;
+
+       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;
+
+               vm_mem_set_private(vm, gpa_base + offset, size);
        }
 }
 
@@ -77,6 +100,14 @@ void sev_es_vm_init(struct kvm_vm *vm)
        }
 }
 
+void snp_vm_init(struct kvm_vm *vm)
+{
+       struct kvm_sev_init init = { 0 };
+
+       assert(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 +124,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, 0);
 
        if (policy & SEV_POLICY_ES)
                vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL);
@@ -129,6 +160,35 @@ 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) {
+               privatize_region(vm, region);
+               encrypt_region(vm, region, KVM_SEV_SNP_PAGE_TYPE_NORMAL);
+       }
+
+       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 +205,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