Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: selftests: Add library support for interacting with SNP

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>

authored by

Pratik R. Sampat and committed by
Sean Christopherson
3bf3e0a5 4a4e1e8e

+97 -6
+1
arch/x86/include/uapi/asm/kvm.h
··· 845 845 }; 846 846 847 847 /* Kept in sync with firmware values for simplicity. */ 848 + #define KVM_SEV_PAGE_TYPE_INVALID 0x0 848 849 #define KVM_SEV_SNP_PAGE_TYPE_NORMAL 0x1 849 850 #define KVM_SEV_SNP_PAGE_TYPE_ZERO 0x3 850 851 #define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED 0x4
+1
tools/arch/x86/include/uapi/asm/kvm.h
··· 841 841 }; 842 842 843 843 /* Kept in sync with firmware values for simplicity. */ 844 + #define KVM_SEV_PAGE_TYPE_INVALID 0x0 844 845 #define KVM_SEV_SNP_PAGE_TYPE_NORMAL 0x1 845 846 #define KVM_SEV_SNP_PAGE_TYPE_ZERO 0x3 846 847 #define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED 0x4
+32 -1
tools/testing/selftests/kvm/include/x86/sev.h
··· 25 25 #define SEV_POLICY_NO_DBG (1UL << 0) 26 26 #define SEV_POLICY_ES (1UL << 2) 27 27 28 + #define SNP_POLICY_SMT (1ULL << 16) 29 + #define SNP_POLICY_RSVD_MBO (1ULL << 17) 30 + #define SNP_POLICY_DBG (1ULL << 19) 31 + 28 32 #define GHCB_MSR_TERM_REQ 0x100 29 33 30 34 static inline bool is_sev_snp_vm(struct kvm_vm *vm) ··· 49 45 void sev_vm_launch(struct kvm_vm *vm, uint32_t policy); 50 46 void sev_vm_launch_measure(struct kvm_vm *vm, uint8_t *measurement); 51 47 void sev_vm_launch_finish(struct kvm_vm *vm); 48 + void snp_vm_launch_start(struct kvm_vm *vm, uint64_t policy); 49 + void snp_vm_launch_update(struct kvm_vm *vm); 50 + void snp_vm_launch_finish(struct kvm_vm *vm); 52 51 53 52 struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, void *guest_code, 54 53 struct kvm_vcpu **cpu); 55 - void vm_sev_launch(struct kvm_vm *vm, uint32_t policy, uint8_t *measurement); 54 + void vm_sev_launch(struct kvm_vm *vm, uint64_t policy, uint8_t *measurement); 56 55 57 56 kvm_static_assert(SEV_RET_SUCCESS == 0); 57 + 58 + /* 59 + * A SEV-SNP VM requires the policy reserved bit to always be set. 60 + * The SMT policy bit is also required to be set based on SMT being 61 + * available and active on the system. 62 + */ 63 + static inline u64 snp_default_policy(void) 64 + { 65 + return SNP_POLICY_RSVD_MBO | (is_smt_on() ? SNP_POLICY_SMT : 0); 66 + } 58 67 59 68 /* 60 69 * The KVM_MEMORY_ENCRYPT_OP uAPI is utter garbage and takes an "unsigned long" ··· 102 85 103 86 void sev_vm_init(struct kvm_vm *vm); 104 87 void sev_es_vm_init(struct kvm_vm *vm); 88 + void snp_vm_init(struct kvm_vm *vm); 105 89 106 90 static inline void vmgexit(void) 107 91 { ··· 129 111 }; 130 112 131 113 vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_DATA, &update_data); 114 + } 115 + 116 + static inline void snp_launch_update_data(struct kvm_vm *vm, vm_paddr_t gpa, 117 + uint64_t hva, uint64_t size, uint8_t type) 118 + { 119 + struct kvm_sev_snp_launch_update update_data = { 120 + .uaddr = hva, 121 + .gfn_start = gpa >> PAGE_SHIFT, 122 + .len = size, 123 + .type = type, 124 + }; 125 + 126 + vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_UPDATE, &update_data); 132 127 } 133 128 134 129 #endif /* SELFTEST_KVM_SEV_H */
+63 -5
tools/testing/selftests/kvm/lib/x86/sev.c
··· 14 14 * and find the first range, but that's correct because the condition 15 15 * expression would cause us to quit the loop. 16 16 */ 17 - static void encrypt_region(struct kvm_vm *vm, struct userspace_mem_region *region) 17 + static void encrypt_region(struct kvm_vm *vm, struct userspace_mem_region *region, 18 + uint8_t page_type, bool private) 18 19 { 19 20 const struct sparsebit *protected_phy_pages = region->protected_phy_pages; 20 21 const vm_paddr_t gpa_base = region->region.guest_phys_addr; ··· 25 24 if (!sparsebit_any_set(protected_phy_pages)) 26 25 return; 27 26 28 - sev_register_encrypted_memory(vm, region); 27 + if (!is_sev_snp_vm(vm)) 28 + sev_register_encrypted_memory(vm, region); 29 29 30 30 sparsebit_for_each_set_range(protected_phy_pages, i, j) { 31 31 const uint64_t size = (j - i + 1) * vm->page_size; 32 32 const uint64_t offset = (i - lowest_page_in_region) * vm->page_size; 33 33 34 - sev_launch_update_data(vm, gpa_base + offset, size); 34 + if (private) 35 + vm_mem_set_private(vm, gpa_base + offset, size); 36 + 37 + if (is_sev_snp_vm(vm)) 38 + snp_launch_update_data(vm, gpa_base + offset, 39 + (uint64_t)addr_gpa2hva(vm, gpa_base + offset), 40 + size, page_type); 41 + else 42 + sev_launch_update_data(vm, gpa_base + offset, size); 43 + 35 44 } 36 45 } 37 46 ··· 71 60 } 72 61 } 73 62 63 + void snp_vm_init(struct kvm_vm *vm) 64 + { 65 + struct kvm_sev_init init = { 0 }; 66 + 67 + TEST_ASSERT_EQ(vm->type, KVM_X86_SNP_VM); 68 + vm_sev_ioctl(vm, KVM_SEV_INIT2, &init); 69 + } 70 + 74 71 void sev_vm_launch(struct kvm_vm *vm, uint32_t policy) 75 72 { 76 73 struct kvm_sev_launch_start launch_start = { ··· 95 76 TEST_ASSERT_EQ(status.state, SEV_GUEST_STATE_LAUNCH_UPDATE); 96 77 97 78 hash_for_each(vm->regions.slot_hash, ctr, region, slot_node) 98 - encrypt_region(vm, region); 79 + encrypt_region(vm, region, KVM_SEV_PAGE_TYPE_INVALID, false); 99 80 100 81 if (policy & SEV_POLICY_ES) 101 82 vm_sev_ioctl(vm, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL); ··· 131 112 TEST_ASSERT_EQ(status.state, SEV_GUEST_STATE_RUNNING); 132 113 } 133 114 115 + void snp_vm_launch_start(struct kvm_vm *vm, uint64_t policy) 116 + { 117 + struct kvm_sev_snp_launch_start launch_start = { 118 + .policy = policy, 119 + }; 120 + 121 + vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_START, &launch_start); 122 + } 123 + 124 + void snp_vm_launch_update(struct kvm_vm *vm) 125 + { 126 + struct userspace_mem_region *region; 127 + int ctr; 128 + 129 + hash_for_each(vm->regions.slot_hash, ctr, region, slot_node) 130 + encrypt_region(vm, region, KVM_SEV_SNP_PAGE_TYPE_NORMAL, true); 131 + 132 + vm->arch.is_pt_protected = true; 133 + } 134 + 135 + void snp_vm_launch_finish(struct kvm_vm *vm) 136 + { 137 + struct kvm_sev_snp_launch_finish launch_finish = { 0 }; 138 + 139 + vm_sev_ioctl(vm, KVM_SEV_SNP_LAUNCH_FINISH, &launch_finish); 140 + } 141 + 134 142 struct kvm_vm *vm_sev_create_with_one_vcpu(uint32_t type, void *guest_code, 135 143 struct kvm_vcpu **cpu) 136 144 { ··· 174 128 return vm; 175 129 } 176 130 177 - void vm_sev_launch(struct kvm_vm *vm, uint32_t policy, uint8_t *measurement) 131 + void vm_sev_launch(struct kvm_vm *vm, uint64_t policy, uint8_t *measurement) 178 132 { 133 + if (is_sev_snp_vm(vm)) { 134 + vm_enable_cap(vm, KVM_CAP_EXIT_HYPERCALL, BIT(KVM_HC_MAP_GPA_RANGE)); 135 + 136 + snp_vm_launch_start(vm, policy); 137 + 138 + snp_vm_launch_update(vm); 139 + 140 + snp_vm_launch_finish(vm); 141 + 142 + return; 143 + } 144 + 179 145 sev_vm_launch(vm, policy); 180 146 181 147 if (!measurement)