/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AMD Memory Encryption Support
*
* Copyright ( C ) 2017 Advanced Micro Devices , Inc .
*
* Author : Tom Lendacky < thomas . lendacky @ amd . com >
*/
#include <linux/linkage.h>
#include <asm/processor-flags.h>
#include <asm/msr.h>
#include <asm/asm-offsets.h>
#include <asm/segment.h>
#include <asm/trapnr.h>
.text
.code32
SYM_FUNC_START(get_sev_encryption_bit)
push %ebx
movl $0 x80000000, %eax /* CPUID to check the highest leaf */
cpuid
cmpl $0 x8000001f, %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 $0 x8000001f, %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 $0 x3f, %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 $0 x00000004, %eax
movl $MSR_AMD64_SEV_ES_GHCB, %ecx
wrmsr
rep; vmmcall # VMGEXIT
rdmsr
/* Check response */
movl %eax, %ecx
andl $0 x3ffff000, %ecx # Bits [12 -29 ] MBZ
jnz 2 f
/* Check return code */
andl $0 xfff, %eax
cmpl $5 , %eax
jne 2 f
/* All good - return success */
xorl %eax, %eax
1 :
RET
2 :
movl $-1 , %eax
jmp 1 b
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 $0 x72, 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 $0 x80000000, %ebx
jne .Lcheck_sev
cmpl $0 x8000001f, 12 (%esp)
jb .Lfail
jmp .Ldone
.Lcheck_sev:
/* Fail if SEV bit not set in CPUID[0x8000001f].EAX[1] */
cmpl $0 x8000001f, %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 $0 x100, %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 $0 x0000ffff, %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 $0 xffff0000, %edx # Target code segment offset [31 :16 ]
orl $0 x00008e00, %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 1 f
1 : pop %ebp
leal (boot32_idt - 1 b)(%ebp), %ebx
/* #VC handler */
leal (startup32_vc_handler - 1 b)(%ebp), %eax
movl $X86_TRAP_VC, %edx
movl %ebx, %ecx
call startup32_set_idt_entry
/* Load IDT */
leal (boot32_idt_desc - 1 b)(%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 0 f
0 : popl %ebp
/* Check for non-zero sev_status */
movl (sev_status - 0 b)(%ebp), %eax
testl %eax, %eax
jz 4 f
/*
* 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 1 b
2 : rdrand %ebx
jnc 2 b
/* Store to memory and keep it in the registers */
leal (sev_check_data - 0 b)(%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 3 f
cmpl %ebx, 4 (%ebp)
jne 3 f
movl %edx, %cr0 /* Restore previous %cr0 */
jmp 4 f
3 : /* Check failed - hlt the machine */
hlt
jmp 3 b
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)
Messung V0.5 in Prozent C=89 H=100 G=94
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland