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

x86/efi: Add GHCB mappings when SEV-ES is active

Calling down to EFI runtime services can result in the firmware
performing VMGEXIT calls. The firmware is likely to use the GHCB of the
OS (e.g., for setting EFI variables), so each GHCB in the system needs
to be identity-mapped in the EFI page tables, as unencrypted, to avoid
page faults.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
[ jroedel@suse.de: Moved GHCB mapping loop to sev-es.c ]
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Signed-off-by: Borislav Petkov <bp@suse.de>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Link: https://lkml.kernel.org/r/20200907131613.12703-72-joro@8bytes.org

authored by

Tom Lendacky and committed by
Borislav Petkov
39336f4f 4ca68e02

+43
+1
arch/x86/boot/compressed/sev-es.c
··· 12 12 */ 13 13 #include "misc.h" 14 14 15 + #include <asm/pgtable_types.h> 15 16 #include <asm/sev-es.h> 16 17 #include <asm/trapnr.h> 17 18 #include <asm/trap_pf.h>
+2
arch/x86/include/asm/sev-es.h
··· 102 102 if (static_branch_unlikely(&sev_es_enable_key)) 103 103 __sev_es_nmi_complete(); 104 104 } 105 + extern int __init sev_es_efi_map_ghcbs(pgd_t *pgd); 105 106 #else 106 107 static inline void sev_es_ist_enter(struct pt_regs *regs) { } 107 108 static inline void sev_es_ist_exit(void) { } 108 109 static inline int sev_es_setup_ap_jump_table(struct real_mode_header *rmh) { return 0; } 109 110 static inline void sev_es_nmi_complete(void) { } 111 + static inline int sev_es_efi_map_ghcbs(pgd_t *pgd) { return 0; } 110 112 #endif 111 113 112 114 #endif
+30
arch/x86/kernel/sev-es.c
··· 491 491 return 0; 492 492 } 493 493 494 + /* 495 + * This is needed by the OVMF UEFI firmware which will use whatever it finds in 496 + * the GHCB MSR as its GHCB to talk to the hypervisor. So make sure the per-cpu 497 + * runtime GHCBs used by the kernel are also mapped in the EFI page-table. 498 + */ 499 + int __init sev_es_efi_map_ghcbs(pgd_t *pgd) 500 + { 501 + struct sev_es_runtime_data *data; 502 + unsigned long address, pflags; 503 + int cpu; 504 + u64 pfn; 505 + 506 + if (!sev_es_active()) 507 + return 0; 508 + 509 + pflags = _PAGE_NX | _PAGE_RW; 510 + 511 + for_each_possible_cpu(cpu) { 512 + data = per_cpu(runtime_data, cpu); 513 + 514 + address = __pa(&data->ghcb_page); 515 + pfn = address >> PAGE_SHIFT; 516 + 517 + if (kernel_map_pages_in_pgd(pgd, pfn, address, 1, pflags)) 518 + return 1; 519 + } 520 + 521 + return 0; 522 + } 523 + 494 524 static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt) 495 525 { 496 526 struct pt_regs *regs = ctxt->regs;
+10
arch/x86/platform/efi/efi_64.c
··· 47 47 #include <asm/realmode.h> 48 48 #include <asm/time.h> 49 49 #include <asm/pgalloc.h> 50 + #include <asm/sev-es.h> 50 51 51 52 /* 52 53 * We allocate runtime services regions top-down, starting from -4G, i.e. ··· 227 226 */ 228 227 if (kernel_map_pages_in_pgd(pgd, 0x0, 0x0, 1, pf)) { 229 228 pr_err("Failed to create 1:1 mapping for the first page!\n"); 229 + return 1; 230 + } 231 + 232 + /* 233 + * When SEV-ES is active, the GHCB as set by the kernel will be used 234 + * by firmware. Create a 1:1 unencrypted mapping for each GHCB. 235 + */ 236 + if (sev_es_efi_map_ghcbs(pgd)) { 237 + pr_err("Failed to create 1:1 mapping for the GHCBs!\n"); 230 238 return 1; 231 239 } 232 240