/** 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; // // For X64 SMM, we allocate separate GDT/TSS for each CPUs to avoid TSS load contention // on each SMI entry. // GdtTssTableSize = (gcSmiGdtr.Limit + 1 + TSS_SIZE + 7) & ~7; // 8 bytes aligned 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 + TSS_SIZE); // // Fixup TSS descriptors // TssBase = (UINTN)(GdtTssTables + GdtTableStepSize * Index + gcSmiGdtr.Limit + 1); GdtDescriptor = (IA32_SEGMENT_DESCRIPTOR *)(TssBase) - 2; GdtDescriptor->Bits.BaseLow = (UINT16)(UINTN)TssBase; GdtDescriptor->Bits.BaseMid = (UINT8)((UINTN)TssBase >> 16); GdtDescriptor->Bits.BaseHigh = (UINT8)((UINTN)TssBase >> 24); if (FeaturePcdGet (PcdCpuSmmStackGuard)) { // // Setup top of known good stack as IST1 for each processor. // *(UINTN *)(TssBase + TSS_X64_IST1_OFFSET) = (mSmmStackArrayBase + EFI_PAGE_SIZE + Index * mSmmStackSize); } } *GdtStepSize = GdtTableStepSize; return GdtTssTables; }
/** Initialize IDT to setup exception handlers for SMM. **/ VOID InitializeSmmIdt ( VOID ) { EFI_STATUS Status; BOOLEAN InterruptState; IA32_DESCRIPTOR DxeIdtr; // // There are 32 (not 255) entries in it since only processor // generated exceptions will be handled. // gcSmiIdtr.Limit = (sizeof(IA32_IDT_GATE_DESCRIPTOR) * 32) - 1; // // Allocate page aligned IDT, because it might be set as read only. // gcSmiIdtr.Base = (UINTN)AllocateCodePages (EFI_SIZE_TO_PAGES(gcSmiIdtr.Limit + 1)); ASSERT (gcSmiIdtr.Base != 0); ZeroMem ((VOID *)gcSmiIdtr.Base, gcSmiIdtr.Limit + 1); // // Disable Interrupt and save DXE IDT table // InterruptState = SaveAndDisableInterrupts (); AsmReadIdtr (&DxeIdtr); // // Load SMM temporary IDT table // AsmWriteIdtr (&gcSmiIdtr); // // Setup SMM default exception handlers, SMM IDT table // will be updated and saved in gcSmiIdtr // Status = InitializeCpuExceptionHandlers (NULL); ASSERT_EFI_ERROR (Status); // // Restore DXE IDT table and CPU interrupt // AsmWriteIdtr ((IA32_DESCRIPTOR *) &DxeIdtr); SetInterruptState (InterruptState); }
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; } } 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.