root/arch/x86/boot/compressed/mem_encrypt.S
/* 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    $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)