mirror of https://github.com/acidanthera/audk.git
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:
parent
4f10654e04
commit
f32bfe6d06
|
@ -113,6 +113,40 @@ GetWakeupBuffer (
|
|||
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.
|
||||
|
||||
|
|
|
@ -41,4 +41,9 @@ Cr3Location equ LockLocation + 34h
|
|||
InitFlagLocation equ LockLocation + 38h
|
||||
CpuInfoLocation equ LockLocation + 3Ch
|
||||
NumApsExecutingLocation equ LockLocation + 40h
|
||||
InitializeFloatingPointUnitsAddress equ LockLocation + 48h
|
||||
ModeTransitionMemoryLocation equ LockLocation + 4Ch
|
||||
ModeTransitionSegmentLocation equ LockLocation + 50h
|
||||
ModeHighMemoryLocation equ LockLocation + 52h
|
||||
ModeHighSegmentLocation equ LockLocation + 56h
|
||||
|
||||
|
|
|
@ -48,34 +48,35 @@ BITS 16
|
|||
mov si, BufferStartLocation
|
||||
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 edx, [si]
|
||||
|
||||
;
|
||||
; Get start address of 32-bit code in low memory (<1MB)
|
||||
;
|
||||
mov edi, ModeTransitionMemoryLocation
|
||||
|
||||
mov si, GdtrLocation
|
||||
o32 lgdt [cs:si]
|
||||
|
||||
mov si, IdtrLocation
|
||||
o32 lidt [cs:si]
|
||||
|
||||
xor ax, ax
|
||||
mov ds, ax
|
||||
|
||||
;
|
||||
; Switch to protected mode
|
||||
;
|
||||
mov eax, cr0 ; Get control register 0
|
||||
or eax, 000000003h ; Set PE bit (bit #0) & MP
|
||||
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
|
||||
Flat32Start: ; protected mode entry point
|
||||
mov ds, dx
|
||||
|
@ -266,6 +267,7 @@ ASM_PFX(AsmGetAddressMap):
|
|||
mov dword [ebx + 8h], RendezvousFunnelProcEnd - RendezvousFunnelProcStart
|
||||
mov dword [ebx + 0Ch], AsmRelocateApLoopStart
|
||||
mov dword [ebx + 10h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
|
||||
mov dword [ebx + 14h], Flat32Start - RendezvousFunnelProcStart
|
||||
|
||||
popad
|
||||
ret
|
||||
|
|
|
@ -772,6 +772,8 @@ FillExchangeInfoData (
|
|||
)
|
||||
{
|
||||
volatile MP_CPU_EXCHANGE_INFO *ExchangeInfo;
|
||||
UINTN Size;
|
||||
IA32_SEGMENT_DESCRIPTOR *Selector;
|
||||
|
||||
ExchangeInfo = CpuMpData->MpCpuExchangeInfo;
|
||||
ExchangeInfo->Lock = 0;
|
||||
|
@ -801,6 +803,44 @@ FillExchangeInfoData (
|
|||
//
|
||||
AsmReadGdtr ((IA32_DESCRIPTOR *) &ExchangeInfo->GdtrProfile);
|
||||
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->MpCpuExchangeInfo = (MP_CPU_EXCHANGE_INFO *) (UINTN)
|
||||
(CpuMpData->WakeupBuffer + CpuMpData->AddressMap.RendezvousFunnelSize);
|
||||
CpuMpData->MpCpuExchangeInfo->ModeTransitionMemory = (UINT32)
|
||||
GetModeTransitionBuffer (
|
||||
CpuMpData->AddressMap.RendezvousFunnelSize -
|
||||
CpuMpData->AddressMap.ModeTransitionOffset
|
||||
);
|
||||
}
|
||||
BackupAndPrepareWakeupBuffer (CpuMpData);
|
||||
}
|
||||
|
|
|
@ -152,6 +152,7 @@ typedef struct {
|
|||
UINTN RendezvousFunnelSize;
|
||||
UINT8 *RelocateApLoopFuncAddress;
|
||||
UINTN RelocateApLoopFuncSize;
|
||||
UINTN ModeTransitionOffset;
|
||||
} MP_ASSEMBLY_ADDRESS_MAP;
|
||||
|
||||
typedef struct _CPU_MP_DATA CPU_MP_DATA;
|
||||
|
@ -182,6 +183,10 @@ typedef struct {
|
|||
UINTN NumApsExecuting;
|
||||
CPU_MP_DATA *CpuMpData;
|
||||
UINTN InitializeFloatingPointUnitsAddress;
|
||||
UINT32 ModeTransitionMemory;
|
||||
UINT16 ModeTransitionSegment;
|
||||
UINT32 ModeHighMemory;
|
||||
UINT16 ModeHighSegment;
|
||||
} MP_CPU_EXCHANGE_INFO;
|
||||
|
||||
#pragma pack()
|
||||
|
@ -329,6 +334,23 @@ GetWakeupBuffer (
|
|||
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.
|
||||
|
||||
|
|
|
@ -187,6 +187,29 @@ GetWakeupBuffer (
|
|||
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.
|
||||
|
||||
|
|
|
@ -42,4 +42,7 @@ InitFlagLocation equ LockLocation + 6Ch
|
|||
CpuInfoLocation equ LockLocation + 74h
|
||||
NumApsExecutingLocation equ LockLocation + 7Ch
|
||||
InitializeFloatingPointUnitsAddress equ LockLocation + 8Ch
|
||||
|
||||
ModeTransitionMemoryLocation equ LockLocation + 94h
|
||||
ModeTransitionSegmentLocation equ LockLocation + 98h
|
||||
ModeHighMemoryLocation equ LockLocation + 9Ah
|
||||
ModeHighSegmentLocation equ LockLocation + 9Eh
|
||||
|
|
|
@ -52,16 +52,13 @@ BITS 16
|
|||
mov si, BufferStartLocation
|
||||
mov ebx, [si]
|
||||
|
||||
mov di, ModeOffsetLocation
|
||||
mov eax, [di]
|
||||
mov di, CodeSegmentLocation
|
||||
mov edx, [di]
|
||||
mov di, ax
|
||||
sub di, 02h
|
||||
mov [di],dx ; Patch long mode CS
|
||||
sub di, 04h
|
||||
add eax, ebx
|
||||
mov [di],eax ; Patch address
|
||||
mov si, DataSegmentLocation
|
||||
mov edx, [si]
|
||||
|
||||
;
|
||||
; Get start address of 32-bit code in low memory (<1MB)
|
||||
;
|
||||
mov edi, ModeTransitionMemoryLocation
|
||||
|
||||
mov si, GdtrLocation
|
||||
o32 lgdt [cs:si]
|
||||
|
@ -69,56 +66,79 @@ o32 lgdt [cs:si]
|
|||
mov si, IdtrLocation
|
||||
o32 lidt [cs:si]
|
||||
|
||||
mov si, EnableExecuteDisableLocation
|
||||
cmp byte [si], 0
|
||||
jz SkipEnableExecuteDisableBit
|
||||
;
|
||||
; Switch to protected mode
|
||||
;
|
||||
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
|
||||
;
|
||||
mov esi, EnableExecuteDisableLocation
|
||||
cmp byte [ebx + esi], 0
|
||||
jz SkipEnableExecuteDisableBit
|
||||
|
||||
mov ecx, 0c0000080h ; EFER MSR number
|
||||
rdmsr ; Read EFER
|
||||
bts eax, 11 ; Enable Execute Disable Bit
|
||||
wrmsr ; Write EFER
|
||||
|
||||
SkipEnableExecuteDisableBit:
|
||||
|
||||
mov di, DataSegmentLocation
|
||||
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
|
||||
|
||||
;
|
||||
; Enable PAE
|
||||
;
|
||||
mov eax, cr4
|
||||
bts eax, 5
|
||||
mov cr4, eax
|
||||
|
||||
;
|
||||
; Load page table
|
||||
;
|
||||
mov esi, Cr3Location ; Save CR3 in ecx
|
||||
mov ecx, [ebx + esi]
|
||||
mov cr3, ecx ; Load CR3
|
||||
|
||||
;
|
||||
; Enable long mode
|
||||
;
|
||||
mov ecx, 0c0000080h ; EFER MSR number
|
||||
rdmsr ; Read EFER
|
||||
bts eax, 8 ; Set LME=1
|
||||
wrmsr ; Write EFER
|
||||
|
||||
;
|
||||
; Enable paging
|
||||
;
|
||||
mov eax, cr0 ; Read CR0
|
||||
bts eax, 31 ; Set PG=1
|
||||
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
|
||||
LongModeStart:
|
||||
mov eax, edi
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov ss, ax
|
||||
|
||||
mov esi, ebx
|
||||
lea edi, [esi + InitFlagLocation]
|
||||
cmp qword [edi], 1 ; ApInitConfig
|
||||
|
@ -295,6 +315,7 @@ ASM_PFX(AsmGetAddressMap):
|
|||
lea rax, [ASM_PFX(AsmRelocateApLoop)]
|
||||
mov qword [rcx + 18h], rax
|
||||
mov qword [rcx + 20h], AsmRelocateApLoopEnd - AsmRelocateApLoopStart
|
||||
mov qword [rcx + 28h], Flat32Start - RendezvousFunnelProcStart
|
||||
ret
|
||||
|
||||
;-------------------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue