UefiCpuPkg/MpInitLib: Add AsmRelocateApLoop() assembly code

AsmRelocateApLoop() is used to place APs into MWAIT-loop if MonitorMwait
feature is supported before hand-off to OS, or place APs into HLT-loop if
MonitorMwait feature is not supported.

If the current mode is long mode, we will switch APs to protected mode
before placing APs in MWAIT-loop or HLT-loop. Thus, once APs wakeup from
loop, APs needn't the page table that may be crashed by OS.

v3:
  1. Rename AsmRellocateApLoop to AsmRelocateApLoop.
  2. Fix typo Proteced to Protected.
  3. Fix typo segement to segment
  4. Use word MONITOR instead of mwait-monitor.

Cc: Michael Kinney <michael.d.kinney@intel.com>
Cc: Feng Tian <feng.tian@intel.com>
Cc: Giri P Mudusuru <giri.p.mudusuru@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.0
Signed-off-by: Jeff Fan <jeff.fan@intel.com>
Reviewed-by: Giri P Mudusuru <giri.p.mudusuru@intel.com>
Reviewed-by: Michael Kinney <michael.d.kinney@intel.com>
Tested-by: Laszlo Ersek <lersek@redhat.com>
Tested-by: Michael Kinney <michael.d.kinney@intel.com>
diff --git a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
index 8bacb42..49f5503 100644
--- a/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
+++ b/UefiCpuPkg/Library/MpInitLib/Ia32/MpFuncs.nasm
@@ -169,6 +169,30 @@
 RendezvousFunnelProcEnd:

 

 ;-------------------------------------------------------------------------------------

+;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment);

+;-------------------------------------------------------------------------------------

+global ASM_PFX(AsmRelocateApLoop)

+ASM_PFX(AsmRelocateApLoop):

+AsmRelocateApLoopStart:

+    cmp        byte [esp + 4], 1

+    jnz        HltLoop

+MwaitLoop:

+    mov        eax, esp

+    xor        ecx, ecx

+    xor        edx, edx

+    monitor

+    mov        eax, [esp + 8]    ; Mwait Cx, Target C-State per eax[7:4]

+    shl        eax, 4

+    mwait

+    jmp        MwaitLoop

+HltLoop:

+    cli

+    hlt

+    jmp        HltLoop

+    ret

+AsmRelocateApLoopEnd:

+

+;-------------------------------------------------------------------------------------

 ;  AsmGetAddressMap (&AddressMap);

 ;-------------------------------------------------------------------------------------

 global ASM_PFX(AsmGetAddressMap)

diff --git a/UefiCpuPkg/Library/MpInitLib/MpLib.h b/UefiCpuPkg/Library/MpInitLib/MpLib.h
index 28a3cd4..39ec5de 100644
--- a/UefiCpuPkg/Library/MpInitLib/MpLib.h
+++ b/UefiCpuPkg/Library/MpInitLib/MpLib.h
@@ -60,5 +60,26 @@
 } MP_CPU_EXCHANGE_INFO;

 

 #pragma pack()

+/**

+  Assembly code to place AP into safe loop mode.

+

+  Place AP into targeted C-State if MONITOR is supported, otherwise

+  place AP into hlt state.

+  Place AP in protected mode if the current is long mode. Due to AP maybe

+  wakeup by some hardware event. It could avoid accessing page table that

+  may not available during booting to OS.

+

+  @param[in] MwaitSupport    TRUE indicates MONITOR is supported.

+                             FALSE indicates MONITOR is not supported.

+  @param[in] ApTargetCState  Target C-State value.

+  @param[in] PmCodeSegment   Protected mode code segment value.

+**/

+typedef

+VOID

+(EFIAPI * ASM_RELOCATE_AP_LOOP) (

+  IN BOOLEAN                 MwaitSupport,

+  IN UINTN                   ApTargetCState,

+  IN UINTN                   PmCodeSegment

+  );

 #endif

 

diff --git a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
index 3c55ffa..d5d7efe 100644
--- a/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
+++ b/UefiCpuPkg/Library/MpInitLib/X64/MpFuncs.nasm
@@ -177,6 +177,60 @@
 RendezvousFunnelProcEnd:

 

 ;-------------------------------------------------------------------------------------

+;  AsmRelocateApLoop (MwaitSupport, ApTargetCState, PmCodeSegment);

+;-------------------------------------------------------------------------------------

+global ASM_PFX(AsmRelocateApLoop)

+ASM_PFX(AsmRelocateApLoop):

+AsmRelocateApLoopStart:

+    push       rcx

+    push       rdx

+

+    lea        rsi, [PmEntry]    ; rsi <- The start address of transition code

+

+    push       r8

+    push       rsi

+    DB         0x48

+    retf

+BITS 32

+PmEntry:

+    mov        eax, cr0

+    btr        eax, 31           ; Clear CR0.PG

+    mov        cr0, eax          ; Disable paging and caches

+

+    mov        ebx, edx          ; Save EntryPoint to rbx, for rdmsr will overwrite rdx

+    mov        ecx, 0xc0000080

+    rdmsr

+    and        ah, ~ 1           ; Clear LME

+    wrmsr

+    mov        eax, cr4

+    and        al, ~ (1 << 5)    ; Clear PAE

+    mov        cr4, eax

+

+    pop        edx

+    add        esp, 4

+    pop        ecx,

+    add        esp, 4

+    cmp        cl, 1              ; Check mwait-monitor support

+    jnz        HltLoop

+    mov        ebx, edx           ; Save C-State to ebx

+MwaitLoop:

+    mov        eax, esp           ; Set Monitor Address

+    xor        ecx, ecx           ; ecx = 0

+    xor        edx, edx           ; edx = 0

+    monitor

+    shl        ebx, 4

+    mov        eax, ebx           ; Mwait Cx, Target C-State per eax[7:4]

+    mwait

+    jmp        MwaitLoop

+HltLoop:

+    cli

+    hlt

+    jmp        HltLoop

+    ret

+BITS 64

+AsmRelocateApLoopEnd:

+

+;-------------------------------------------------------------------------------------

 ;  AsmGetAddressMap (&AddressMap);

 ;-------------------------------------------------------------------------------------

 global ASM_PFX(AsmGetAddressMap)