/* SPDX-License-Identifier: GPL-2.0-only */ /* * AMD Memory Encryption Support * * Copyright (C) 2017 Advanced Micro Devices, Inc. * * Author: Tom Lendacky */ #include #include #include #include #include #include .text .code32 SYM_FUNC_START(get_sev_encryption_bit) push %ebx movl $0x80000000, %eax /* CPUID to check the highest leaf */ cpuid cmpl $0x8000001f, %eax /* See if 0x8000001f is available */ jb .Lno_sev /* * Check for the SEV feature: * CPUID Fn8000_001F[EAX] - Bit 1 * CPUID Fn8000_001F[EBX] - Bits 5:0 * Pagetable bit position used to indicate encryption */ movl $0x8000001f, %eax cpuid bt $1, %eax /* Check if SEV is available */ jnc .Lno_sev movl $MSR_AMD64_SEV, %ecx /* Read the SEV MSR */ rdmsr bt $MSR_AMD64_SEV_ENABLED_BIT, %eax /* Check if SEV is active */ jnc .Lno_sev movl %ebx, %eax andl $0x3f, %eax /* Return the encryption bit location */ jmp .Lsev_exit .Lno_sev: xor %eax, %eax .Lsev_exit: pop %ebx RET SYM_FUNC_END(get_sev_encryption_bit) /** * sev_es_req_cpuid - Request a CPUID value from the Hypervisor using * the GHCB MSR protocol * * @%eax: Register to request (0=EAX, 1=EBX, 2=ECX, 3=EDX) * @%edx: CPUID Function * * Returns 0 in %eax on success, non-zero on failure * %edx returns CPUID value on success */ SYM_CODE_START_LOCAL(sev_es_req_cpuid) shll $30, %eax orl $0x00000004, %eax movl $MSR_AMD64_SEV_ES_GHCB, %ecx wrmsr rep; vmmcall # VMGEXIT rdmsr /* Check response */ movl %eax, %ecx andl $0x3ffff000, %ecx # Bits [12-29] MBZ jnz 2f /* Check return code */ andl $0xfff, %eax cmpl $5, %eax jne 2f /* All good - return success */ xorl %eax, %eax 1: RET 2: movl $-1, %eax jmp 1b SYM_CODE_END(sev_es_req_cpuid) SYM_CODE_START_LOCAL(startup32_vc_handler) pushl %eax pushl %ebx pushl %ecx pushl %edx /* Keep CPUID function in %ebx */ movl %eax, %ebx /* Check if error-code == SVM_EXIT_CPUID */ cmpl $0x72, 16(%esp) jne .Lfail movl $0, %eax # Request CPUID[fn].EAX movl %ebx, %edx # CPUID fn call sev_es_req_cpuid # Call helper testl %eax, %eax # Check return code jnz .Lfail movl %edx, 12(%esp) # Store result movl $1, %eax # Request CPUID[fn].EBX movl %ebx, %edx # CPUID fn call sev_es_req_cpuid # Call helper testl %eax, %eax # Check return code jnz .Lfail movl %edx, 8(%esp) # Store result movl $2, %eax # Request CPUID[fn].ECX movl %ebx, %edx # CPUID fn call sev_es_req_cpuid # Call helper testl %eax, %eax # Check return code jnz .Lfail movl %edx, 4(%esp) # Store result movl $3, %eax # Request CPUID[fn].EDX movl %ebx, %edx # CPUID fn call sev_es_req_cpuid # Call helper testl %eax, %eax # Check return code jnz .Lfail movl %edx, 0(%esp) # Store result /* * Sanity check CPUID results from the Hypervisor. See comment in * do_vc_no_ghcb() for more details on why this is necessary. */ /* Fail if SEV leaf not available in CPUID[0x80000000].EAX */ cmpl $0x80000000, %ebx jne .Lcheck_sev cmpl $0x8000001f, 12(%esp) jb .Lfail jmp .Ldone .Lcheck_sev: /* Fail if SEV bit not set in CPUID[0x8000001f].EAX[1] */ cmpl $0x8000001f, %ebx jne .Ldone btl $1, 12(%esp) jnc .Lfail .Ldone: popl %edx popl %ecx popl %ebx popl %eax /* Remove error code */ addl $4, %esp /* Jump over CPUID instruction */ addl $2, (%esp) iret .Lfail: /* Send terminate request to Hypervisor */ movl $0x100, %eax xorl %edx, %edx movl $MSR_AMD64_SEV_ES_GHCB, %ecx wrmsr rep; vmmcall /* If request fails, go to hlt loop */ hlt jmp .Lfail SYM_CODE_END(startup32_vc_handler) /* * Write an IDT entry into boot32_idt * * Parameters: * * %eax: Handler address * %edx: Vector number * %ecx: IDT address */ SYM_FUNC_START_LOCAL(startup32_set_idt_entry) /* IDT entry address to %ecx */ leal (%ecx, %edx, 8), %ecx /* Build IDT entry, lower 4 bytes */ movl %eax, %edx andl $0x0000ffff, %edx # Target code segment offset [15:0] orl $(__KERNEL32_CS << 16), %edx # Target code segment selector /* Store lower 4 bytes to IDT */ movl %edx, (%ecx) /* Build IDT entry, upper 4 bytes */ movl %eax, %edx andl $0xffff0000, %edx # Target code segment offset [31:16] orl $0x00008e00, %edx # Present, Type 32-bit Interrupt Gate /* Store upper 4 bytes to IDT */ movl %edx, 4(%ecx) RET SYM_FUNC_END(startup32_set_idt_entry) SYM_FUNC_START(startup32_load_idt) push %ebp push %ebx call 1f 1: pop %ebp leal (boot32_idt - 1b)(%ebp), %ebx /* #VC handler */ leal (startup32_vc_handler - 1b)(%ebp), %eax movl $X86_TRAP_VC, %edx movl %ebx, %ecx call startup32_set_idt_entry /* Load IDT */ leal (boot32_idt_desc - 1b)(%ebp), %ecx movl %ebx, 2(%ecx) lidt (%ecx) pop %ebx pop %ebp RET SYM_FUNC_END(startup32_load_idt) /* * Check for the correct C-bit position when the startup_32 boot-path is used. * * The check makes use of the fact that all memory is encrypted when paging is * disabled. The function creates 64 bits of random data using the RDRAND * instruction. RDRAND is mandatory for SEV guests, so always available. If the * hypervisor violates that the kernel will crash right here. * * The 64 bits of random data are stored to a memory location and at the same * time kept in the %eax and %ebx registers. Since encryption is always active * when paging is off the random data will be stored encrypted in main memory. * * Then paging is enabled. When the C-bit position is correct all memory is * still mapped encrypted and comparing the register values with memory will * succeed. An incorrect C-bit position will map all memory unencrypted, so that * the compare will use the encrypted random data and fail. */ SYM_FUNC_START(startup32_check_sev_cbit) pushl %ebx pushl %ebp call 0f 0: popl %ebp /* Check for non-zero sev_status */ movl (sev_status - 0b)(%ebp), %eax testl %eax, %eax jz 4f /* * Get two 32-bit random values - Don't bail out if RDRAND fails * because it is better to prevent forward progress if no random value * can be gathered. */ 1: rdrand %eax jnc 1b 2: rdrand %ebx jnc 2b /* Store to memory and keep it in the registers */ leal (sev_check_data - 0b)(%ebp), %ebp movl %eax, 0(%ebp) movl %ebx, 4(%ebp) /* Enable paging to see if encryption is active */ movl %cr0, %edx /* Backup %cr0 in %edx */ movl $(X86_CR0_PG | X86_CR0_PE), %ecx /* Enable Paging and Protected mode */ movl %ecx, %cr0 cmpl %eax, 0(%ebp) jne 3f cmpl %ebx, 4(%ebp) jne 3f movl %edx, %cr0 /* Restore previous %cr0 */ jmp 4f 3: /* Check failed - hlt the machine */ hlt jmp 3b 4: popl %ebp popl %ebx RET SYM_FUNC_END(startup32_check_sev_cbit) .code64 #include "../../kernel/sev_verify_cbit.S" .data .balign 8 SYM_DATA(sme_me_mask, .quad 0) SYM_DATA(sev_status, .quad 0) SYM_DATA(sev_check_data, .quad 0) SYM_DATA_START_LOCAL(boot32_idt) .rept 32 .quad 0 .endr SYM_DATA_END(boot32_idt) SYM_DATA_START_LOCAL(boot32_idt_desc) .word . - boot32_idt - 1 .long 0 SYM_DATA_END(boot32_idt_desc)