Merge branch 'kvm-guest-sev-migration' into kvm-master
authorPaolo Bonzini <pbonzini@redhat.com>
Thu, 11 Nov 2021 12:40:26 +0000 (07:40 -0500)
committerPaolo Bonzini <pbonzini@redhat.com>
Thu, 11 Nov 2021 12:40:26 +0000 (07:40 -0500)
Add guest api and guest kernel support for SEV live migration.

Introduces a new hypercall to notify the host of changes to the page
encryption status.  If the page is encrypted then it must be migrated
through the SEV firmware or a helper VM sharing the key.  If page is
not encrypted then it can be migrated normally by userspace.  This new
hypercall is invoked using paravirt_ops.

Conflicts: sev_active() replaced by cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT).

1  2 
arch/x86/include/asm/mem_encrypt.h
arch/x86/include/asm/paravirt.h
arch/x86/include/asm/paravirt_types.h
arch/x86/kernel/kvm.c
arch/x86/kernel/paravirt.c
arch/x86/mm/mem_encrypt.c
arch/x86/mm/pat/set_memory.c

Simple merge
Simple merge
Simple merge
index 8863d1941f1bedff318aa234561701d3ef5c1031,3910419fae6cbcaed87e6fd2444693c51757f9a2..41e2965f64991e128bada0a4ba22a9ed75cde2ed
@@@ -27,7 -27,7 +27,8 @@@
  #include <linux/nmi.h>
  #include <linux/swait.h>
  #include <linux/syscore_ops.h>
 +#include <linux/cc_platform.h>
+ #include <linux/efi.h>
  #include <asm/timer.h>
  #include <asm/cpu.h>
  #include <asm/traps.h>
@@@ -548,6 -551,55 +552,55 @@@ static void kvm_send_ipi_mask_allbutsel
        __send_ipi_mask(local_mask, vector);
  }
  
 -      if (!sev_active() ||
+ static int __init setup_efi_kvm_sev_migration(void)
+ {
+       efi_char16_t efi_sev_live_migration_enabled[] = L"SevLiveMigrationEnabled";
+       efi_guid_t efi_variable_guid = AMD_SEV_MEM_ENCRYPT_GUID;
+       efi_status_t status;
+       unsigned long size;
+       bool enabled;
++      if (!cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT) ||
+           !kvm_para_has_feature(KVM_FEATURE_MIGRATION_CONTROL))
+               return 0;
+       if (!efi_enabled(EFI_BOOT))
+               return 0;
+       if (!efi_enabled(EFI_RUNTIME_SERVICES)) {
+               pr_info("%s : EFI runtime services are not enabled\n", __func__);
+               return 0;
+       }
+       size = sizeof(enabled);
+       /* Get variable contents into buffer */
+       status = efi.get_variable(efi_sev_live_migration_enabled,
+                                 &efi_variable_guid, NULL, &size, &enabled);
+       if (status == EFI_NOT_FOUND) {
+               pr_info("%s : EFI live migration variable not found\n", __func__);
+               return 0;
+       }
+       if (status != EFI_SUCCESS) {
+               pr_info("%s : EFI variable retrieval failed\n", __func__);
+               return 0;
+       }
+       if (enabled == 0) {
+               pr_info("%s: live migration disabled in EFI\n", __func__);
+               return 0;
+       }
+       pr_info("%s : live migration enabled in EFI\n", __func__);
+       wrmsrl(MSR_KVM_MIGRATION_CONTROL, KVM_MIGRATION_READY);
+       return 1;
+ }
+ late_initcall(setup_efi_kvm_sev_migration);
  /*
   * Set the IPI entry points
   */
@@@ -806,8 -858,62 +859,62 @@@ static bool __init kvm_msi_ext_dest_id(
        return kvm_para_has_feature(KVM_FEATURE_MSI_EXT_DEST_ID);
  }
  
