UefiCpuPkg/MpInitLib: split wake up buffer into two parts

If PcdDxeNxMemoryProtectionPolicy is set to enable protection for memory
of EfiBootServicesCode, EfiConventionalMemory, the BIOS will hang at a page
fault exception during MP initialization.

The root cause is that the AP wake up buffer, which is below 1MB and used
to hold both AP init code and data, is type of EfiConventionalMemory (not
really allocated because of potential conflict with legacy code), and is
marked as non-executable. During the transition from real address mode
to long mode, the AP init code has to enable paging which will then cause
itself a page fault exception because it's just running in non-executable
memory.

The solution is splitting AP wake up buffer into two part: lower part is
still below 1MB and shared with legacy system, higher part is really
allocated memory of BootServicesCode type. The init code in the memory
below 1MB will not enable paging but just switch to protected mode and
jump to higher memory, in which the init code will enable paging and
switch to long mode.

Cc: Jiewen Yao <jiewen.yao@intel.com>
Cc: Ruiyu Ni <ruiyu.ni@intel.com>
Cc: Eric Dong <eric.dong@intel.com>
Cc: Laszlo Ersek <lersek@redhat.com>
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Jian J Wang <jian.j.wang@intel.com>
Reviewed-by: Eric Dong <eric.dong@intel.com>
This commit is contained in:
Jian J Wang 2017-12-29 09:12:54 +08:00 committed by Ruiyu Ni
parent 4f10654e04
commit f32bfe6d06
8 changed files with 204 additions and 49 deletions

View File

