Path: blob/master/tools/testing/selftests/kvm/x86/sev_smoke_test.c
38237 views
// SPDX-License-Identifier: GPL-2.0-only1#include <fcntl.h>2#include <stdio.h>3#include <stdlib.h>4#include <string.h>5#include <sys/ioctl.h>6#include <math.h>78#include "test_util.h"9#include "kvm_util.h"10#include "processor.h"11#include "svm_util.h"12#include "linux/psp-sev.h"13#include "sev.h"141516#define XFEATURE_MASK_X87_AVX (XFEATURE_MASK_FP | XFEATURE_MASK_SSE | XFEATURE_MASK_YMM)1718static void guest_snp_code(void)19{20uint64_t sev_msr = rdmsr(MSR_AMD64_SEV);2122GUEST_ASSERT(sev_msr & MSR_AMD64_SEV_ENABLED);23GUEST_ASSERT(sev_msr & MSR_AMD64_SEV_ES_ENABLED);24GUEST_ASSERT(sev_msr & MSR_AMD64_SEV_SNP_ENABLED);2526wrmsr(MSR_AMD64_SEV_ES_GHCB, GHCB_MSR_TERM_REQ);27vmgexit();28}2930static void guest_sev_es_code(void)31{32/* TODO: Check CPUID after GHCB-based hypercall support is added. */33GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SEV_ENABLED);34GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SEV_ES_ENABLED);3536/*37* TODO: Add GHCB and ucall support for SEV-ES guests. For now, simply38* force "termination" to signal "done" via the GHCB MSR protocol.39*/40wrmsr(MSR_AMD64_SEV_ES_GHCB, GHCB_MSR_TERM_REQ);41vmgexit();42}4344static void guest_sev_code(void)45{46GUEST_ASSERT(this_cpu_has(X86_FEATURE_SEV));47GUEST_ASSERT(rdmsr(MSR_AMD64_SEV) & MSR_AMD64_SEV_ENABLED);4849GUEST_DONE();50}5152/* Stash state passed via VMSA before any compiled code runs. */53extern void guest_code_xsave(void);54asm("guest_code_xsave:\n"55"mov $" __stringify(XFEATURE_MASK_X87_AVX) ", %eax\n"56"xor %edx, %edx\n"57"xsave (%rdi)\n"58"jmp guest_sev_es_code");5960static void compare_xsave(u8 *from_host, u8 *from_guest)61{62int i;63bool bad = false;64for (i = 0; i < 4095; i++) {65if (from_host[i] != from_guest[i]) {66printf("mismatch at %u | %02hhx %02hhx\n",67i, from_host[i], from_guest[i]);68bad = true;69}70}7172if (bad)73abort();74}7576static void test_sync_vmsa(uint32_t type, uint64_t policy)77{78struct kvm_vcpu *vcpu;79struct kvm_vm *vm;80vm_vaddr_t gva;81void *hva;8283double x87val = M_PI;84struct kvm_xsave __attribute__((aligned(64))) xsave = { 0 };8586vm = vm_sev_create_with_one_vcpu(type, guest_code_xsave, &vcpu);87gva = vm_vaddr_alloc_shared(vm, PAGE_SIZE, KVM_UTIL_MIN_VADDR,88MEM_REGION_TEST_DATA);89hva = addr_gva2hva(vm, gva);9091vcpu_args_set(vcpu, 1, gva);9293asm("fninit\n"94"vpcmpeqb %%ymm4, %%ymm4, %%ymm4\n"95"fldl %3\n"96"xsave (%2)\n"97"fstp %%st\n"98: "=m"(xsave)99: "A"(XFEATURE_MASK_X87_AVX), "r"(&xsave), "m" (x87val)100: "ymm4", "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)");101vcpu_xsave_set(vcpu, &xsave);102103vm_sev_launch(vm, policy, NULL);104105/* This page is shared, so make it decrypted. */106memset(hva, 0, PAGE_SIZE);107108vcpu_run(vcpu);109110TEST_ASSERT(vcpu->run->exit_reason == KVM_EXIT_SYSTEM_EVENT,111"Wanted SYSTEM_EVENT, got %s",112exit_reason_str(vcpu->run->exit_reason));113TEST_ASSERT_EQ(vcpu->run->system_event.type, KVM_SYSTEM_EVENT_SEV_TERM);114TEST_ASSERT_EQ(vcpu->run->system_event.ndata, 1);115TEST_ASSERT_EQ(vcpu->run->system_event.data[0], GHCB_MSR_TERM_REQ);116117compare_xsave((u8 *)&xsave, (u8 *)hva);118119kvm_vm_free(vm);120}121122static void test_sev(void *guest_code, uint32_t type, uint64_t policy)123{124struct kvm_vcpu *vcpu;125struct kvm_vm *vm;126struct ucall uc;127128vm = vm_sev_create_with_one_vcpu(type, guest_code, &vcpu);129130/* TODO: Validate the measurement is as expected. */131vm_sev_launch(vm, policy, NULL);132133for (;;) {134vcpu_run(vcpu);135136if (is_sev_es_vm(vm)) {137TEST_ASSERT(vcpu->run->exit_reason == KVM_EXIT_SYSTEM_EVENT,138"Wanted SYSTEM_EVENT, got %s",139exit_reason_str(vcpu->run->exit_reason));140TEST_ASSERT_EQ(vcpu->run->system_event.type, KVM_SYSTEM_EVENT_SEV_TERM);141TEST_ASSERT_EQ(vcpu->run->system_event.ndata, 1);142TEST_ASSERT_EQ(vcpu->run->system_event.data[0], GHCB_MSR_TERM_REQ);143break;144}145146switch (get_ucall(vcpu, &uc)) {147case UCALL_SYNC:148continue;149case UCALL_DONE:150return;151case UCALL_ABORT:152REPORT_GUEST_ASSERT(uc);153default:154TEST_FAIL("Unexpected exit: %s",155exit_reason_str(vcpu->run->exit_reason));156}157}158159kvm_vm_free(vm);160}161162static void guest_shutdown_code(void)163{164struct desc_ptr idt;165166/* Clobber the IDT so that #UD is guaranteed to trigger SHUTDOWN. */167memset(&idt, 0, sizeof(idt));168set_idt(&idt);169170__asm__ __volatile__("ud2");171}172173static void test_sev_shutdown(uint32_t type, uint64_t policy)174{175struct kvm_vcpu *vcpu;176struct kvm_vm *vm;177178vm = vm_sev_create_with_one_vcpu(type, guest_shutdown_code, &vcpu);179180vm_sev_launch(vm, policy, NULL);181182vcpu_run(vcpu);183TEST_ASSERT(vcpu->run->exit_reason == KVM_EXIT_SHUTDOWN,184"Wanted SHUTDOWN, got %s",185exit_reason_str(vcpu->run->exit_reason));186187kvm_vm_free(vm);188}189190static void test_sev_smoke(void *guest, uint32_t type, uint64_t policy)191{192const u64 xf_mask = XFEATURE_MASK_X87_AVX;193194if (type == KVM_X86_SNP_VM)195test_sev(guest, type, policy | SNP_POLICY_DBG);196else197test_sev(guest, type, policy | SEV_POLICY_NO_DBG);198test_sev(guest, type, policy);199200if (type == KVM_X86_SEV_VM)201return;202203test_sev_shutdown(type, policy);204205if (kvm_has_cap(KVM_CAP_XCRS) &&206(xgetbv(0) & kvm_cpu_supported_xcr0() & xf_mask) == xf_mask) {207test_sync_vmsa(type, policy);208if (type == KVM_X86_SNP_VM)209test_sync_vmsa(type, policy | SNP_POLICY_DBG);210else211test_sync_vmsa(type, policy | SEV_POLICY_NO_DBG);212}213}214215int main(int argc, char *argv[])216{217TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV));218219test_sev_smoke(guest_sev_code, KVM_X86_SEV_VM, 0);220221if (kvm_cpu_has(X86_FEATURE_SEV_ES))222test_sev_smoke(guest_sev_es_code, KVM_X86_SEV_ES_VM, SEV_POLICY_ES);223224if (kvm_cpu_has(X86_FEATURE_SEV_SNP))225test_sev_smoke(guest_snp_code, KVM_X86_SNP_VM, snp_default_policy());226227return 0;228}229230231