/** @file SMM CPU misc functions for Ia32 arch specific. Copyright (c) 2015 - 2019, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "PiSmmCpuDxeSmm.h" extern UINT64 gTaskGateDescriptor; EFI_PHYSICAL_ADDRESS mGdtBuffer; UINTN mGdtBufferSize; extern BOOLEAN mCetSupported; extern UINTN mSmmShadowStackSize; X86_ASSEMBLY_PATCH_LABEL mPatchCetPl0Ssp; X86_ASSEMBLY_PATCH_LABEL mPatchCetInterruptSsp; UINT32 mCetPl0Ssp; UINT32 mCetInterruptSsp; /** Initialize IDT for SMM Stack Guard. **/ VOID EFIAPI InitializeIDTSmmStackGuard ( VOID ) { IA32_IDT_GATE_DESCRIPTOR *IdtGate; // // If SMM Stack Guard feature is enabled, the Page Fault Exception entry in IDT // is a Task Gate Descriptor so that when a Page Fault Exception occurs, // the processors can use a known good stack in case stack is ran out. // IdtGate = (IA32_IDT_GATE_DESCRIPTOR *)gcSmiIdtr.Base; IdtGate += EXCEPT_IA32_PAGE_FAULT; IdtGate->Uint64 = gTaskGateDescriptor; } /** Initialize Gdt for all processors. @param[in] Cr3 CR3 value. @param[out] GdtStepSize The step size for GDT table. @return GdtBase for processor 0. GdtBase for processor X is: GdtBase + (GdtStepSize * X) **/ VOID * InitGdt ( IN UINTN Cr3, OUT UINTN *GdtStepSize ) { UINTN Index; IA32_SEGMENT_DESCRIPTOR *GdtDescriptor; UINTN TssBase; UINTN GdtTssTableSize; UINT8 *GdtTssTables; UINTN GdtTableStepSize; UINTN InterruptShadowStack; if (FeaturePcdGet (PcdCpuSmmStackGuard)) { // // For IA32 SMM, if SMM Stack Guard feature is enabled, we use 2 TSS. // in this case, we allocate separate GDT/TSS for each CPUs to avoid TSS load contention // on each SMI entry. // // // Enlarge GDT to contain 2 TSS descriptors // gcSmiGdtr.Limit += (UINT16)(2 * sizeof (IA32_SEGMENT_DESCRIPTOR)); GdtTssTableSize = (gcSmiGdtr.Limit + 1 + TSS_SIZE + EXCEPTION_TSS_SIZE + 7) & ~7; // 8 bytes aligned mGdtBufferSize = GdtTssTableSize * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; // // IA32 Stack Guard need use task switch to switch stack that need // write GDT and TSS, so AllocateCodePages() could not be used here // as code pages will be set to RO. // GdtTssTables = (UINT8*)AllocatePages (EFI_SIZE_TO_PAGES (mGdtBufferSize)); ASSERT (GdtTssTables != NULL); mGdtBuffer = (UINTN)GdtTssTables; GdtTableStepSize = GdtTssTableSize; for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) { CopyMem (GdtTssTables + GdtTableStepSize * Index, (VOID*)(UINTN)gcSmiGdtr.Base, gcSmiGdtr.Limit + 1 + TSS_SIZE + EXCEPTION_TSS_SIZE); // // Fixup TSS descriptors // TssBase = (UINTN)(GdtTssTables + GdtTableStepSize * Index + gcSmiGdtr.Limit + 1); GdtDescriptor = (IA32_SEGMENT_DESCRIPTOR *)(TssBase) - 2; GdtDescriptor->Bits.BaseLow = (UINT16)TssBase; GdtDescriptor->Bits.BaseMid = (UINT8)(TssBase >> 16); GdtDescriptor->Bits.BaseHigh = (UINT8)(TssBase >> 24); TssBase += TSS_SIZE; GdtDescriptor++; GdtDescriptor->Bits.BaseLow = (UINT16)TssBase; GdtDescriptor->Bits.BaseMid = (UINT8)(TssBase >> 16); GdtDescriptor->Bits.BaseHigh = (UINT8)(TssBase >> 24); // // Fixup TSS segments // // ESP as known good stack // *(UINTN *)(TssBase + TSS_IA32_ESP_OFFSET) = mSmmStackArrayBase + EFI_PAGE_SIZE + Index * mSmmStackSize; *(UINT32 *)(TssBase + TSS_IA32_CR3_OFFSET) = Cr3; // // Setup ShadowStack for stack switch // if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { InterruptShadowStack = (UINTN)(mSmmStackArrayBase + mSmmStackSize + EFI_PAGES_TO_SIZE (1) - sizeof(UINT64) + (mSmmStackSize + mSmmShadowStackSize) * Index); *(UINT32 *)(TssBase + TSS_IA32_SSP_OFFSET) = (UINT32)InterruptShadowStack; } } } else { // // Just use original table, AllocatePage and copy them here to make sure GDTs are covered in page memory. // GdtTssTableSize = gcSmiGdtr.Limit + 1; mGdtBufferSize = GdtTssTableSize * gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; GdtTssTables = (UINT8*)AllocateCodePages (EFI_SIZE_TO_PAGES (mGdtBufferSize)); ASSERT (GdtTssTables != NULL); mGdtBuffer = (UINTN)GdtTssTables; GdtTableStepSize = GdtTssTableSize; for (Index = 0; Index < gSmmCpuPrivate->SmmCoreEntryContext.NumberOfCpus; Index++) { CopyMem (GdtTssTables + GdtTableStepSize * Index, (VOID*)(UINTN)gcSmiGdtr.Base, gcSmiGdtr.Limit + 1); } } *GdtStepSize = GdtTableStepSize; return GdtTssTables; } /** Transfer AP to safe hlt-loop after it finished restore CPU features on S3 patch. @param[in] ApHltLoopCode The address of the safe hlt-loop function. @param[in] TopOfStack A pointer to the new stack to use for the ApHltLoopCode. @param[in] NumberToFinishAddress Address of Semaphore of APs finish count. **/ VOID TransferApToSafeState ( IN UINTN ApHltLoopCode, IN UINTN TopOfStack, IN UINTN NumberToFinishAddress ) { SwitchStack ( (SWITCH_STACK_ENTRY_POINT)ApHltLoopCode, (VOID *)NumberToFinishAddress, NULL, (VOID *)TopOfStack ); // // It should never reach here // ASSERT (FALSE); } /** Initialize the shadow stack related data structure. @param CpuIndex The index of CPU. @param ShadowStack The bottom of the shadow stack for this CPU. **/ VOID InitShadowStack ( IN UINTN CpuIndex, IN VOID *ShadowStack ) { UINTN SmmShadowStackSize; if ((PcdGet32 (PcdControlFlowEnforcementPropertyMask) != 0) && mCetSupported) { SmmShadowStackSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmShadowStackSize))); if (FeaturePcdGet (PcdCpuSmmStackGuard)) { SmmShadowStackSize += EFI_PAGES_TO_SIZE (2); } mCetPl0Ssp = (UINT32)((UINTN)ShadowStack + SmmShadowStackSize - sizeof(UINT64)); PatchInstructionX86 (mPatchCetPl0Ssp, mCetPl0Ssp, 4); DEBUG ((DEBUG_INFO, "mCetPl0Ssp - 0x%x\n", mCetPl0Ssp)); DEBUG ((DEBUG_INFO, "ShadowStack - 0x%x\n", ShadowStack)); DEBUG ((DEBUG_INFO, " SmmShadowStackSize - 0x%x\n", SmmShadowStackSize)); if (FeaturePcdGet (PcdCpuSmmStackGuard)) { mCetInterruptSsp = (UINT32)((UINTN)ShadowStack + EFI_PAGES_TO_SIZE(1) - sizeof(UINT64)); PatchInstructionX86 (mPatchCetInterruptSsp, mCetInterruptSsp, 4); DEBUG ((DEBUG_INFO, "mCetInterruptSsp - 0x%x\n", mCetInterruptSsp)); } } }