+ static void kvm_sev_hc_page_enc_status(unsigned long pfn, int npages, bool enc)
+ {
+       kvm_sev_hypercall3(KVM_HC_MAP_GPA_RANGE, pfn << PAGE_SHIFT, npages,
+                          KVM_MAP_GPA_RANGE_ENC_STAT(enc) | KVM_MAP_GPA_RANGE_PAGE_SZ_4K);
+ }
  static void __init kvm_init_platform(void)
  {
 -      if (sev_active() &&
++      if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT) &&
+           kvm_para_has_feature(KVM_FEATURE_MIGRATION_CONTROL)) {
+               unsigned long nr_pages;
+               int i;
+               pv_ops.mmu.notify_page_enc_status_changed =
+                       kvm_sev_hc_page_enc_status;
+               /*
+                * Reset the host's shared pages list related to kernel
+                * specific page encryption status settings before we load a
+                * new kernel by kexec. Reset the page encryption status
+                * during early boot intead of just before kexec to avoid SMP
+                * races during kvm_pv_guest_cpu_reboot().
+                * NOTE: We cannot reset the complete shared pages list
+                * here as we need to retain the UEFI/OVMF firmware
+                * specific settings.
+                */
+               for (i = 0; i < e820_table->nr_entries; i++) {
+                       struct e820_entry *entry = &e820_table->entries[i];
+                       if (entry->type != E820_TYPE_RAM)
+                               continue;
+                       nr_pages = DIV_ROUND_UP(entry->size, PAGE_SIZE);
+                       kvm_sev_hypercall3(KVM_HC_MAP_GPA_RANGE, entry->addr,
+                                      nr_pages,
+                                      KVM_MAP_GPA_RANGE_ENCRYPTED | KVM_MAP_GPA_RANGE_PAGE_SZ_4K);
+               }
+               /*
+                * Ensure that _bss_decrypted section is marked as decrypted in the
+                * shared pages list.
+                */
+               nr_pages = DIV_ROUND_UP(__end_bss_decrypted - __start_bss_decrypted,
+                                       PAGE_SIZE);
+               early_set_mem_enc_dec_hypercall((unsigned long)__start_bss_decrypted,
+                                               nr_pages, 0);
+               /*
+                * If not booted using EFI, enable Live migration support.
+                */
+               if (!efi_enabled(EFI_BOOT))
+                       wrmsrl(MSR_KVM_MIGRATION_CONTROL,
+                              KVM_MIGRATION_READY);
+       }
        kvmclock_init();
        x86_platform.apic_post_init = kvm_apic_init;
  }
index 7157c2df3bc2aba06d3a6e11210f678bd9138861,1cc20ac9a54ffe8b350873f5cfb13636388ec667..7f7636aac62091bdeaa38a0b0fa3ce2f0f7d43f5
@@@ -337,10 -296,11 +337,11 @@@ struct paravirt_patch_template pv_ops 
                        (void (*)(struct mmu_gather *, void *))tlb_remove_page,
  
        .mmu.exit_mmap          = paravirt_nop,
+       .mmu.notify_page_enc_status_changed     = paravirt_nop,
  
  #ifdef CONFIG_PARAVIRT_XXL
 -      .mmu.read_cr2           = __PV_IS_CALLEE_SAVE(native_read_cr2),
 -      .mmu.write_cr2          = native_write_cr2,
 +      .mmu.read_cr2           = __PV_IS_CALLEE_SAVE(pv_native_read_cr2),
 +      .mmu.write_cr2          = pv_native_write_cr2,
        .mmu.read_cr3           = __native_read_cr3,
        .mmu.write_cr3          = native_write_cr3,
  
index 23d54b810f0854a95546565c4a865beb58f01593,2673a89d17d9c52e6e929f56dfafad6e4121de01..35487305d8afec3bda45c6757abd312e59dfba36
@@@ -361,6 -409,40 +410,11 @@@ int __init early_set_memory_encrypted(u
        return early_set_memory_enc_dec(vaddr, size, true);
  }
  
 -/*
 - * SME and SEV are very similar but they are not the same, so there are
 - * times that the kernel will need to distinguish between SME and SEV. The
 - * sme_active() and sev_active() functions are used for this.  When a
 - * distinction isn't needed, the mem_encrypt_active() function can be used.
 - *
 - * The trampoline code is a good example for this requirement.  Before
 - * paging is activated, SME will access all memory as decrypted, but SEV
 - * will access all memory as encrypted.  So, when APs are being brought
 - * up under SME the trampoline area cannot be encrypted, whereas under SEV
 - * the trampoline area must be encrypted.
 - */
 -bool sev_active(void)
 -{
 -      return sev_status & MSR_AMD64_SEV_ENABLED;
 -}
 -
 -bool sme_active(void)
 -{
 -      return sme_me_mask && !sev_active();
 -}
 -EXPORT_SYMBOL_GPL(sev_active);
 -
 -/* Needs to be called from non-instrumentable code */
 -bool noinstr sev_es_active(void)
 -{
 -      return sev_status & MSR_AMD64_SEV_ES_ENABLED;
 -}
 -
+ void __init early_set_mem_enc_dec_hypercall(unsigned long vaddr, int npages, bool enc)
+ {
+       notify_range_enc_status_changed(vaddr, npages, enc);
+ }
  /* Override for DMA direct allocation check - ARCH_HAS_FORCE_DMA_UNENCRYPTED */
  bool force_dma_unencrypted(struct device *dev)
  {
Simple merge