// SPDX-License-Identifier: GPL-2.0-only /* * Kernel-based Virtual Machine driver for Linux * * AMD SVM-SEV support * * Copyright 2010 Red Hat, Inc. and/or its affiliates.
*/ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
/* Called with the sev_bitmap_lock held, or on shutdown */ staticint sev_flush_asids(unsignedint min_asid, unsignedint max_asid)
{ int ret, error = 0; unsignedint asid;
/* Check if there are any ASIDs to reclaim before performing a flush */
asid = find_next_bit(sev_reclaim_asid_bitmap, nr_asids, min_asid); if (asid > max_asid) return -EBUSY;
/* * DEACTIVATE will clear the WBINVD indicator causing DF_FLUSH to fail, * so it must be guarded.
*/
down_write(&sev_deactivate_lock);
/* SNP firmware requires use of WBINVD for ASID recycling. */
wbinvd_on_all_cpus();
if (sev_snp_enabled)
ret = sev_do_cmd(SEV_CMD_SNP_DF_FLUSH, NULL, &error); else
ret = sev_guest_df_flush(&error);
/* Must be called with the sev_bitmap_lock held */ staticbool __sev_recycle_asids(unsignedint min_asid, unsignedint max_asid)
{ if (sev_flush_asids(min_asid, max_asid)) returnfalse;
/* The flush process will flush all reclaimable SEV and SEV-ES ASIDs */
bitmap_xor(sev_asid_bitmap, sev_asid_bitmap, sev_reclaim_asid_bitmap,
nr_asids);
bitmap_zero(sev_reclaim_asid_bitmap, nr_asids);
staticint sev_asid_new(struct kvm_sev_info *sev)
{ /* * SEV-enabled guests must use asid from min_sev_asid to max_sev_asid. * SEV-ES-enabled guest can use from 1 to min_sev_asid - 1. * Note: min ASID can end up larger than the max if basic SEV support is * effectively disabled by disallowing use of ASIDs for SEV guests.
*/ unsignedint min_asid = sev->es_active ? 1 : min_sev_asid; unsignedint max_asid = sev->es_active ? min_sev_asid - 1 : max_sev_asid; unsignedint asid; bool retry = true; int ret;
if (min_asid > max_asid) return -ENOTTY;
WARN_ON(sev->misc_cg);
sev->misc_cg = get_current_misc_cg();
ret = sev_misc_cg_try_charge(sev); if (ret) {
put_misc_cg(sev->misc_cg);
sev->misc_cg = NULL; return ret;
}
/* * Transition a page to hypervisor-owned/shared state in the RMP table. This * should not fail under normal conditions, but leak the page should that * happen since it will no longer be usable by the host due to RMP protections.
*/ staticint kvm_rmp_make_shared(struct kvm *kvm, u64 pfn, enum pg_level level)
{ if (KVM_BUG_ON(rmp_make_shared(pfn, level), kvm)) {
snp_leak_pages(pfn, page_level_size(level) >> PAGE_SHIFT); return -EIO;
}
return 0;
}
/* * Certain page-states, such as Pre-Guest and Firmware pages (as documented * in Chapter 5 of the SEV-SNP Firmware ABI under "Page States") cannot be * directly transitioned back to normal/hypervisor-owned state via RMPUPDATE * unless they are reclaimed first. * * Until they are reclaimed and subsequently transitioned via RMPUPDATE, they * might not be usable by the host due to being set as immutable or still * being associated with a guest ASID. * * Bug the VM and leak the page if reclaim fails, or if the RMP entry can't be * converted back to shared, as the page is no longer usable due to RMP * protections, and it's infeasible for the guest to continue on.
*/ staticint snp_page_reclaim(struct kvm *kvm, u64 pfn)
{ struct sev_data_snp_page_reclaim data = {0}; int fw_err, rc;
/* Guard DEACTIVATE against WBINVD/DF_FLUSH used in ASID recycling */
down_read(&sev_deactivate_lock);
sev_guest_deactivate(&deactivate, NULL);
up_read(&sev_deactivate_lock);
sev_decommission(handle);
}
/* * This sets up bounce buffers/firmware pages to handle SNP Guest Request * messages (e.g. attestation requests). See "SNP Guest Request" in the GHCB * 2.0 specification for more details. * * Technically, when an SNP Guest Request is issued, the guest will provide its * own request/response pages, which could in theory be passed along directly * to firmware rather than using bounce pages. However, these pages would need * special care: * * - Both pages are from shared guest memory, so they need to be protected * from migration/etc. occurring while firmware reads/writes to them. At a * minimum, this requires elevating the ref counts and potentially needing * an explicit pinning of the memory. This places additional restrictions * on what type of memory backends userspace can use for shared guest * memory since there is some reliance on using refcounted pages. * * - The response page needs to be switched to Firmware-owned[1] state * before the firmware can write to it, which can lead to potential * host RMP #PFs if the guest is misbehaved and hands the host a * guest page that KVM might write to for other reasons (e.g. virtio * buffers/etc.). * * Both of these issues can be avoided completely by using separately-allocated * bounce pages for both the request/response pages and passing those to * firmware instead. So that's what is being set up here. * * Guest requests rely on message sequence numbers to ensure requests are * issued to firmware in the order the guest issues them, so concurrent guest * requests generally shouldn't happen. But a misbehaved guest could issue * concurrent guest requests in theory, so a mutex is used to serialize * access to the bounce buffers. * * [1] See the "Page States" section of the SEV-SNP Firmware ABI for more * details on Firmware-owned pages, along with "RMP and VMPL Access Checks" * in the APM for details on the related RMP restrictions.
*/ staticint snp_guest_req_init(struct kvm *kvm)
{ struct kvm_sev_info *sev = to_kvm_sev_info(kvm); struct page *req_page;
req_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); if (!req_page) return -ENOMEM;
/* * Currently KVM supports the full range of mandatory features defined * by version 2 of the GHCB protocol, so default to that for SEV-ES * guests created via KVM_SEV_INIT2.
*/ if (sev->es_active && !sev->ghcb_version)
sev->ghcb_version = GHCB_VERSION_DEFAULT;
if (vm_type == KVM_X86_SNP_VM)
sev->vmsa_features |= SVM_SEV_FEAT_SNP_ACTIVE;
ret = sev_asid_new(sev); if (ret) goto e_no_asid;
init_args.probe = false;
ret = sev_platform_init(&init_args); if (ret) goto e_free_asid;
if (!zalloc_cpumask_var(&sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
ret = -ENOMEM; goto e_free_asid;
}
/* This needs to happen after SEV/SNP firmware initialization. */ if (vm_type == KVM_X86_SNP_VM) {
ret = snp_guest_req_init(kvm); if (ret) goto e_free;
}
/* * KVM_SEV_ES_INIT has been deprecated by KVM_SEV_INIT2, so it will * continue to only ever support the minimal GHCB protocol version.
*/ if (vm_type == KVM_X86_SEV_ES_VM)
data.ghcb_version = GHCB_VERSION_MIN;
for (i = 0; i < npages; i++) {
page_virtual = kmap_local_page(pages[i]);
clflush_cache_range(page_virtual, PAGE_SIZE);
kunmap_local(page_virtual);
cond_resched();
}
}
staticvoid sev_writeback_caches(struct kvm *kvm)
{ /* * Ensure that all dirty guest tagged cache entries are written back * before releasing the pages back to the system for use. CLFLUSH will * not do this without SME_COHERENT, and flushing many cache lines * individually is slower than blasting WBINVD for large VMs, so issue * WBNOINVD (or WBINVD if the "no invalidate" variant is unsupported) * on CPUs that have done VMRUN, i.e. may have dirtied data using the * VM's ASID. * * For simplicity, never remove CPUs from the bitmap. Ideally, KVM * would clear the mask when flushing caches, but doing so requires * serializing multiple calls and having responding CPUs (to the IPI) * mark themselves as still running if they are running (or about to * run) a vCPU for the VM. * * Note, the caller is responsible for ensuring correctness if the mask * can be modified, e.g. if a CPU could be doing VMRUN.
*/
wbnoinvd_on_cpus_mask(to_kvm_sev_info(kvm)->have_run_cpus);
}
/* Lock the user memory. */
inpages = sev_pin_memory(kvm, vaddr, size, &npages, FOLL_WRITE); if (IS_ERR(inpages)) return PTR_ERR(inpages);
/* * Flush (on non-coherent CPUs) before LAUNCH_UPDATE encrypts pages in * place; the cache may contain the data that was written unencrypted.
*/
sev_clflush_pages(inpages, npages);
for (i = 0; vaddr < vaddr_end; vaddr = next_vaddr, i += pages) { int offset, len;
/* * If the user buffer is not page-aligned, calculate the offset * within the page.
*/
offset = vaddr & (PAGE_SIZE - 1);
/* Calculate the number of pages that can be encrypted in one go. */
pages = get_num_contig_pages(i, inpages, npages);
len = min_t(size_t, ((pages * PAGE_SIZE) - offset), size);
data.len = len;
data.address = __sme_page_pa(inpages[i]) + offset;
ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_DATA, &data, &argp->error); if (ret) goto e_unpin;
size -= len;
next_vaddr = vaddr + len;
}
e_unpin: /* content of memory is updated, mark pages dirty */ for (i = 0; i < npages; i++) {
set_page_dirty_lock(inpages[i]);
mark_page_accessed(inpages[i]);
} /* unlock the user pages */
sev_unpin_memory(kvm, inpages, npages); return ret;
}
/* Check some debug related fields before encrypting the VMSA */ if (svm->vcpu.guest_debug || (svm->vmcb->save.dr7 & ~DR7_FIXED_1)) return -EINVAL;
/* * SEV-ES will use a VMSA that is pointed to by the VMCB, not * the traditional VMSA that is part of the VMCB. Copy the * traditional VMSA as it has been built so far (in prep * for LAUNCH_UPDATE_VMSA) to be the initial SEV-ES state.
*/
memcpy(save, &svm->vmcb->save, sizeof(svm->vmcb->save));
for (i = 0; i < 8; i++) { /* * The format of the x87 save area is undocumented and * definitely not what you would expect. It consists of * an 8*8 bytes area with bytes 0-7, and an 8*2 bytes * area with bytes 8-9 of each register.
*/
d = save->fpreg_x87 + i * 8;
s = ((u8 *)xsave->i387.st_space) + i * 16;
memcpy(d, s, 8);
save->fpreg_x87[64 + i * 2] = s[8];
save->fpreg_x87[64 + i * 2 + 1] = s[9];
}
memcpy(save->fpreg_xmm, xsave->i387.xmm_space, 256);
s = get_xsave_addr(xsave, XFEATURE_YMM); if (s)
memcpy(save->fpreg_ymm, s, 256); else
memset(save->fpreg_ymm, 0, 256);
}
pr_debug("Virtual Machine Save Area (VMSA):\n");
print_hex_dump_debug("", DUMP_PREFIX_NONE, 16, 1, save, sizeof(*save), false);
return 0;
}
staticint __sev_launch_update_vmsa(struct kvm *kvm, struct kvm_vcpu *vcpu, int *error)
{ struct sev_data_launch_update_vmsa vmsa; struct vcpu_svm *svm = to_svm(vcpu); int ret;
if (vcpu->guest_debug) {
pr_warn_once("KVM_SET_GUEST_DEBUG for SEV-ES guest is not supported"); return -EINVAL;
}
/* Perform some pre-encryption checks against the VMSA */
ret = sev_es_sync_vmsa(svm); if (ret) return ret;
/* * The LAUNCH_UPDATE_VMSA command will perform in-place encryption of * the VMSA memory content (i.e it will write the same memory region * with the guest's key), so invalidate it first.
*/
clflush_cache_range(svm->sev_es.vmsa, PAGE_SIZE);
/* * SEV-ES guests maintain an encrypted version of their FPU * state which is restored and saved on VMRUN and VMEXIT. * Mark vcpu->arch.guest_fpu->fpstate as scratch so it won't * do xsave/xrstor on it.
*/
fpstate_set_confidential(&vcpu->arch.guest_fpu);
vcpu->arch.guest_state_protected = true;
/* * SEV-ES guest mandates LBR Virtualization to be _always_ ON. Enable it * only after setting guest_state_protected because KVM_SET_MSRS allows * dynamic toggling of LBRV (for performance reason) on write access to * MSR_IA32_DEBUGCTLMSR when guest_state_protected is not set.
*/
svm_enable_lbrv(vcpu); return 0;
}
staticint __sev_dbg_decrypt(struct kvm *kvm, unsignedlong src_paddr, unsignedlong dst_paddr, int sz, int *err)
{ int offset;
/* * Its safe to read more than we are asked, caller should ensure that * destination has enough space.
*/
offset = src_paddr & 15;
src_paddr = round_down(src_paddr, 16);
sz = round_up(sz + offset, 16);
staticint __sev_dbg_decrypt_user(struct kvm *kvm, unsignedlong paddr, void __user *dst_uaddr, unsignedlong dst_paddr, int size, int *err)
{ struct page *tpage = NULL; int ret, offset;
/* if inputs are not 16-byte then use intermediate buffer */ if (!IS_ALIGNED(dst_paddr, 16) ||
!IS_ALIGNED(paddr, 16) ||
!IS_ALIGNED(size, 16)) {
tpage = (void *)alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO); if (!tpage) return -ENOMEM;
dst_paddr = __sme_page_pa(tpage);
}
ret = __sev_dbg_decrypt(kvm, paddr, dst_paddr, size, err); if (ret) goto e_free;
if (tpage) {
offset = paddr & 15; if (copy_to_user(dst_uaddr, page_address(tpage) + offset, size))
ret = -EFAULT;
}
e_free: if (tpage)
__free_page(tpage);
return ret;
}
staticint __sev_dbg_encrypt_user(struct kvm *kvm, unsignedlong paddr, void __user *vaddr, unsignedlong dst_paddr, void __user *dst_vaddr, int size, int *error)
{ struct page *src_tpage = NULL; struct page *dst_tpage = NULL; int ret, len = size;
/* If source buffer is not aligned then use an intermediate buffer */ if (!IS_ALIGNED((unsignedlong)vaddr, 16)) {
src_tpage = alloc_page(GFP_KERNEL_ACCOUNT); if (!src_tpage) return -ENOMEM;
if (copy_from_user(page_address(src_tpage), vaddr, size)) {
__free_page(src_tpage); return -EFAULT;
}
paddr = __sme_page_pa(src_tpage);
}
/* * If destination buffer or length is not aligned then do read-modify-write: * - decrypt destination in an intermediate buffer * - copy the source buffer in an intermediate buffer * - use the intermediate buffer as source buffer
*/ if (!IS_ALIGNED((unsignedlong)dst_vaddr, 16) || !IS_ALIGNED(size, 16)) { int dst_offset;
dst_tpage = alloc_page(GFP_KERNEL_ACCOUNT); if (!dst_tpage) {
ret = -ENOMEM; goto e_free;
}
ret = __sev_dbg_decrypt(kvm, dst_paddr,
__sme_page_pa(dst_tpage), size, error); if (ret) goto e_free;
/* * If source is kernel buffer then use memcpy() otherwise * copy_from_user().
*/
dst_offset = dst_paddr & 15;
if (src_tpage)
memcpy(page_address(dst_tpage) + dst_offset,
page_address(src_tpage), size); else { if (copy_from_user(page_address(dst_tpage) + dst_offset,
vaddr, size)) {
ret = -EFAULT; goto e_free;
}
}
/* * Flush (on non-coherent CPUs) before DBG_{DE,EN}CRYPT read or modify * the pages; flush the destination too so that future accesses do not * see stale data.
*/
sev_clflush_pages(src_p, 1);
sev_clflush_pages(dst_p, 1);
/* * Since user buffer may not be page aligned, calculate the * offset within the page.
*/
s_off = vaddr & ~PAGE_MASK;
d_off = dst_vaddr & ~PAGE_MASK;
len = min_t(size_t, (PAGE_SIZE - s_off), size);
/* * Flush (on non-coherent CPUs) before LAUNCH_SECRET encrypts pages in * place; the cache may contain the data that was written unencrypted.
*/
sev_clflush_pages(pages, n);
/* * The secret must be copied into contiguous memory region, lets verify * that userspace memory pages are contiguous before we issue command.
*/ if (get_num_contig_pages(0, pages, n) != n) {
ret = -EINVAL; goto e_unpin_memory;
}
if (copy_from_user(¶ms, u64_to_user_ptr(argp->data), sizeof(struct kvm_sev_send_start))) return -EFAULT;
/* if session_len is zero, userspace wants to query the session length */ if (!params.session_len) return __sev_send_start_query_session_length(kvm, argp,
¶ms);
/* some sanity checks */ if (!params.pdh_cert_uaddr || !params.pdh_cert_len ||
!params.session_uaddr || params.session_len > SEV_FW_BLOB_MAX_SIZE) return -EINVAL;
/* allocate the memory to hold the session data blob */
session_data = kzalloc(params.session_len, GFP_KERNEL_ACCOUNT); if (!session_data) return -ENOMEM;
/* copy the certificate blobs from userspace */
pdh_cert = psp_copy_user_blob(params.pdh_cert_uaddr,
params.pdh_cert_len); if (IS_ERR(pdh_cert)) {
ret = PTR_ERR(pdh_cert); goto e_free_session;
}
plat_certs = psp_copy_user_blob(params.plat_certs_uaddr,
params.plat_certs_len); if (IS_ERR(plat_certs)) {
ret = PTR_ERR(plat_certs); goto e_free_pdh;
}
amd_certs = psp_copy_user_blob(params.amd_certs_uaddr,
params.amd_certs_len); if (IS_ERR(amd_certs)) {
ret = PTR_ERR(amd_certs); goto e_free_plat_cert;
}
if (copy_from_user(¶ms, u64_to_user_ptr(argp->data), sizeof(struct kvm_sev_send_update_data))) return -EFAULT;
/* userspace wants to query either header or trans length */ if (!params.trans_len || !params.hdr_len) return __sev_send_update_data_query_lengths(kvm, argp, ¶ms);
if (!params.trans_uaddr || !params.guest_uaddr ||
!params.guest_len || !params.hdr_uaddr) return -EINVAL;
/* Check if we are crossing the page boundary */
offset = params.guest_uaddr & (PAGE_SIZE - 1); if (params.guest_len > PAGE_SIZE || (params.guest_len + offset) > PAGE_SIZE) return -EINVAL;
/* The SEND_UPDATE_DATA command requires C-bit to be always set. */
data.guest_address = (page_to_pfn(guest_page[0]) << PAGE_SHIFT) + offset;
data.guest_address |= sev_me_mask;
data.guest_len = params.guest_len;
data.handle = to_kvm_sev_info(kvm)->handle;
ret = sev_issue_cmd(kvm, SEV_CMD_SEND_UPDATE_DATA, &data, &argp->error);
if (ret) goto e_free_trans_data;
/* copy transport buffer to user space */ if (copy_to_user(u64_to_user_ptr(params.trans_uaddr),
trans_data, params.trans_len)) {
ret = -EFAULT; goto e_free_trans_data;
}
/* Copy packet header to userspace. */ if (copy_to_user(u64_to_user_ptr(params.hdr_uaddr), hdr,
params.hdr_len))
ret = -EFAULT;
/* * Flush (on non-coherent CPUs) before RECEIVE_UPDATE_DATA, the PSP * encrypts the written data with the guest's key, and the cache may * contain dirty, unencrypted data.
*/
sev_clflush_pages(guest_page, n);
/* The RECEIVE_UPDATE_DATA command requires C-bit to be always set. */
data.guest_address = (page_to_pfn(guest_page[0]) << PAGE_SHIFT) + offset;
data.guest_address |= sev_me_mask;
data.guest_len = params.guest_len;
data.handle = to_kvm_sev_info(kvm)->handle;
ret = sev_issue_cmd(kvm, SEV_CMD_RECEIVE_UPDATE_DATA, &data,
&argp->error);
staticbool is_cmd_allowed_from_mirror(u32 cmd_id)
{ /* * Allow mirrors VM to call KVM_SEV_LAUNCH_UPDATE_VMSA to enable SEV-ES * active mirror VMs. Also allow the debugging and status commands.
*/ if (cmd_id == KVM_SEV_LAUNCH_UPDATE_VMSA ||
cmd_id == KVM_SEV_GUEST_STATUS || cmd_id == KVM_SEV_DBG_DECRYPT ||
cmd_id == KVM_SEV_DBG_ENCRYPT) returntrue;
/* * Bail if these VMs are already involved in a migration to avoid * deadlock between two VMs trying to migrate to/from each other.
*/ if (atomic_cmpxchg_acquire(&dst_sev->migration_in_progress, 0, 1)) return -EBUSY;
if (atomic_cmpxchg_acquire(&src_sev->migration_in_progress, 0, 1)) goto release_dst;
r = -EINTR; if (mutex_lock_killable(&dst_kvm->lock)) goto release_src; if (mutex_lock_killable_nested(&src_kvm->lock, SINGLE_DEPTH_NESTING)) goto unlock_dst; return 0;
/* * If this VM has mirrors, "transfer" each mirror's refcount of the * source to the destination (this KVM). The caller holds a reference * to the source, so there's no danger of use-after-free.
*/
list_cut_before(&dst->mirror_vms, &src->mirror_vms, &src->mirror_vms);
list_for_each_entry(mirror, &dst->mirror_vms, mirror_entry) {
kvm_get_kvm(dst_kvm);
kvm_put_kvm(src_kvm);
mirror->enc_context_owner = dst_kvm;
}
/* * If this VM is a mirror, remove the old mirror from the owners list * and add the new mirror to the list.
*/ if (is_mirroring_enc_context(dst_kvm)) { struct kvm_sev_info *owner_sev_info = to_kvm_sev_info(dst->enc_context_owner);
/* * Note, the source is not required to have the same number of * vCPUs as the destination when migrating a vanilla SEV VM.
*/
src_vcpu = kvm_get_vcpu(src_kvm, i);
src_svm = to_svm(src_vcpu);
/* * Transfer VMSA and GHCB state to the destination. Nullify and * clear source fields as appropriate, the state now belongs to * the destination.
*/
memcpy(&dst_svm->sev_es, &src_svm->sev_es, sizeof(src_svm->sev_es));
dst_svm->vmcb->control.ghcb_gpa = src_svm->vmcb->control.ghcb_gpa;
dst_svm->vmcb->control.vmsa_pa = src_svm->vmcb->control.vmsa_pa;
dst_vcpu->arch.guest_state_protected = true;
source_kvm = fd_file(f)->private_data;
ret = sev_lock_two_vms(kvm, source_kvm); if (ret) return ret;
if (kvm->arch.vm_type != source_kvm->arch.vm_type ||
sev_guest(kvm) || !sev_guest(source_kvm)) {
ret = -EINVAL; goto out_unlock;
}
src_sev = to_kvm_sev_info(source_kvm);
dst_sev->misc_cg = get_current_misc_cg();
cg_cleanup_sev = dst_sev; if (dst_sev->misc_cg != src_sev->misc_cg) {
ret = sev_misc_cg_try_charge(dst_sev); if (ret) goto out_dst_cgroup;
charged = true;
}
ret = kvm_lock_all_vcpus(kvm); if (ret) goto out_dst_cgroup;
ret = kvm_lock_all_vcpus(source_kvm); if (ret) goto out_dst_vcpu;
ret = sev_check_source_vcpus(kvm, source_kvm); if (ret) goto out_source_vcpu;
/* * Allocate a new have_run_cpus for the destination, i.e. don't copy * the set of CPUs from the source. If a CPU was used to run a vCPU in * the source VM but is never used for the destination VM, then the CPU * can only have cached memory that was accessible to the source VM.
*/ if (!zalloc_cpumask_var(&dst_sev->have_run_cpus, GFP_KERNEL_ACCOUNT)) {
ret = -ENOMEM; goto out_source_vcpu;
}
sev_migrate_from(kvm, source_kvm);
kvm_vm_dead(source_kvm);
cg_cleanup_sev = src_sev;
ret = 0;
out_source_vcpu:
kvm_unlock_all_vcpus(source_kvm);
out_dst_vcpu:
kvm_unlock_all_vcpus(kvm);
out_dst_cgroup: /* Operates on the source on success, on the destination on failure. */ if (charged)
sev_misc_cg_uncharge(cg_cleanup_sev);
put_misc_cg(cg_cleanup_sev->misc_cg);
cg_cleanup_sev->misc_cg = NULL;
out_unlock:
sev_unlock_two_vms(kvm, source_kvm); return ret;
}
int sev_dev_get_attr(u32 group, u64 attr, u64 *val)
{ if (group != KVM_X86_GRP_SEV) return -ENXIO;
switch (attr) { case KVM_X86_SEV_VMSA_FEATURES:
*val = sev_supported_vmsa_features; return 0;
default: return -ENXIO;
}
}
/* * The guest context contains all the information, keys and metadata * associated with the guest that the firmware tracks to implement SEV * and SNP features. The firmware stores the guest context in hypervisor * provide page via the SNP_GCTX_CREATE command.
*/ staticvoid *snp_context_create(struct kvm *kvm, struct kvm_sev_cmd *argp)
{ struct sev_data_snp_addr data = {}; void *context; int rc;
/* Allocate memory for context page */
context = snp_alloc_firmware_page(GFP_KERNEL_ACCOUNT); if (!context) return NULL;
ret = __sev_issue_cmd(sev_populate_args->sev_fd, SEV_CMD_SNP_LAUNCH_UPDATE,
&fw_args, &sev_populate_args->fw_error); if (ret) goto fw_err;
}
return 0;
fw_err: /* * If the firmware command failed handle the reclaim and cleanup of that * PFN specially vs. prior pages which can be cleaned up below without * needing to reclaim in advance. * * Additionally, when invalid CPUID function entries are detected, * firmware writes the expected values into the page and leaves it * unencrypted so it can be used for debugging and error-reporting. * * Copy this page back into the source buffer so userspace can use this * information to provide information on which CPUID leaves/fields * failed CPUID validation.
*/ if (!snp_page_reclaim(kvm, pfn + i) &&
sev_populate_args->type == KVM_SEV_SNP_PAGE_TYPE_CPUID &&
sev_populate_args->fw_error == SEV_RET_INVALID_PARAM) { void *vaddr = kmap_local_pfn(pfn + i);
if (copy_to_user(src + i * PAGE_SIZE, vaddr, PAGE_SIZE))
pr_debug("Failed to write CPUID page back to userspace\n");
kunmap_local(vaddr);
}
/* pfn + i is hypervisor-owned now, so skip below cleanup for it. */
n_private--;
err:
pr_debug("%s: exiting with error ret %d (fw_error %d), restoring %d gmem PFNs to shared.\n",
__func__, ret, sev_populate_args->fw_error, n_private); for (i = 0; i < n_private; i++)
kvm_rmp_make_shared(kvm, pfn + i, PG_LEVEL_4K);
/* * For each GFN that's being prepared as part of the initial guest * state, the following pre-conditions are verified: * * 1) The backing memslot is a valid private memslot. * 2) The GFN has been set to private via KVM_SET_MEMORY_ATTRIBUTES * beforehand. * 3) The PFN of the guest_memfd has not already been set to private * in the RMP table. * * The KVM MMU relies on kvm->mmu_invalidate_seq to retry nested page * faults if there's a race between a fault and an attribute update via * KVM_SET_MEMORY_ATTRIBUTES, and a similar approach could be utilized * here. However, kvm->slots_lock guards against both this as well as * concurrent memslot updates occurring while these checks are being * performed, so use that here to make it easier to reason about the * initial expected state and better guard against unexpected * situations.
*/
mutex_lock(&kvm->slots_lock);
memslot = gfn_to_memslot(kvm, params.gfn_start); if (!kvm_slot_can_be_private(memslot)) {
ret = -EINVAL; goto out;
}
/* Transition the VMSA page to a firmware state. */
ret = rmp_make_private(pfn, INITIAL_VMSA_GPA, PG_LEVEL_4K, sev->asid, true); if (ret) return ret;
/* Issue the SNP command to encrypt the VMSA */
data.address = __sme_pa(svm->sev_es.vmsa);
ret = __sev_issue_cmd(argp->sev_fd, SEV_CMD_SNP_LAUNCH_UPDATE,
&data, &argp->error); if (ret) {
snp_page_reclaim(kvm, pfn);
return ret;
}
svm->vcpu.arch.guest_state_protected = true; /* * SEV-ES (and thus SNP) guest mandates LBR Virtualization to * be _always_ ON. Enable it only after setting * guest_state_protected because KVM_SET_MSRS allows dynamic * toggling of LBRV (for performance reason) on write access to * MSR_IA32_DEBUGCTLMSR when guest_state_protected is not set.
*/
svm_enable_lbrv(vcpu);
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.