X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=UefiCpuPkg%2FLibrary%2FMpInitLib%2FDxeMpLib.c;h=93fc63bf93e3070d5e3ddffae14158d72aacc74c;hb=dbc22a178546edb6373a4a1f331b0f16f04e2e3d;hp=479f8189f6553f21347f5eef7b3f5823a0ae413b;hpb=a6b3d753f98118ee547ae935b347f4f00fa67e7c;p=mirror_edk2.git diff --git a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c index 479f8189f6..93fc63bf93 100644 --- a/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c +++ b/UefiCpuPkg/Library/MpInitLib/DxeMpLib.c @@ -1,14 +1,8 @@ /** @file MP initialize support functions for DXE phase. - Copyright (c) 2016, Intel Corporation. All rights reserved.
- This program and the accompanying materials - are licensed and made available under the terms and conditions of the BSD License - which accompanies this distribution. The full text of the license may be found at - http://opensource.org/licenses/bsd-license.php - - THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + Copyright (c) 2016 - 2020, Intel Corporation. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent **/ @@ -17,10 +11,13 @@ #include #include #include +#include +#include +#include +#include #include -#define AP_CHECK_INTERVAL (EFI_TIMER_PERIOD_MILLISECONDS (100)) #define AP_SAFE_STACK_SIZE 128 CPU_MP_DATA *mCpuMpData = NULL; @@ -32,6 +29,11 @@ VOID *mReservedApLoopFunc = NULL; UINTN mReservedTopOfApStack; volatile UINT32 mNumberToFinish = 0; +// +// Begin wakeup buffer allocation below 0x88000 +// +STATIC EFI_PHYSICAL_ADDRESS mSevEsDxeWakeupBuffer = 0x88000; + /** Enable Debug Agent to support source debugging on AP function. @@ -75,7 +77,7 @@ SaveCpuMpData ( } /** - Get available system memory below 1MB by specified size. + Get available system memory below 0x88000 by specified size. @param[in] WakeupBufferSize Wakeup buffer size required @@ -89,26 +91,129 @@ GetWakeupBuffer ( { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS StartAddress; + EFI_MEMORY_TYPE MemoryType; + + if (PcdGetBool (PcdSevEsIsEnabled)) { + MemoryType = EfiReservedMemoryType; + } else { + MemoryType = EfiBootServicesData; + } - StartAddress = BASE_1MB; + // + // Try to allocate buffer below 1M for waking vector. + // LegacyBios driver only reports warning when page allocation in range + // [0x60000, 0x88000) fails. + // This library is consumed by CpuDxe driver to produce CPU Arch protocol. + // LagacyBios driver depends on CPU Arch protocol which guarantees below + // allocation runs earlier than LegacyBios driver. + // + if (PcdGetBool (PcdSevEsIsEnabled)) { + // + // SEV-ES Wakeup buffer should be under 0x88000 and under any previous one + // + StartAddress = mSevEsDxeWakeupBuffer; + } else { + StartAddress = 0x88000; + } Status = gBS->AllocatePages ( AllocateMaxAddress, - EfiBootServicesData, + MemoryType, EFI_SIZE_TO_PAGES (WakeupBufferSize), &StartAddress ); ASSERT_EFI_ERROR (Status); - if (!EFI_ERROR (Status)) { - Status = gBS->FreePages( - StartAddress, - EFI_SIZE_TO_PAGES (WakeupBufferSize) - ); - ASSERT_EFI_ERROR (Status); - DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n", - (UINTN) StartAddress, WakeupBufferSize)); - } else { + if (EFI_ERROR (Status)) { StartAddress = (EFI_PHYSICAL_ADDRESS) -1; + } else if (PcdGetBool (PcdSevEsIsEnabled)) { + // + // Next SEV-ES wakeup buffer allocation must be below this allocation + // + mSevEsDxeWakeupBuffer = StartAddress; + } + + DEBUG ((DEBUG_INFO, "WakeupBufferStart = %x, WakeupBufferSize = %x\n", + (UINTN) StartAddress, WakeupBufferSize)); + + 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; +} + +/** + Return the address of the SEV-ES AP jump table. + + This buffer is required in order for an SEV-ES guest to transition from + UEFI into an OS. + + @return Return SEV-ES AP jump table buffer +**/ +UINTN +GetSevEsAPMemory ( + VOID + ) +{ + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS StartAddress; + MSR_SEV_ES_GHCB_REGISTER Msr; + GHCB *Ghcb; + BOOLEAN InterruptState; + + // + // Allocate 1 page for AP jump table page + // + StartAddress = BASE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + 1, + &StartAddress + ); + ASSERT_EFI_ERROR (Status); + + DEBUG ((DEBUG_INFO, "Dxe: SevEsAPMemory = %lx\n", (UINTN) StartAddress)); + + // + // Save the SevEsAPMemory as the AP jump table. + // + Msr.GhcbPhysicalAddress = AsmReadMsr64 (MSR_SEV_ES_GHCB); + Ghcb = Msr.Ghcb; + + VmgInit (Ghcb, &InterruptState); + VmgExit (Ghcb, SVM_EXIT_AP_JUMP_TABLE, 0, (UINT64) (UINTN) StartAddress); + VmgDone (Ghcb, InterruptState); + return (UINTN) StartAddress; } @@ -186,6 +291,38 @@ CheckApsStatus ( } } +/** + Get Protected mode code segment with 16-bit default addressing + from current GDT table. + + @return Protected mode 16-bit code segment value. +**/ +UINT16 +GetProtectedMode16CS ( + VOID + ) +{ + IA32_DESCRIPTOR GdtrDesc; + IA32_SEGMENT_DESCRIPTOR *GdtEntry; + UINTN GdtEntryCount; + UINT16 Index; + + Index = (UINT16) -1; + AsmReadGdtr (&GdtrDesc); + GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR); + GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; + for (Index = 0; Index < GdtEntryCount; Index++) { + if (GdtEntry->Bits.L == 0) { + if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 0) { + break; + } + } + GdtEntry++; + } + ASSERT (Index != GdtEntryCount); + return Index * 8; +} + /** Get Protected mode code segment from current GDT table. @@ -201,19 +338,18 @@ GetProtectedModeCS ( UINTN GdtEntryCount; UINT16 Index; - Index = (UINT16) -1; AsmReadGdtr (&GdtrDesc); GdtEntryCount = (GdtrDesc.Limit + 1) / sizeof (IA32_SEGMENT_DESCRIPTOR); GdtEntry = (IA32_SEGMENT_DESCRIPTOR *) GdtrDesc.Base; for (Index = 0; Index < GdtEntryCount; Index++) { if (GdtEntry->Bits.L == 0) { - if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.L == 0) { + if (GdtEntry->Bits.Type > 8 && GdtEntry->Bits.DB == 1) { break; } } GdtEntry++; } - ASSERT (Index != -1); + ASSERT (Index != GdtEntryCount); return Index * 8; } @@ -232,17 +368,26 @@ RelocateApLoop ( BOOLEAN MwaitSupport; ASM_RELOCATE_AP_LOOP AsmRelocateApLoopFunc; UINTN ProcessorNumber; + UINTN StackStart; - MpInitLibWhoAmI (&ProcessorNumber); + MpInitLibWhoAmI (&ProcessorNumber); CpuMpData = GetCpuMpData (); MwaitSupport = IsMwaitSupport (); + if (CpuMpData->SevEsIsEnabled) { + StackStart = CpuMpData->SevEsAPResetStackStart; + } else { + StackStart = mReservedTopOfApStack; + } AsmRelocateApLoopFunc = (ASM_RELOCATE_AP_LOOP) (UINTN) mReservedApLoopFunc; AsmRelocateApLoopFunc ( MwaitSupport, CpuMpData->ApTargetCState, CpuMpData->PmCodeSegment, - mReservedTopOfApStack - ProcessorNumber * AP_SAFE_STACK_SIZE, - (UINTN) &mNumberToFinish + StackStart - ProcessorNumber * AP_SAFE_STACK_SIZE, + (UINTN) &mNumberToFinish, + CpuMpData->Pm16CodeSegment, + CpuMpData->SevEsAPBuffer, + CpuMpData->WakeupBuffer ); // // It should never reach here @@ -269,12 +414,28 @@ MpInitChangeApLoopCallback ( CpuMpData = GetCpuMpData (); CpuMpData->PmCodeSegment = GetProtectedModeCS (); + CpuMpData->Pm16CodeSegment = GetProtectedMode16CS (); CpuMpData->ApLoopMode = PcdGet8 (PcdCpuApLoopMode); mNumberToFinish = CpuMpData->CpuCount - 1; - WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL); + WakeUpAP (CpuMpData, TRUE, 0, RelocateApLoop, NULL, TRUE); while (mNumberToFinish > 0) { CpuPause (); } + + if (CpuMpData->SevEsIsEnabled && (CpuMpData->WakeupBuffer != (UINTN) -1)) { + // + // There are APs present. Re-use reserved memory area below 1MB from + // WakeupBuffer as the area to be used for transitioning to 16-bit mode + // in support of booting of the AP by an OS. + // + CopyMem ( + (VOID *) CpuMpData->WakeupBuffer, + (VOID *) (CpuMpData->AddressMap.RendezvousFunnelAddress + + CpuMpData->AddressMap.SwitchToRealPM16ModeOffset), + CpuMpData->AddressMap.SwitchToRealPM16ModeSize + ); + } + DEBUG ((DEBUG_INFO, "%a() done!\n", __FUNCTION__)); } @@ -288,9 +449,13 @@ InitMpGlobalData ( IN CPU_MP_DATA *CpuMpData ) { - EFI_STATUS Status; - EFI_PHYSICAL_ADDRESS Address; - UINTN ApSafeBufferSize; + EFI_STATUS Status; + EFI_PHYSICAL_ADDRESS Address; + UINTN ApSafeBufferSize; + UINTN Index; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc; + UINTN StackBase; + CPU_INFO_IN_HOB *CpuInfoInHob; SaveCpuMpData (CpuMpData); @@ -301,6 +466,46 @@ InitMpGlobalData ( return; } + if (PcdGetBool (PcdCpuStackGuard)) { + // + // One extra page at the bottom of the stack is needed for Guard page. + // + if (CpuMpData->CpuApStackSize <= EFI_PAGE_SIZE) { + DEBUG ((DEBUG_ERROR, "PcdCpuApStackSize is not big enough for Stack Guard!\n")); + ASSERT (FALSE); + } + + // + // DXE will reuse stack allocated for APs at PEI phase if it's available. + // Let's check it here. + // + // Note: BSP's stack guard is set at DxeIpl phase. But for the sake of + // BSP/AP exchange, stack guard for ApTopOfStack of cpu 0 will still be + // set here. + // + CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)CpuMpData->CpuInfoInHob; + for (Index = 0; Index < CpuMpData->CpuCount; ++Index) { + if (CpuInfoInHob != NULL && CpuInfoInHob[Index].ApTopOfStack != 0) { + StackBase = (UINTN)CpuInfoInHob[Index].ApTopOfStack - CpuMpData->CpuApStackSize; + } else { + StackBase = CpuMpData->Buffer + Index * CpuMpData->CpuApStackSize; + } + + Status = gDS->GetMemorySpaceDescriptor (StackBase, &MemDesc); + ASSERT_EFI_ERROR (Status); + + Status = gDS->SetMemorySpaceAttributes ( + StackBase, + EFI_PAGES_TO_SIZE (1), + MemDesc.Attributes | EFI_MEMORY_RP + ); + ASSERT_EFI_ERROR (Status); + + DEBUG ((DEBUG_INFO, "Stack Guard set at %lx [cpu%lu]!\n", + (UINT64)StackBase, (UINT64)Index)); + } + } + // // Avoid APs access invalid buffer data which allocated by BootServices, // so we will allocate reserved data for AP loop code. We also need to @@ -309,9 +514,9 @@ InitMpGlobalData ( // Allocating it in advance since memory services are not available in // Exit Boot Services callback function. // - ApSafeBufferSize = CpuMpData->AddressMap.RelocateApLoopFuncSize; - ApSafeBufferSize += CpuMpData->CpuCount * AP_SAFE_STACK_SIZE; - + ApSafeBufferSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES ( + CpuMpData->AddressMap.RelocateApLoopFuncSize + )); Address = BASE_4GB - 1; Status = gBS->AllocatePages ( AllocateMaxAddress, @@ -320,9 +525,39 @@ InitMpGlobalData ( &Address ); ASSERT_EFI_ERROR (Status); + mReservedApLoopFunc = (VOID *) (UINTN) Address; ASSERT (mReservedApLoopFunc != NULL); - mReservedTopOfApStack = (UINTN) Address + EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (ApSafeBufferSize)); + + // + // Make sure that the buffer memory is executable if NX protection is enabled + // for EfiReservedMemoryType. + // + // TODO: Check EFI_MEMORY_XP bit set or not once it's available in DXE GCD + // service. + // + Status = gDS->GetMemorySpaceDescriptor (Address, &MemDesc); + if (!EFI_ERROR (Status)) { + gDS->SetMemorySpaceAttributes ( + Address, + ApSafeBufferSize, + MemDesc.Attributes & (~EFI_MEMORY_XP) + ); + } + + ApSafeBufferSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES ( + CpuMpData->CpuCount * AP_SAFE_STACK_SIZE + )); + Address = BASE_4GB - 1; + Status = gBS->AllocatePages ( + AllocateMaxAddress, + EfiReservedMemoryType, + EFI_SIZE_TO_PAGES (ApSafeBufferSize), + &Address + ); + ASSERT_EFI_ERROR (Status); + + mReservedTopOfApStack = (UINTN) Address + ApSafeBufferSize; ASSERT ((mReservedTopOfApStack & (UINTN)(CPU_STACK_ALIGNMENT - 1)) == 0); CopyMem ( mReservedApLoopFunc, @@ -345,7 +580,9 @@ InitMpGlobalData ( Status = gBS->SetTimer ( mCheckAllApsEvent, TimerPeriodic, - AP_CHECK_INTERVAL + EFI_TIMER_PERIOD_MICROSECONDS ( + PcdGet32 (PcdCpuApStatusCheckIntervalInMicroSeconds) + ) ); ASSERT_EFI_ERROR (Status); @@ -462,9 +699,10 @@ MpInitLibStartupAllAPs ( // mStopCheckAllApsStatus = TRUE; - Status = StartupAllAPsWorker ( + Status = StartupAllCPUsWorker ( Procedure, SingleThread, + TRUE, WaitEvent, TimeoutInMicroseconds, ProcedureArgument, @@ -709,3 +947,27 @@ MpInitLibEnableDisableAP ( return Status; } + +/** + This funtion will try to invoke platform specific microcode shadow logic to + relocate microcode update patches into memory. + + @param[in, out] CpuMpData The pointer to CPU MP Data structure. + + @retval EFI_SUCCESS Shadow microcode success. + @retval EFI_OUT_OF_RESOURCES No enough resource to complete the operation. + @retval EFI_UNSUPPORTED Can't find platform specific microcode shadow + PPI/Protocol. +**/ +EFI_STATUS +PlatformShadowMicrocode ( + IN OUT CPU_MP_DATA *CpuMpData + ) +{ + // + // There is no DXE version of platform shadow microcode protocol so far. + // A platform which only uses DxeMpInitLib instance could only supports + // the PCD based microcode shadowing. + // + return EFI_UNSUPPORTED; +}