x86/boot: Set CR0.NE early and keep it set during the boot
authorKirill A. Shutemov <kirill.shutemov@linux.intel.com>
Tue, 5 Apr 2022 23:29:31 +0000 (02:29 +0300)
committerDave Hansen <dave.hansen@linux.intel.com>
Thu, 7 Apr 2022 15:27:53 +0000 (08:27 -0700)
TDX guest requires CR0.NE to be set. Clearing the bit triggers #GP(0).

If CR0.NE is 0, the MS-DOS compatibility mode for handling floating-point
exceptions is selected. In this mode, the software exception handler for
floating-point exceptions is invoked externally using the processor’s
FERR#, INTR, and IGNNE# pins.

Using FERR# and IGNNE# to handle floating-point exception is deprecated.
CR0.NE=0 also limits newer processors to operate with one logical
processor active.

Kernel uses CR0_STATE constant to initialize CR0. It has NE bit set.
But during early boot kernel has more ad-hoc approach to setting bit
in the register. During some of this ad-hoc manipulation, CR0.NE is
cleared. This causes a #GP in TDX guests and makes it die in early boot.

Make CR0 initialization consistent, deriving the initial value of CR0
from CR0_STATE. Since CR0_STATE always has CR0.NE=1, this ensures that
CR0.NE is never 0 and avoids the #GP.

Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20220405232939.73860-23-kirill.shutemov@linux.intel.com
arch/x86/boot/compressed/head_64.S
arch/x86/realmode/rm/trampoline_64.S

index dea95301196b8550fdd3faa81355bb20eeba93c8..7b5d3621435297e0ac7e261f1e24694e309086ca 100644 (file)
@@ -289,7 +289,7 @@ SYM_FUNC_START(startup_32)
        pushl   %eax
 
        /* Enter paged protected Mode, activating Long Mode */
-       movl    $(X86_CR0_PG | X86_CR0_PE), %eax /* Enable Paging and Protected mode */
+       movl    $CR0_STATE, %eax
        movl    %eax, %cr0
 
        /* Jump from 32bit compatibility mode into 64bit mode. */
@@ -661,8 +661,9 @@ SYM_CODE_START(trampoline_32bit_src)
        pushl   $__KERNEL_CS
        pushl   %eax
 
-       /* Enable paging again */
-       movl    $(X86_CR0_PG | X86_CR0_PE), %eax
+       /* Enable paging again. */
+       movl    %cr0, %eax
+       btsl    $X86_CR0_PG_BIT, %eax
        movl    %eax, %cr0
 
        lret
index ae112a91592faa1a35d433a65adede869e72bdc7..d380f2d1fd23ab6d64cd0f7bb9ffc052dc437ca2 100644 (file)
@@ -70,7 +70,7 @@ SYM_CODE_START(trampoline_start)
        movw    $__KERNEL_DS, %dx       # Data segment descriptor
 
        # Enable protected mode
-       movl    $X86_CR0_PE, %eax       # protected mode (PE) bit
+       movl    $(CR0_STATE & ~X86_CR0_PG), %eax
        movl    %eax, %cr0              # into protected mode
 
        # flush prefetch and jump to startup_32
@@ -148,8 +148,8 @@ SYM_CODE_START(startup_32)
        movl    $MSR_EFER, %ecx
        wrmsr
 
-       # Enable paging and in turn activate Long Mode
-       movl    $(X86_CR0_PG | X86_CR0_WP | X86_CR0_PE), %eax
+       # Enable paging and in turn activate Long Mode.
+       movl    $CR0_STATE, %eax
        movl    %eax, %cr0
 
        /*
@@ -169,7 +169,7 @@ SYM_CODE_START(pa_trampoline_compat)
        movl    $rm_stack_end, %esp
        movw    $__KERNEL_DS, %dx
 
-       movl    $X86_CR0_PE, %eax
+       movl    $(CR0_STATE & ~X86_CR0_PG), %eax
        movl    %eax, %cr0
        ljmpl   $__KERNEL32_CS, $pa_startup_32
 SYM_CODE_END(pa_trampoline_compat)