|
28 | 28 | #include <linux/swait.h> |
29 | 29 | #include <linux/syscore_ops.h> |
30 | 30 | #include <linux/cc_platform.h> |
| 31 | +#include <linux/efi.h> |
31 | 32 | #include <asm/timer.h> |
32 | 33 | #include <asm/cpu.h> |
33 | 34 | #include <asm/traps.h> |
|
41 | 42 | #include <asm/ptrace.h> |
42 | 43 | #include <asm/reboot.h> |
43 | 44 | #include <asm/svm.h> |
| 45 | +#include <asm/e820/api.h> |
44 | 46 |
|
45 | 47 | DEFINE_STATIC_KEY_FALSE(kvm_async_pf_enabled); |
46 | 48 |
|
@@ -434,6 +436,8 @@ static void kvm_guest_cpu_offline(bool shutdown) |
434 | 436 | kvm_disable_steal_time(); |
435 | 437 | if (kvm_para_has_feature(KVM_FEATURE_PV_EOI)) |
436 | 438 | wrmsrl(MSR_KVM_PV_EOI_EN, 0); |
| 439 | + if (kvm_para_has_feature(KVM_FEATURE_MIGRATION_CONTROL)) |
| 440 | + wrmsrl(MSR_KVM_MIGRATION_CONTROL, 0); |
437 | 441 | kvm_pv_disable_apf(); |
438 | 442 | if (!shutdown) |
439 | 443 | apf_task_wake_all(); |
@@ -548,6 +552,55 @@ static void kvm_send_ipi_mask_allbutself(const struct cpumask *mask, int vector) |
548 | 552 | __send_ipi_mask(local_mask, vector); |
549 | 553 | } |
550 | 554 |
|
| 555 | +static int __init setup_efi_kvm_sev_migration(void) |
| 556 | +{ |
| 557 | + efi_char16_t efi_sev_live_migration_enabled[] = L"SevLiveMigrationEnabled"; |
| 558 | + efi_guid_t efi_variable_guid = AMD_SEV_MEM_ENCRYPT_GUID; |
| 559 | + efi_status_t status; |
| 560 | + unsigned long size; |
| 561 | + bool enabled; |
| 562 | + |
| 563 | + if (!cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT) || |
| 564 | + !kvm_para_has_feature(KVM_FEATURE_MIGRATION_CONTROL)) |
| 565 | + return 0; |
| 566 | + |
| 567 | + if (!efi_enabled(EFI_BOOT)) |
| 568 | + return 0; |
| 569 | + |
| 570 | + if (!efi_enabled(EFI_RUNTIME_SERVICES)) { |
| 571 | + pr_info("%s : EFI runtime services are not enabled\n", __func__); |
| 572 | + return 0; |
| 573 | + } |
| 574 | + |
| 575 | + size = sizeof(enabled); |
| 576 | + |
| 577 | + /* Get variable contents into buffer */ |
| 578 | + status = efi.get_variable(efi_sev_live_migration_enabled, |
| 579 | + &efi_variable_guid, NULL, &size, &enabled); |
| 580 | + |
| 581 | + if (status == EFI_NOT_FOUND) { |
| 582 | + pr_info("%s : EFI live migration variable not found\n", __func__); |
| 583 | + return 0; |
| 584 | + } |
| 585 | + |
| 586 | + if (status != EFI_SUCCESS) { |
| 587 | + pr_info("%s : EFI variable retrieval failed\n", __func__); |
| 588 | + return 0; |
| 589 | + } |
| 590 | + |
| 591 | + if (enabled == 0) { |
| 592 | + pr_info("%s: live migration disabled in EFI\n", __func__); |
| 593 | + return 0; |
| 594 | + } |
| 595 | + |
| 596 | + pr_info("%s : live migration enabled in EFI\n", __func__); |
| 597 | + wrmsrl(MSR_KVM_MIGRATION_CONTROL, KVM_MIGRATION_READY); |
| 598 | + |
| 599 | + return 1; |
| 600 | +} |
| 601 | + |
| 602 | +late_initcall(setup_efi_kvm_sev_migration); |
| 603 | + |
551 | 604 | /* |
552 | 605 | * Set the IPI entry points |
553 | 606 | */ |
@@ -806,8 +859,62 @@ static bool __init kvm_msi_ext_dest_id(void) |
806 | 859 | return kvm_para_has_feature(KVM_FEATURE_MSI_EXT_DEST_ID); |
807 | 860 | } |
808 | 861 |
|
| 862 | +static void kvm_sev_hc_page_enc_status(unsigned long pfn, int npages, bool enc) |
| 863 | +{ |
| 864 | + kvm_sev_hypercall3(KVM_HC_MAP_GPA_RANGE, pfn << PAGE_SHIFT, npages, |
| 865 | + KVM_MAP_GPA_RANGE_ENC_STAT(enc) | KVM_MAP_GPA_RANGE_PAGE_SZ_4K); |
| 866 | +} |
| 867 | + |
809 | 868 | static void __init kvm_init_platform(void) |
810 | 869 | { |
| 870 | + if (cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT) && |
| 871 | + kvm_para_has_feature(KVM_FEATURE_MIGRATION_CONTROL)) { |
| 872 | + unsigned long nr_pages; |
| 873 | + int i; |
| 874 | + |
| 875 | + pv_ops.mmu.notify_page_enc_status_changed = |
| 876 | + kvm_sev_hc_page_enc_status; |
| 877 | + |
| 878 | + /* |
| 879 | + * Reset the host's shared pages list related to kernel |
| 880 | + * specific page encryption status settings before we load a |
| 881 | + * new kernel by kexec. Reset the page encryption status |
| 882 | + * during early boot intead of just before kexec to avoid SMP |
| 883 | + * races during kvm_pv_guest_cpu_reboot(). |
| 884 | + * NOTE: We cannot reset the complete shared pages list |
| 885 | + * here as we need to retain the UEFI/OVMF firmware |
| 886 | + * specific settings. |
| 887 | + */ |
| 888 | + |
| 889 | + for (i = 0; i < e820_table->nr_entries; i++) { |
| 890 | + struct e820_entry *entry = &e820_table->entries[i]; |
| 891 | + |
| 892 | + if (entry->type != E820_TYPE_RAM) |
| 893 | + continue; |
| 894 | + |
| 895 | + nr_pages = DIV_ROUND_UP(entry->size, PAGE_SIZE); |
| 896 | + |
| 897 | + kvm_sev_hypercall3(KVM_HC_MAP_GPA_RANGE, entry->addr, |
| 898 | + nr_pages, |
| 899 | + KVM_MAP_GPA_RANGE_ENCRYPTED | KVM_MAP_GPA_RANGE_PAGE_SZ_4K); |
| 900 | + } |
| 901 | + |
| 902 | + /* |
| 903 | + * Ensure that _bss_decrypted section is marked as decrypted in the |
| 904 | + * shared pages list. |
| 905 | + */ |
| 906 | + nr_pages = DIV_ROUND_UP(__end_bss_decrypted - __start_bss_decrypted, |
| 907 | + PAGE_SIZE); |
| 908 | + early_set_mem_enc_dec_hypercall((unsigned long)__start_bss_decrypted, |
| 909 | + nr_pages, 0); |
| 910 | + |
| 911 | + /* |
| 912 | + * If not booted using EFI, enable Live migration support. |
| 913 | + */ |
| 914 | + if (!efi_enabled(EFI_BOOT)) |
| 915 | + wrmsrl(MSR_KVM_MIGRATION_CONTROL, |
| 916 | + KVM_MIGRATION_READY); |
| 917 | + } |
811 | 918 | kvmclock_init(); |
812 | 919 | x86_platform.apic_post_init = kvm_apic_init; |
813 | 920 | } |
|
0 commit comments