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

x86/efistub: Perform SNP feature test while running in the firmware

Before refactoring the EFI stub boot flow to avoid the legacy bare metal
decompressor, duplicate the SNP feature check in the EFI stub before
handing over to the kernel proper.

The SNP feature check can be performed while running under the EFI boot
services, which means it can force the boot to fail gracefully and
return an error to the bootloader if the loaded kernel does not
implement support for all the features that the hypervisor enabled.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/20230807162720.545787-23-ardb@kernel.org

authored by

Ard Biesheuvel and committed by
Borislav Petkov (AMD)
31c77a50 bc5ddcef

+87 -46
+66 -46
arch/x86/boot/compressed/sev.c
··· 367 367 */ 368 368 #define SNP_FEATURES_PRESENT (0) 369 369 370 + u64 snp_get_unsupported_features(u64 status) 371 + { 372 + if (!(status & MSR_AMD64_SEV_SNP_ENABLED)) 373 + return 0; 374 + 375 + return status & SNP_FEATURES_IMPL_REQ & ~SNP_FEATURES_PRESENT; 376 + } 377 + 370 378 void snp_check_features(void) 371 379 { 372 380 u64 unsupported; 373 - 374 - if (!(sev_status & MSR_AMD64_SEV_SNP_ENABLED)) 375 - return; 376 381 377 382 /* 378 383 * Terminate the boot if hypervisor has enabled any feature lacking ··· 385 380 * EXIT_INFO_2 of the GHCB protocol so that those features can be reported 386 381 * as part of the guest boot failure. 387 382 */ 388 - unsupported = sev_status & SNP_FEATURES_IMPL_REQ & ~SNP_FEATURES_PRESENT; 383 + unsupported = snp_get_unsupported_features(sev_status); 389 384 if (unsupported) { 390 385 if (ghcb_version < 2 || (!boot_ghcb && !early_setup_ghcb())) 391 386 sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SNP_UNSUPPORTED); ··· 395 390 } 396 391 } 397 392 398 - void sev_enable(struct boot_params *bp) 393 + /* 394 + * sev_check_cpu_support - Check for SEV support in the CPU capabilities 395 + * 396 + * Returns < 0 if SEV is not supported, otherwise the position of the 397 + * encryption bit in the page table descriptors. 398 + */ 399 + static int sev_check_cpu_support(void) 399 400 { 400 401 unsigned int eax, ebx, ecx, edx; 402 + 403 + /* Check for the SME/SEV support leaf */ 404 + eax = 0x80000000; 405 + ecx = 0; 406 + native_cpuid(&eax, &ebx, &ecx, &edx); 407 + if (eax < 0x8000001f) 408 + return -ENODEV; 409 + 410 + /* 411 + * Check for the SME/SEV feature: 412 + * CPUID Fn8000_001F[EAX] 413 + * - Bit 0 - Secure Memory Encryption support 414 + * - Bit 1 - Secure Encrypted Virtualization support 415 + * CPUID Fn8000_001F[EBX] 416 + * - Bits 5:0 - Pagetable bit position used to indicate encryption 417 + */ 418 + eax = 0x8000001f; 419 + ecx = 0; 420 + native_cpuid(&eax, &ebx, &ecx, &edx); 421 + /* Check whether SEV is supported */ 422 + if (!(eax & BIT(1))) 423 + return -ENODEV; 424 + 425 + return ebx & 0x3f; 426 + } 427 + 428 + void sev_enable(struct boot_params *bp) 429 + { 401 430 struct msr m; 431 + int bitpos; 402 432 bool snp; 403 433 404 434 /* ··· 453 413 * which is good enough. 454 414 */ 455 415 456 - /* Check for the SME/SEV support leaf */ 457 - eax = 0x80000000; 458 - ecx = 0; 459 - native_cpuid(&eax, &ebx, &ecx, &edx); 460 - if (eax < 0x8000001f) 461 - return; 462 - 463 - /* 464 - * Check for the SME/SEV feature: 465 - * CPUID Fn8000_001F[EAX] 466 - * - Bit 0 - Secure Memory Encryption support 467 - * - Bit 1 - Secure Encrypted Virtualization support 468 - * CPUID Fn8000_001F[EBX] 469 - * - Bits 5:0 - Pagetable bit position used to indicate encryption 470 - */ 471 - eax = 0x8000001f; 472 - ecx = 0; 473 - native_cpuid(&eax, &ebx, &ecx, &edx); 474 - /* Check whether SEV is supported */ 475 - if (!(eax & BIT(1))) 416 + if (sev_check_cpu_support() < 0) 476 417 return; 477 418 478 419 /* ··· 464 443 465 444 /* Now repeat the checks with the SNP CPUID table. */ 466 445 467 - /* Recheck the SME/SEV support leaf */ 468 - eax = 0x80000000; 469 - ecx = 0; 470 - native_cpuid(&eax, &ebx, &ecx, &edx); 471 - if (eax < 0x8000001f) 472 - return; 473 - 474 - /* 475 - * Recheck for the SME/SEV feature: 476 - * CPUID Fn8000_001F[EAX] 477 - * - Bit 0 - Secure Memory Encryption support 478 - * - Bit 1 - Secure Encrypted Virtualization support 479 - * CPUID Fn8000_001F[EBX] 480 - * - Bits 5:0 - Pagetable bit position used to indicate encryption 481 - */ 482 - eax = 0x8000001f; 483 - ecx = 0; 484 - native_cpuid(&eax, &ebx, &ecx, &edx); 485 - /* Check whether SEV is supported */ 486 - if (!(eax & BIT(1))) { 446 + bitpos = sev_check_cpu_support(); 447 + if (bitpos < 0) { 487 448 if (snp) 488 449 error("SEV-SNP support indicated by CC blob, but not CPUID."); 489 450 return; ··· 497 494 if (snp && !(sev_status & MSR_AMD64_SEV_SNP_ENABLED)) 498 495 error("SEV-SNP supported indicated by CC blob, but not SEV status MSR."); 499 496 500 - sme_me_mask = BIT_ULL(ebx & 0x3f); 497 + sme_me_mask = BIT_ULL(bitpos); 498 + } 499 + 500 + /* 501 + * sev_get_status - Retrieve the SEV status mask 502 + * 503 + * Returns 0 if the CPU is not SEV capable, otherwise the value of the 504 + * AMD64_SEV MSR. 505 + */ 506 + u64 sev_get_status(void) 507 + { 508 + struct msr m; 509 + 510 + if (sev_check_cpu_support() < 0) 511 + return 0; 512 + 513 + boot_rdmsr(MSR_AMD64_SEV, &m); 514 + return m.q; 501 515 } 502 516 503 517 /* Search for Confidential Computing blob in the EFI config table. */
+4
arch/x86/include/asm/sev.h
··· 210 210 void __init __noreturn snp_abort(void); 211 211 int snp_issue_guest_request(u64 exit_code, struct snp_req_data *input, struct snp_guest_request_ioctl *rio); 212 212 void snp_accept_memory(phys_addr_t start, phys_addr_t end); 213 + u64 snp_get_unsupported_features(u64 status); 214 + u64 sev_get_status(void); 213 215 #else 214 216 static inline void sev_es_ist_enter(struct pt_regs *regs) { } 215 217 static inline void sev_es_ist_exit(void) { } ··· 237 235 } 238 236 239 237 static inline void snp_accept_memory(phys_addr_t start, phys_addr_t end) { } 238 + static inline u64 snp_get_unsupported_features(u64 status) { return 0; } 239 + static inline u64 sev_get_status(void) { return 0; } 240 240 #endif 241 241 242 242 #endif
+17
drivers/firmware/efi/libstub/x86-stub.c
··· 15 15 #include <asm/setup.h> 16 16 #include <asm/desc.h> 17 17 #include <asm/boot.h> 18 + #include <asm/sev.h> 18 19 19 20 #include "efistub.h" 20 21 #include "x86-stub.h" ··· 791 790 return EFI_SUCCESS; 792 791 } 793 792 793 + static bool have_unsupported_snp_features(void) 794 + { 795 + u64 unsupported; 796 + 797 + unsupported = snp_get_unsupported_features(sev_get_status()); 798 + if (unsupported) { 799 + efi_err("Unsupported SEV-SNP features detected: 0x%llx\n", 800 + unsupported); 801 + return true; 802 + } 803 + return false; 804 + } 805 + 794 806 static void __noreturn enter_kernel(unsigned long kernel_addr, 795 807 struct boot_params *boot_params) 796 808 { ··· 833 819 /* Check if we were booted by the EFI firmware */ 834 820 if (efi_system_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE) 835 821 efi_exit(handle, EFI_INVALID_PARAMETER); 822 + 823 + if (have_unsupported_snp_features()) 824 + efi_exit(handle, EFI_UNSUPPORTED); 836 825 837 826 if (IS_ENABLED(CONFIG_EFI_DXE_MEM_ATTRIBUTES)) { 838 827 efi_dxe_table = get_efi_config_table(EFI_DXE_SERVICES_TABLE_GUID);