@ -113,6 +113,40 @@ GetWakeupBuffer (
return (UINTN) StartAddress; return (UINTN) StartAddress;
} }
/**
Get available EfiBootServicesCode memory below 4GB by specified size.
This buffer is required to safely transfer AP from real address mode to
protected mode or long mode, due to the fact that the buffer returned by
GetWakeupBuffer() may be marked as non-executable.
@param[in] BufferSize Wakeup transition buffer size.
@retval other Return wakeup transition buffer address below 4GB.
@retval 0 Cannot find free memory below 4GB.
**/
UINTN
GetModeTransitionBuffer (
IN UINTN BufferSize
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS StartAddress;
StartAddress = BASE_4GB - 1;
Status = gBS->AllocatePages (
AllocateMaxAddress,
EfiBootServicesCode,
EFI_SIZE_TO_PAGES (BufferSize),
&StartAddress
);
if (EFI_ERROR (Status)) {
StartAddress = 0;
}
return (UINTN)StartAddress;
}
/** /**
Checks APs status and updates APs status if needed. Checks APs status and updates APs status if needed.

View File

@ -41,4 +41,9 @@ Cr3Location equ LockLocation + 34h
InitFlagLocation equ LockLocation + 38h InitFlagLocation equ LockLocation + 38h
CpuInfoLocation equ LockLocation + 3Ch CpuInfoLocation equ LockLocation + 3Ch
NumApsExecutingLocation equ LockLocation + 40h NumApsExecutingLocation equ LockLocation + 40h
InitializeFloatingPointUnitsAddress equ LockLocation + 48h
ModeTransitionMemoryLocation equ LockLocation + 4Ch
ModeTransitionSegmentLocation equ LockLocation + 50h
ModeHighMemoryLocation equ LockLocation + 52h
ModeHighSegmentLocation equ LockLocation + 56h

View File

@ -48,34 +48,35 @@ BITS 16
mov si, BufferStartLocation mov si, BufferStartLocation
mov ebx, [si] mov ebx, [si]
mov si, ModeOffsetLocation
mov eax, [si]
mov si, CodeSegmentLocation
mov edx, [si]
mov di, ax
sub di, 02h
mov [di], dx
sub di, 04h
add eax, ebx
mov [di],eax
mov si, DataSegmentLocation mov si, DataSegmentLocation
mov edx, [si] mov edx, [si]
;
; Get start address of 32-bit code in low memory (<1MB)
;
mov edi, ModeTransitionMemoryLocation
mov si, GdtrLocation mov si, GdtrLocation
o32 lgdt [cs:si] o32 lgdt [cs:si]
mov si, IdtrLocation mov si, IdtrLocation
o32 lidt [cs:si] o32 lidt [cs:si]
xor ax, ax ;
mov ds, ax ; Switch to protected mode
;
mov eax, cr0 ; Get control register 0 mov eax, cr0 ; Get control register 0
or eax, 000000003h ; Set PE bit (bit #0) & MP or eax, 000000003h ; Set PE bit (bit #0) & MP
mov cr0, eax mov cr0, eax
jmp 0:strict dword 0 ; far jump to protected mode ; Switch to 32-bit code in executable memory (>1MB)
o32 jmp far [cs:di]
;
; Following code may be copied to memory with type of EfiBootServicesCode.
; This is required at DXE phase if NX is enabled for EfiBootServicesCode of
; memory.
;
BITS 32 BITS 32
Flat32Start: ; protected mode entry point Flat32Start: ; protected mode entry point
mov ds, dx mov ds, dx
@ -266,6 +267,7 @@ ASM_PFX(AsmGetAddressMap):
mov dword [ebx + 8h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart mov dword [ebx + 8h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart
mov dword [ebx + 0Ch], AsmRelocateApLoopStart mov dword [ebx + 0Ch], AsmRelocateApLoopStart
mov dword [ebx + 10h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart mov dword [ebx + 10h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
mov dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart
popad popad
ret ret

View File

@ -772,6 +772,8 @@ FillExchangeInfoData (
) )
{ {
volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo; volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
UINTN Size;
IA32_SEGMENT_DESCRIPTOR *Selector;
ExchangeInfo = CpuMpData->MpCpuExchangeInfo; ExchangeInfo = CpuMpData->MpCpuExchangeInfo;
ExchangeInfo->Lock = 0; ExchangeInfo->Lock = 0;
@ -801,6 +803,44 @@ FillExchangeInfoData (
// //
AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile); AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile);
AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile); AsmReadIdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->IdtrProfile);
//
// Find a 32-bit code segment
//
Selector = (IA32_SEGMENT_DESCRIPTOR *)ExchangeInfo->GdtrProfile.Base;
Size = ExchangeInfo->GdtrProfile.Limit + 1;
while (Size > 0) {
if (Selector->Bits.L == 0 && Selector->Bits.Type >= 8) {
ExchangeInfo->ModeTransitionSegment =
(UINT16)((UINTN)Selector - ExchangeInfo->GdtrProfile.Base);
break;
}
Selector += 1;
Size -= sizeof (IA32_SEGMENT_DESCRIPTOR);
}
//
// Copy all 32-bit code and 64-bit code into memory with type of
// EfiBootServicesCode to avoid page fault if NX memory protection is enabled.
//
if (ExchangeInfo->ModeTransitionMemory != 0) {
Size = CpuMpData->AddressMap.RendezvousFunnelSize -
CpuMpData->AddressMap.ModeTransitionOffset;
CopyMem (
(VOID *)(UINTN)ExchangeInfo->ModeTransitionMemory,
CpuMpData->AddressMap.RendezvousFunnelAddress +
CpuMpData->AddressMap.ModeTransitionOffset,
Size
);
ExchangeInfo->ModeHighMemory = ExchangeInfo->ModeTransitionMemory;
ExchangeInfo->ModeHighMemory += (UINT32)ExchangeInfo->ModeOffset -
(UINT32)CpuMpData->AddressMap.ModeTransitionOffset;
ExchangeInfo->ModeHighSegment = (UINT16)ExchangeInfo->CodeSegment;
} else {
ExchangeInfo->ModeTransitionMemory = (UINT32)
(ExchangeInfo->BufferStart + CpuMpData->AddressMap.ModeTransitionOffset);
}
} }
/** /**
@ -876,6 +916,11 @@ AllocateResetVector (
CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize); CpuMpData->WakeupBuffer = GetWakeupBuffer (ApResetVectorSize);
CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN) CpuMpData->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)
(CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize); (CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);
CpuMpData->MpCpuExchangeInfo->ModeTransitionMemory = (UINT32)
GetModeTransitionBuffer (
CpuMpData->AddressMap.RendezvousFunnelSize -
CpuMpData->AddressMap.ModeTransitionOffset
);
} }
BackupAndPrepareWakeupBuffer (CpuMpData); BackupAndPrepareWakeupBuffer (CpuMpData);
} }

View File

@ -152,6 +152,7 @@ typedef struct {
UINTN RendezvousFunnelSize; UINTN RendezvousFunnelSize;
UINT8 *RelocateApLoopFuncAddress; UINT8 *RelocateApLoopFuncAddress;
UINTN RelocateApLoopFuncSize; UINTN RelocateApLoopFuncSize;
UINTN ModeTransitionOffset;
} MP_ASSEMBLY_ADDRESS_MAP; } MP_ASSEMBLY_ADDRESS_MAP;
typedef struct _CPU_MP_DATA CPU_MP_DATA; typedef struct _CPU_MP_DATA CPU_MP_DATA;
@ -182,6 +183,10 @@ typedef struct {
UINTN NumApsExecuting; UINTN NumApsExecuting;
CPU_MP_DATA *CpuMpData; CPU_MP_DATA *CpuMpData;
UINTN InitializeFloatingPointUnitsAddress; UINTN InitializeFloatingPointUnitsAddress;
UINT32 ModeTransitionMemory;
UINT16 ModeTransitionSegment;
UINT32 ModeHighMemory;
UINT16 ModeHighSegment;
} MP_CPU_EXCHANGE_INFO; } MP_CPU_EXCHANGE_INFO;
#pragma pack() #pragma pack()
@ -329,6 +334,23 @@ GetWakeupBuffer (
IN UINTN WakeupBufferSize IN UINTN WakeupBufferSize
); );
/**
Get available EfiBootServicesCode memory below 4GB by specified size.
This buffer is required to safely transfer AP from real address mode to
protected mode or long mode, due to the fact that the buffer returned by
GetWakeupBuffer() may be marked as non-executable.
@param[in] BufferSize Wakeup transition buffer size.
@retval other Return wakeup transition buffer address below 4GB.
@retval 0 Cannot find free memory below 4GB.
**/
UINTN
GetModeTransitionBuffer (
IN UINTN BufferSize
);
/** /**
This function will be called by BSP to wakeup AP. This function will be called by BSP to wakeup AP.

View File

@ -187,6 +187,29 @@ GetWakeupBuffer (
return (UINTN) -1; return (UINTN) -1;
} }
/**
Get available EfiBootServicesCode memory below 4GB by specified size.
This buffer is required to safely transfer AP from real address mode to
protected mode or long mode, due to the fact that the buffer returned by
GetWakeupBuffer() may be marked as non-executable.
@param[in] BufferSize Wakeup transition buffer size.
@retval other Return wakeup transition buffer address below 4GB.
@retval 0 Cannot find free memory below 4GB.
**/
UINTN
GetModeTransitionBuffer (
IN UINTN BufferSize
)
{
//
// PEI phase doesn't need to do such transition. So simply return 0.
//
return 0;
}
/** /**
Checks APs status and updates APs status if needed. Checks APs status and updates APs status if needed.

View File

@ -42,4 +42,7 @@ InitFlagLocation equ LockLocation + 6Ch
CpuInfoLocation equ LockLocation + 74h CpuInfoLocation equ LockLocation + 74h
NumApsExecutingLocation equ LockLocation + 7Ch NumApsExecutingLocation equ LockLocation + 7Ch
InitializeFloatingPointUnitsAddress equ LockLocation + 8Ch InitializeFloatingPointUnitsAddress equ LockLocation + 8Ch
ModeTransitionMemoryLocation equ LockLocation + 94h
ModeTransitionSegmentLocation equ LockLocation + 98h
ModeHighMemoryLocation equ LockLocation + 9Ah
ModeHighSegmentLocation equ LockLocation + 9Eh

View File

@ -52,16 +52,13 @@ BITS 16
mov si, BufferStartLocation mov si, BufferStartLocation
mov ebx, [si] mov ebx, [si]
mov di, ModeOffsetLocation mov si, DataSegmentLocation
mov eax, [di] mov edx, [si]
mov di, CodeSegmentLocation
mov edx, [di] ;
mov di, ax ; Get start address of 32-bit code in low memory (<1MB)
sub di, 02h ;
mov [di],dx ; Patch long mode CS mov edi, ModeTransitionMemoryLocation
sub di, 04h
add eax, ebx
mov [di],eax ; Patch address
mov si, GdtrLocation mov si, GdtrLocation
o32 lgdt [cs:si] o32 lgdt [cs:si]
@ -69,56 +66,79 @@ o32 lgdt [cs:si]
mov si, IdtrLocation mov si, IdtrLocation
o32 lidt [cs:si] o32 lidt [cs:si]
mov si, EnableExecuteDisableLocation ;
cmp byte [si], 0 ; Switch to protected mode
jz SkipEnableExecuteDisableBit ;
mov eax, cr0 ; Get control register 0
or eax, 000000003h ; Set PE bit (bit #0) & MP
mov cr0, eax
; Switch to 32-bit code (>1MB)
o32 jmp far [cs:di]
;
; Following code must be copied to memory with type of EfiBootServicesCode.
; This is required if NX is enabled for EfiBootServicesCode of memory.
;
BITS 32
Flat32Start: ; protected mode entry point
mov ds, dx
mov es, dx
mov fs, dx
mov gs, dx
mov ss, dx
; ;
; Enable execute disable bit ; Enable execute disable bit
; ;
mov esi, EnableExecuteDisableLocation
cmp byte [ebx + esi], 0
jz SkipEnableExecuteDisableBit
mov ecx, 0c0000080h ; EFER MSR number mov ecx, 0c0000080h ; EFER MSR number
rdmsr ; Read EFER rdmsr ; Read EFER
bts eax, 11 ; Enable Execute Disable Bit bts eax, 11 ; Enable Execute Disable Bit
wrmsr ; Write EFER wrmsr ; Write EFER
SkipEnableExecuteDisableBit: SkipEnableExecuteDisableBit:
;
mov di, DataSegmentLocation ; Enable PAE
mov edi, [di] ; Save long mode DS in edi ;
mov si, Cr3Location ; Save CR3 in ecx
mov ecx, [si]
xor ax, ax
mov ds, ax ; Clear data segment
mov eax, cr0 ; Get control register 0
or eax, 000000003h ; Set PE bit (bit #0) & MP
mov cr0, eax
mov eax, cr4 mov eax, cr4
bts eax, 5 bts eax, 5
mov cr4, eax mov cr4, eax
;
; Load page table
;
mov esi, Cr3Location ; Save CR3 in ecx
mov ecx, [ebx + esi]
mov cr3, ecx ; Load CR3 mov cr3, ecx ; Load CR3
;
; Enable long mode
;
mov ecx, 0c0000080h ; EFER MSR number mov ecx, 0c0000080h ; EFER MSR number
rdmsr ; Read EFER rdmsr ; Read EFER
bts eax, 8 ; Set LME=1 bts eax, 8 ; Set LME=1
wrmsr ; Write EFER wrmsr ; Write EFER
;
; Enable paging
;
mov eax, cr0 ; Read CR0 mov eax, cr0 ; Read CR0
bts eax, 31 ; Set PG=1 bts eax, 31 ; Set PG=1
mov cr0, eax ; Write CR0 mov cr0, eax ; Write CR0
jmp 0:strict dword 0 ; far jump to long mode ;
; Far jump to 64-bit code
;
mov edi, ModeHighMemoryLocation
add edi, ebx
jmp far [edi]
BITS 64 BITS 64
LongModeStart: LongModeStart:
mov eax, edi
mov ds, ax
mov es, ax
mov ss, ax
mov esi, ebx mov esi, ebx
lea edi, [esi + InitFlagLocation] lea edi, [esi + InitFlagLocation]
cmp qword [edi], 1 ; ApInitConfig cmp qword [edi], 1 ; ApInitConfig
@ -295,6 +315,7 @@ ASM_PFX(AsmGetAddressMap):
lea rax, [ASM_PFX(AsmRelocateApLoop)] lea rax, [ASM_PFX(AsmRelocateApLoop)]
mov qword [rcx + 18h], rax mov qword [rcx + 18h], rax
mov qword [rcx + 20h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart mov qword [rcx + 20h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
mov qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart
ret ret
;------------------------------------------------------------------------------------- ;-------------------------------------------------------------------------------------