/** @file SMM Policy Shim (SPS) support functions @copyright INTEL CONFIDENTIAL Copyright 2018 - 2021 Intel Corporation. The source code contained or described herein and all documents related to the source code ("Material") are owned by Intel Corporation or its suppliers or licensors. Title to the Material remains with Intel Corporation or its suppliers and licensors. The Material may contain trade secrets and proprietary and confidential information of Intel Corporation and its suppliers and licensors, and is protected by worldwide copyright and trade secret laws and treaty provisions. No part of the Material may be used, copied, reproduced, modified, published, uploaded, posted, transmitted, distributed, or disclosed in any way without Intel's prior express written permission. No license under any patent, copyright, trade secret or other intellectual property right is granted to or conferred upon you by disclosure or delivery of the Materials, either expressly, by implication, inducement, estoppel or otherwise. Any license under such intellectual property rights must be express and approved by Intel in writing. Unless otherwise agreed by Intel in writing, you may not remove or alter this notice or any other notice embedded in Materials by Intel or Intel's suppliers or licensors in any way. This file contains an 'Intel Peripheral Driver' and is uniquely identified as "Intel Reference Module" and is licensed for Intel CPUs and chipsets under the terms of your license agreement with Intel or your vendor. This file may be modified by the user, subject to additional terms of the license agreement. @par Specification Reference: **/ #include "SmmCpuFeatures.h" #include "SmmResourceConfig.h" #include "SmmSpa.h" #include #include #include #include /** Setup SMM Protected Mode context for processor. @param ProcessorNumber The processor number. @param SmBase The SMBASE value of the processor. @param StackAddress Stack address of the processor. @param GdtBase Gdt table base address of the processor. @param GdtSize Gdt table size of the processor. @param CodeSegment Code segment value. @param ProtModeIdt Pointer to protected-mode IDT descriptor. **/ VOID SetupSmmProtectedModeContext( IN UINTN ProcessorNumber, IN UINT32 SmBase, IN UINT32 StackAddress, IN UINTN GdtBase, IN UINTN GdtSize, IN UINT16 CodeSegment, IN IA32_DESCRIPTOR *ProtModeIdt ); extern VOID SetPageTableAttributes ( VOID ); extern VOID PpamSmmConfigurationTableInit ( VOID ); extern IA32_PROT_DESCRIPTOR gGdtDesc; extern IA32_DESCRIPTOR gSmmFeatureSmiHandlerIdtr; extern SPA_CTXT *gSpaCtxt; extern EFI_HANDLE gSpaSmmReadyToLockRegistration; extern volatile UINT32 gSmmFeatureSmiStack; extern UINT32 gSmmStackSize; extern UINT32 gSmmFeatureSmiCr3; extern UINT32 gProtModeIdtr; extern UINT32 gPMStackDesc[2]; extern BOOLEAN mSmmProtectedModeEnable; extern BOOLEAN mSmmSpsStateSaveEnable; BOOLEAN gSmmFeatureRing3Supported; BOOLEAN gSmmSupervisorStateSave = 0; volatile UINT32 gSmmFeatureSmiUserStack; UINT32 gSmmUserStackSize; UINTN gSmmCpuStackArrayBase; UINTN gSmmCpuStackSize; UINT32 gSmmExceptionStack; UINT32 gSmmExceptionStackSize; UINT32 gSmmUserExceptionStack; UINT32 gSmmUserExceptionStackSize; UINT64 gSmmUserExceptionEntry; UINT64 gMsrBitMapBase; UINT64 gSmmXcr0Data = 3; UINT64 gSmmXssData = 0; UINT64 gSmmSmiRendezvous; UINT8 *gExceptionStack; UINTN gExceptionStackSize; extern UINT32 *mSmBase; extern UINTN mNumberOfCpus; SPIN_LOCK *mInternalDebugLock; BOOLEAN gGdtFlag = FALSE; EFI_PHYSICAL_ADDRESS gGdtBuffer; UINTN gGdtBufferSize; UINTN gGdtTssTableSize; BOOLEAN gSpaEnable = FALSE; EFI_PHYSICAL_ADDRESS gTxtDprMemoryBase = 0; UINT64 gTxtDprMemorySize = 0; EDKII_SMM_MEMORY_ATTRIBUTE_PROTOCOL *gSmmMemoryAttribute = NULL; SPS_RING3_EXCEPTION_HANDLER *gSpsRing3ExceptionHandlerProtocol = NULL; // // Global copy of the PcdPteMemoryEncryptionAddressOrMask // extern UINT64 mAddressEncMask; extern SMM_ENTRY_POINT_HEADER *mSmmEntryPointHeader; extern SMM_ENTRY_POINT_INFORMATION_TABLE *mSmmEntryPointInfoTable; extern BOOLEAN mPpamConfigurationTableInitialized; VOID EFIAPI AsmSmiRendezvous ( VOID ); VOID EFIAPI AsmOemExceptionHandler ( IN CONST EFI_EXCEPTION_TYPE InterruptType, IN CONST EFI_SYSTEM_CONTEXT SystemContext ); VOID *gSpsBin; UINTN gSpsBinSize; SPS_KERNEL_CONTEXT *mSpsKernelContext; /** Allocate Code page for PE code @param[in] ImageAddress Points to addres of Image data @param[in] ImageSize Size of Image data @param[in] ImageAddressTemp Points to addres of Image data extracted from FV. **/ VOID ConvertCodePage ( IN VOID *ImageAddress, IN UINTN ImageSize, IN VOID *ImageAddressTemp ) { EFI_IMAGE_DOS_HEADER *DosHdr; UINT32 PeCoffHeaderOffset; UINT32 SectionAlignment; EFI_IMAGE_SECTION_HEADER *Section; EFI_IMAGE_OPTIONAL_HEADER_PTR_UNION Hdr; UINT8 *Name; UINTN Index; UINT16 Magic; EFI_STATUS Status; EFI_PHYSICAL_ADDRESS CodeMemory; UINTN CodePages; EFI_PHYSICAL_ADDRESS CodeMemoryTemp; DosHdr = (EFI_IMAGE_DOS_HEADER *) (UINTN) ImageAddress; PeCoffHeaderOffset = 0; if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE) { PeCoffHeaderOffset = DosHdr->e_lfanew; } Hdr.Pe32 = (EFI_IMAGE_NT_HEADERS32 *)((UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset); if (Hdr.Pe32->Signature != EFI_IMAGE_NT_SIGNATURE) { DEBUG ((DEBUG_ERROR, "SPS Hdr.Pe32->Signature invalid - 0x%x\n", Hdr.Pe32->Signature)); ASSERT(FALSE); return; } // // Get SectionAlignment // if (Hdr.Pe32->FileHeader.Machine == IMAGE_FILE_MACHINE_IA64 && Hdr.Pe32->OptionalHeader.Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { // // NOTE: Some versions of Linux ELILO for Itanium have an incorrect magic value // in the PE/COFF Header. If the MachineType is Itanium(IA64) and the // Magic value in the OptionalHeader is EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC // then override the magic value to EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC // Magic = EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC; } else { // // Get the magic value from the PE/COFF Optional Header // Magic = Hdr.Pe32->OptionalHeader.Magic; } if (Magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC) { SectionAlignment = Hdr.Pe32->OptionalHeader.SectionAlignment; } else { SectionAlignment = Hdr.Pe32Plus->OptionalHeader.SectionAlignment; } if ((SectionAlignment & (SIZE_4KB - 1)) != 0) { DEBUG ((DEBUG_ERROR, "SPS Section Alignment(0x%x) is not 4K\n", SectionAlignment)); ASSERT(FALSE); return; } Section = (EFI_IMAGE_SECTION_HEADER *) ( (UINT8 *) (UINTN) ImageAddress + PeCoffHeaderOffset + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER) + Hdr.Pe32->FileHeader.SizeOfOptionalHeader ); for (Index = 0; Index < Hdr.Pe32->FileHeader.NumberOfSections; Index++) { Name = Section[Index].Name; DEBUG (( DEBUG_VERBOSE, "SMM Section - '%c%c%c%c%c%c%c%c'\n", Name[0], Name[1], Name[2], Name[3], Name[4], Name[5], Name[6], Name[7] )); if ((Section[Index].Characteristics & EFI_IMAGE_SCN_CNT_CODE) != 0) { DEBUG ((DEBUG_VERBOSE, "SMM VirtualSize - 0x%08x\n", Section[Index].Misc.VirtualSize)); DEBUG ((DEBUG_VERBOSE, "SMM VirtualAddress - 0x%08x\n", Section[Index].VirtualAddress)); DEBUG ((DEBUG_VERBOSE, "SMM SizeOfRawData - 0x%08x\n", Section[Index].SizeOfRawData)); DEBUG ((DEBUG_VERBOSE, "SMM PointerToRawData - 0x%08x\n", Section[Index].PointerToRawData)); DEBUG ((DEBUG_VERBOSE, "SMM PointerToRelocations - 0x%08x\n", Section[Index].PointerToRelocations)); DEBUG ((DEBUG_VERBOSE, "SMM PointerToLinenumbers - 0x%08x\n", Section[Index].PointerToLinenumbers)); DEBUG ((DEBUG_VERBOSE, "SMM NumberOfRelocations - 0x%08x\n", Section[Index].NumberOfRelocations)); DEBUG ((DEBUG_VERBOSE, "SMM NumberOfLinenumbers - 0x%08x\n", Section[Index].NumberOfLinenumbers)); DEBUG ((DEBUG_VERBOSE, "SMM Characteristics - 0x%08x\n", Section[Index].Characteristics)); CodeMemory = (UINTN) ImageAddress + Section[Index].VirtualAddress; CodePages = EFI_SIZE_TO_PAGES(Section[Index].SizeOfRawData); DEBUG ((DEBUG_INFO, "SPS Code: 0x%016lx - 0x%016lx\n", CodeMemory, EFI_PAGES_TO_SIZE(CodePages))); CodeMemoryTemp = (UINTN) ImageAddressTemp + Section[Index].VirtualAddress; // // Allocate Code page for PE code. // Status = gSmst->SmmFreePages (CodeMemory, CodePages); ASSERT_EFI_ERROR (Status); Status = gSmst->SmmAllocatePages (AllocateAddress, EfiRuntimeServicesCode, CodePages, &CodeMemory); ASSERT_EFI_ERROR (Status); // // SmmFreePages corrupts first 24 bytes in CodeMemory. // To recover it, copying back from the parent buffer as a WA. // CopyMem ((VOID *) CodeMemory, (VOID *) CodeMemoryTemp, 24); } } } /** Allocates buffer for Exception stack and updates supervisor exception stack address in TSS @param[in] CpuIndex The processor index. @param[in] TssBase Base address of Tast State Segment. **/ VOID InitRing3ModeGdt ( IN UINTN CpuIndex, IN VOID *TssBase ) { if (gExceptionStack == NULL) { gExceptionStackSize = PcdGet32 (PcdCpuSmmStackSize); gExceptionStack = AllocatePages (EFI_SIZE_TO_PAGES(gExceptionStackSize * mNumberOfCpus)); ASSERT (gExceptionStack != NULL); } DEBUG ((DEBUG_INFO, "ExceptionStack(%d) - Ring0: 0x%x, Ring3: 0x%x\n", CpuIndex, (UINTN)gExceptionStack + gExceptionStackSize * (CpuIndex + 1), (UINTN)gExceptionStack + gExceptionStackSize * (CpuIndex + 1) - EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM) )); *(UINT64 *) ((UINTN) TssBase + TSS_X64_RSP0_OFFSET) = (UINTN) gExceptionStack + gExceptionStackSize * (CpuIndex + 1); } /** Initialize Gdt for all processors. +-----------------------------+-----------------------------+--------+-----------------------------------------+ | GDT0 | TSS0 | IoMap0 | 0xFF | GDT1 | TSS1 | IoMap1 | 0xFF | ...... | GDT(n-1) | TSS(n-1) | IoMap(n-1) | 0xFF | +-----------------------------+-----------------------------+--------+-----------------------------------------+ **/ VOID InitGdtDgr ( VOID ) { UINTN Index; IA32_SEGMENT_DESCRIPTOR *GdtDescriptor; UINTN TssBase; UINT8 *GdtTssTables; UINTN GdtTableStepSize; // // For X64 SMM, we allocate separate GDT/TSS for each CPUs to avoid TSS load contention // on each SMI entry. // gGdtTssTableSize = (mSmiGdtr.Limit + 1 + FULL_TSS_SIZE + 7) & ~7; // 8 bytes aligned gGdtBufferSize = gGdtTssTableSize * mNumberOfCpus; GdtTssTables = (UINT8*) SmmFeatureAllocateCodePages (EFI_SIZE_TO_PAGES (gGdtBufferSize)); ASSERT (GdtTssTables != NULL); if (GdtTssTables == NULL) { return ; } gGdtBuffer = (UINTN)GdtTssTables; GdtTableStepSize = gGdtTssTableSize; for (Index = 0; Index < mNumberOfCpus; Index++) { CopyMem (GdtTssTables + GdtTableStepSize * Index, (VOID*)(UINTN)mSmiGdtr.Base, mSmiGdtr.Limit + 1 + FULL_TSS_SIZE); // // Fixup TSS descriptors // TssBase = (UINTN)(GdtTssTables + GdtTableStepSize * Index + mSmiGdtr.Limit + 1); GdtDescriptor = (IA32_SEGMENT_DESCRIPTOR *) (TssBase) - 2; ASSERT (GdtDescriptor != NULL); if (GdtDescriptor == NULL) { return ; } GdtDescriptor->Bits.BaseLow = (UINT16)(UINTN)TssBase; GdtDescriptor->Bits.BaseMid = (UINT8)((UINTN)TssBase >> 16); GdtDescriptor->Bits.BaseHigh = (UINT8)((UINTN)TssBase >> 24); InitRing3ModeGdt (Index, (VOID *)TssBase); } return; } /** Constructor for SPS **/ VOID SpsConstructor ( VOID ) { EFI_STATUS Status; VOID *SmmEntryPointBuffer; UINTN SmmEntryPointBufferSize; VOID *SpsBin; SPS_ENTRY_POINT SpsEntryPoint; SPS_STATUS SpsStatus; SPS_BIOS_CONTEXT BiosContext; UINTN Index; TXT_INFO_HOB *HobList = NULL; TXT_INFO_DATA *TxtInfoData = NULL; EFI_GUID *SmmEntryPointFileGuid; EFI_GUID *SpsFileGuid; #if FixedPcdGetBool (PcdSpaEnable) == 1 DXE_CPU_POLICY_PROTOCOL *CpuPolicyData; #endif DEBUG ((DEBUG_INFO, "***** SPS Constructor. Extracts binaries from FV and initializes them *****\n")); #if FixedPcdGetBool (PcdSpaEnable) == 1 // // Get Policy settings for SPA // Status = gBS->LocateProtocol (&gDxeCpuPolicyProtocolGuid, NULL, (VOID **) &CpuPolicyData); ASSERT_EFI_ERROR (Status); if ((!EFI_ERROR (Status)) && (CpuPolicyData->DgrSpaEnable)) { // // Register EFI_SMM_READY_TO_LOCK_PROTOCOL_GUID notify function. // Status = gSmst->SmmRegisterProtocolNotify ( &gEfiSmmReadyToLockProtocolGuid, SpaSmmReadyToLockCallback, &gSpaSmmReadyToLockRegistration ); ASSERT_EFI_ERROR (Status); gSpaEnable = TRUE; DEBUG ((DEBUG_INFO, "*** SPA Enabled. Will load SPA binary instead of SPS ***\n")); } #endif // // Extract SmmEntryPoint binary from FV // SmmEntryPointBuffer = NULL; SmmEntryPointBufferSize = 0; SmmEntryPointFileGuid = PcdGetPtr (PcdSpsSmmEntryPointBinFile); #if FixedPcdGetBool (PcdSpaEnable) == 1 if (gSpaEnable) { SmmEntryPointFileGuid = PcdGetPtr (PcdSpaSmmEntryPointBinFile); } #endif Status = GetSectionFromAnyFv ( SmmEntryPointFileGuid, EFI_SECTION_RAW, 0, &SmmEntryPointBuffer, &SmmEntryPointBufferSize ); if (EFI_ERROR (Status) || (SmmEntryPointBufferSize == 0)) { DEBUG ((DEBUG_ERROR, " Failed to get SMM Entry Point Buffer from FV\n")); ASSERT_EFI_ERROR (Status); return; } mSmmEntryPointHeader = (VOID *) (UINTN) SmmFeatureAllocateCodePages (EFI_SIZE_TO_PAGES(SmmEntryPointBufferSize)); if (mSmmEntryPointHeader == NULL) { ASSERT (mSmmEntryPointHeader != NULL); return; } CopyMem ((VOID *) (UINTN) mSmmEntryPointHeader, SmmEntryPointBuffer, SmmEntryPointBufferSize); DEBUG ((DEBUG_INFO, " mSmmEntryPointHeader - 0x%x\n", mSmmEntryPointHeader)); mSmmEntryPointInfoTable = (VOID *) ((UINT8 *) mSmmEntryPointHeader + mSmmEntryPointHeader->InfoTableAddress); DEBUG ((DEBUG_INFO, " mSmmEntryPointInfoTable - 0x%x\n", mSmmEntryPointInfoTable)); mSmmEntryPointHeader->InfoTableAddress = (UINT32)(UINTN)mSmmEntryPointInfoTable; gBS->FreePool ((VOID *) ((UINTN) SmmEntryPointBuffer)); // // Extract SPS or SPA binary from FV // SpsBin = NULL; gSpsBinSize = 0; SpsFileGuid = PcdGetPtr (PcdSpsBinFile); #if FixedPcdGetBool (PcdSpaEnable) == 1 if (gSpaEnable) { SpsFileGuid = PcdGetPtr (PcdSpaBinFile); } #endif Status = GetSectionFromAnyFv ( SpsFileGuid, EFI_SECTION_RAW, 0, &SpsBin, &gSpsBinSize ); if (EFI_ERROR (Status) || (gSpsBinSize == 0)) { DEBUG ((DEBUG_ERROR, " Failed to get Binary image from FV\n")); ASSERT_EFI_ERROR (Status); return; } gSpsBin = (VOID *) (UINTN) AllocatePages (EFI_SIZE_TO_PAGES (gSpsBinSize)); if (gSpsBin == NULL) { ASSERT (gSpsBin != NULL); return; } ZeroMem ((VOID *) (UINTN) gSpsBin, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (gSpsBinSize))); CopyMem ((VOID *) (UINTN) gSpsBin, SpsBin, gSpsBinSize); DEBUG ((DEBUG_INFO, " Binary image copied at - 0x%x\n", gSpsBin)); ConvertCodePage (gSpsBin, gSpsBinSize, SpsBin); gBS->FreePool ((VOID *) ((UINTN) SpsBin)); // // Initialize SPS / SPA // BiosContext.ImageBase = gSpsBin; BiosContext.CodeSegment = LONG_MODE_CS; BiosContext.TaskSegment = TSS_SEGMENT; Status = PeCoffLoaderGetEntryPoint (gSpsBin, (VOID **) &SpsEntryPoint); ASSERT_EFI_ERROR(Status); DEBUG ((DEBUG_INFO, " Image EntryPoint - 0x%x\n", SpsEntryPoint)); SpsStatus = SpsEntryPoint (&BiosContext, &mSpsKernelContext); DEBUG ((DEBUG_INFO, " Image Initialize Status - 0x%x\n", SpsStatus)); DEBUG ((DEBUG_INFO, " Image KernelContext - 0x%x\n", mSpsKernelContext)); for (Index = 0; Index < ARRAY_SIZE (mSpsKernelContext->SpsExceptionEntryPoint); Index++) { DEBUG ((DEBUG_INFO, " Image ExceptionEntryPoint[%2d] - 0x%x\n", Index, mSpsKernelContext->SpsExceptionEntryPoint [Index])); } /// /// Get TXT DPR Base address and size from HOB. /// TXT DPR & Heap memory will be write protected at SMM ready to Lock. /// HobList = (TXT_INFO_HOB *) GetFirstGuidHob (&gTxtInfoHobGuid); if (HobList != NULL) { TxtInfoData = &HobList->Data; if ((TxtInfoData != 0) && (TxtInfoData->TxtMode != 0) && (TxtInfoData->TxtDprMemoryBase != 0) && (TxtInfoData->TxtDprMemorySize != 0) ) { gTxtDprMemoryBase = TxtInfoData->TxtDprMemoryBase; gTxtDprMemorySize = TxtInfoData->TxtDprMemorySize; } } #if FixedPcdGetBool (PcdSpaEnable) == 1 if (gSpaEnable) { gSpaCtxt = AllocateZeroPool (sizeof (SPA_CTXT)); if (gSpaCtxt == NULL) { ASSERT_EFI_ERROR (FALSE); DEBUG ((DEBUG_ERROR, "Failed to allocate buffer for SPA context\n")); return ; } } #endif } /** Fill in IDT with SPS Kernel Context @param[in] IdtBase Points to IDT Base @param[in] IdtSize IDT Size **/ VOID SpsPatchIdt ( IN VOID *IdtBase, IN UINTN IdtSize ) { UINTN Index; IA32_IDT_GATE_DESCRIPTOR *IdtEntry; IdtEntry = IdtBase; for (Index = 0; Index < ARRAY_SIZE(mSpsKernelContext->SpsExceptionEntryPoint); Index++) { IdtEntry[Index].Bits.Selector = LONG_MODE_CS; IdtEntry[Index].Bits.OffsetLow = (UINT16)(mSpsKernelContext->SpsExceptionEntryPoint[Index]); IdtEntry[Index].Bits.OffsetHigh = (UINT16)(mSpsKernelContext->SpsExceptionEntryPoint[Index] >> 16); IdtEntry[Index].Bits.OffsetUpper = (UINT32)(mSpsKernelContext->SpsExceptionEntryPoint[Index] >> 32); IdtEntry[Index].Bits.GateType = IdtEntry[Index].Bits.GateType | IA32_DPL(3); // clear IST IdtEntry[Index].Bits.Reserved_0 = 0; } } /** Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is returned, then a custom SMI handler is not provided by this library, and the default SMI handler must be used. @retval 0 Use the default SMI handler. @retval > 0 Use the SMI handler installed by SmmCpuFeaturesInstallSmiHandler() The caller is required to allocate enough SMRAM for each CPU to support the size of the custom SMI handler. **/ UINTN EFIAPI SmmCpuFeaturesGetSmiHandlerSizeSps ( VOID ) { return mSmmEntryPointInfoTable->SmmEntryPointSize; } /** Install a custom SMI handler for the CPU specified by CpuIndex. This function is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size is greater than zero and is called by the CPU that was elected as monarch during System Management Mode initialization. @param[in] CpuIndex The index of the CPU to install the custom SMI handler. The value must be between 0 and the NumberOfCpus field in the System Management System Table (SMST). @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. @param[in] SmiStack The stack to use when an SMI is processed by the the CPU specified by CpuIndex. @param[in] StackSize The size, in bytes, if the stack used when an SMI is processed by the CPU specified by CpuIndex. @param[in] GdtBase The base address of the GDT to use when an SMI is processed by the CPU specified by CpuIndex. @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is processed by the CPU specified by CpuIndex. @param[in] IdtBase The base address of the IDT to use when an SMI is processed by the CPU specified by CpuIndex. @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is processed by the CPU specified by CpuIndex. @param[in] Cr3 The base address of the page tables to use when an SMI is processed by the CPU specified by CpuIndex. **/ VOID EFIAPI SmmCpuFeaturesInstallSmiHandlerSps ( IN UINTN CpuIndex, IN UINT32 SmBase, IN VOID *SmiStack, IN UINTN StackSize, IN UINTN GdtBase, IN UINTN GdtSize, IN UINTN IdtBase, IN UINTN IdtSize, IN UINT32 Cr3 ) { EFI_STATUS Status; DEBUG ((DEBUG_INFO, "SmmCpuFeaturesInstallSmiHandlerSps (%d)\n", CpuIndex)); if (!gGdtFlag) { gSmmCpuStackArrayBase = (UINTN)SmiStack; gSmmCpuStackSize = StackSize; // // DGR feature is overriding the GDT built in PiSmmCpuDxeSmm. So, below code is freeing // the GDT buffer build in PiSmmCpuDxeSmm and create a new GDT with full TSS (which includes IO Bitmap) // When EDKII open source is updated with GDT to support user mode descriptors and full TSS, then below // GDT override can be removed. // Status = gSmst->SmmFreePages (GdtBase, EFI_SIZE_TO_PAGES (((GdtSize + TSS_SIZE + 7) & ~7) * mNumberOfCpus)); ASSERT_EFI_ERROR (Status); if (EFI_ERROR (Status)) { return; } InitGdtDgr (); gGdtFlag = TRUE; } GdtBase = (UINTN)(gGdtBuffer + gGdtTssTableSize * CpuIndex); GdtSize = mSmiGdtr.Limit + 1; if (mSmmProtectedModeEnable) { // // Initialize protected mode IDT // InitProtectedModeIdt (Cr3); } // // Initialize values in template before copy // gSmmFeatureSmiStack = (UINT32)((UINTN)SmiStack + StackSize - sizeof (UINTN)); gSmmStackSize = (UINT32)(EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM) - sizeof (UINTN)); // // Initialize Nested flag location with value 0. Nested flag is used by supervisor mode exception handler // *(UINT32 *)(UINTN)(gSmmFeatureSmiStack - gSmmStackSize + 4) = 0; gSmmFeatureSmiUserStack = gSmmFeatureSmiStack - EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM); gSmmUserStackSize = (UINT32)(StackSize - EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM) - sizeof (UINTN)); DEBUG ((DEBUG_INFO, "Stack(%d) - Ring0: 0x%x, Ring3: 0x%x\n", CpuIndex, (UINTN)gSmmFeatureSmiStack, (UINTN)gSmmFeatureSmiUserStack)); gSmmFeatureSmiCr3 = Cr3; gSmmFeatureSmiHandlerIdtr.Base = IdtBase; gSmmFeatureSmiHandlerIdtr.Limit = (UINT16)(IdtSize - 1); gGdtDesc.Base = (UINT32)GdtBase; gGdtDesc.Limit = (UINT16)(GdtSize - 1); // // Set the value at the top of the CPU stack to the CPU Index // *(UINTN*)(UINTN)gSmmFeatureSmiStack = CpuIndex; *(UINTN*)(UINTN)gSmmFeatureSmiUserStack = CpuIndex; gPMStackDesc[0] = gSmmFeatureSmiStack; *(UINTN*)(UINTN)gSmmFeatureSmiStack = CpuIndex; gSmmSmiRendezvous = (UINTN)AsmSmiRendezvous; gSmmUserExceptionEntry = (UINTN)AsmOemExceptionHandler; gSmmExceptionStack = (UINT32)(UINTN)(gExceptionStack + gExceptionStackSize * (CpuIndex + 1)); gSmmExceptionStackSize = EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM); gSmmUserExceptionStack = (UINT32)(UINTN)(gExceptionStack + gExceptionStackSize * (CpuIndex + 1) - EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM)); gSmmUserExceptionStackSize = (UINT32)(gExceptionStackSize - EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM)); // // Fix IDT // SpsPatchIdt ((VOID *)IdtBase, IdtSize); InitializeIoMsrBitmap (CpuIndex, GdtBase, GdtSize); gSmmXcr0Data = 3; gSmmXssData = 0; if (mSmmSpsStateSaveEnable == TRUE) { gSmmSupervisorStateSave = 1; } // // Patch EntryPoint based upon InfoTable Entry // PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_GDT, &gGdtDesc, sizeof(gGdtDesc)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_IDT, &gSmmFeatureSmiHandlerIdtr, sizeof(gSmmFeatureSmiHandlerIdtr)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_CR3, &gSmmFeatureSmiCr3, sizeof(gSmmFeatureSmiCr3)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_SUPOVR_STATE_LOCK, &gSmmSupovrStateLockData, sizeof(gSmmSupovrStateLockData)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_STACK, (VOID *)&gSmmFeatureSmiStack, sizeof(gSmmFeatureSmiStack)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_STACK_SIZE, &gSmmStackSize, sizeof(gSmmStackSize)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_EXCEPTION_STACK, (VOID *)&gSmmExceptionStack, sizeof(gSmmExceptionStack)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_EXCEPTION_STACK_SIZE, &gSmmExceptionStackSize, sizeof(gSmmExceptionStackSize)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_SPS, &gSpsBin, sizeof(gSpsBinSize)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_ENABLE, &gSmmFeatureRing3Supported, sizeof(gSmmFeatureRing3Supported)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_ENTRY_POINT, (VOID *)&gSmmSmiRendezvous, sizeof(gSmmSmiRendezvous)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_STACK, (VOID *)&gSmmFeatureSmiUserStack, sizeof(gSmmFeatureSmiUserStack)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_STACK_SIZE, &gSmmUserStackSize, sizeof(gSmmUserStackSize)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_EXCEPTION_ENTRY_POINT, (VOID *)&gSmmUserExceptionEntry, sizeof(gSmmUserExceptionEntry)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_EXCEPTION_STACK, &gSmmUserExceptionStack, sizeof(gSmmUserExceptionStack)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_EXCEPTION_STACK_SIZE, &gSmmUserExceptionStackSize, sizeof(gSmmUserExceptionStackSize)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_MSR_BITMAP, &gMsrBitMapBase, sizeof(gMsrBitMapBase)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_XCR0, &gSmmXcr0Data, sizeof(gSmmXcr0Data)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_USER_MODE_MSR_IA32_XSS, &gSmmXssData, sizeof(gSmmXssData)); PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_SUPERVISOR_STATE_SAVE, &gSmmSupervisorStateSave, sizeof (gSmmSupervisorStateSave)); #if FixedPcdGetBool (PcdSpaEnable) == 1 if (gSpaEnable) { PatchSmmEntryPoint (mSmmEntryPointHeader, SMM_ENTRY_POINT_INFO_SPA_CTXT, &gSpaCtxt, sizeof (gSpaCtxt)); } #endif // // Copy template to CPU specific SMI handler location // CopyMem ( (VOID*)((UINTN)SmBase + SMM_HANDLER_OFFSET), (VOID*)mSmmEntryPointHeader, mSmmEntryPointInfoTable->SmmEntryPointSize ); // // Copy template to CPU specific SMI handler location // if (mSmmProtectedModeEnable) { // // Prepare for the SMMSEG structure // SetupSmmProtectedModeContext ( CpuIndex, (UINT32)SmBase, gSmmFeatureSmiStack, GdtBase, GdtSize, SMMSEG_PROTECT_MODE_CODE_SEGMENT, (IA32_DESCRIPTOR *)(UINTN)gProtModeIdtr ); } if (!mPpamConfigurationTableInitialized) { PpamSmmConfigurationTableInit(); mPpamConfigurationTableInitialized = TRUE; } } /** Final function which sets up User or Supervisor page access. @param[in] PageEntry Address of the page entry. @param[in] PageAccessRight User or Supervisor page access right. @param[out] IsModified Set to 1 if the access has been changes, 0 if no change to the attribute **/ VOID ConvertPageEntryAccessRight ( IN UINT64 *PageEntry, IN PAGE_ACCESS_RIGHT PageAccessRight, OUT BOOLEAN *IsModified ) { UINT64 CurrentPageEntry; UINT64 NewPageEntry; CurrentPageEntry = *PageEntry; NewPageEntry = CurrentPageEntry; if (PageAccessRight == PageAccessRightSupervisor) { NewPageEntry &= ~(UINT64)IA32_PG_U; } else { NewPageEntry |= IA32_PG_U; } *PageEntry = NewPageEntry; if (CurrentPageEntry != NewPageEntry) { *IsModified = TRUE; DEBUG ((DEBUG_INFO, "ConvertPageEntryAccessRight 0x%lx", CurrentPageEntry)); DEBUG ((DEBUG_INFO, "->0x%lx\n", NewPageEntry)); } else { *IsModified = FALSE; } } PAGE_ATTRIBUTE_TABLE gPageAttributeTable[] = { {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64}, {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64}, {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64}, }; /** Return page table base. @return page table base. **/ UINTN DgrGetPageTableBase ( VOID ) { return (AsmReadCr3 () & PAGING_4K_ADDRESS_MASK_64); } /** Return length according to page attributes. @param[in] PageAttributes The page attribute of the page entry. @return The length of page entry. **/ UINTN DgrPageAttributeToLength ( IN PAGE_ATTRIBUTE PageAttribute ) { UINTN Index; for (Index = 0; Index < sizeof(gPageAttributeTable)/sizeof(gPageAttributeTable[0]); Index++) { if (PageAttribute == gPageAttributeTable[Index].Attribute) { return (UINTN)gPageAttributeTable[Index].Length; } } return 0; } /** Return page table entry to match the address. @param[in] Address The address to be checked. @param[out] PageAttributes The page attribute of the page entry. @return The page entry. **/ VOID * DgrGetPageTableEntry ( IN PHYSICAL_ADDRESS Address, OUT PAGE_ATTRIBUTE *PageAttribute ) { UINTN Index1; UINTN Index2; UINTN Index3; UINTN Index4; UINT64 *L1PageTable; UINT64 *L2PageTable; UINT64 *L3PageTable; UINT64 *L4PageTable; Index4 = ((UINTN)RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK; Index3 = ((UINTN)Address >> 30) & PAGING_PAE_INDEX_MASK; Index2 = ((UINTN)Address >> 21) & PAGING_PAE_INDEX_MASK; Index1 = ((UINTN)Address >> 12) & PAGING_PAE_INDEX_MASK; if (sizeof (UINTN) == sizeof (UINT64)) { L4PageTable = (UINT64 *) DgrGetPageTableBase (); if (L4PageTable[Index4] == 0) { *PageAttribute = PageNone; return NULL; } L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); } else { L3PageTable = (UINT64 *)DgrGetPageTableBase (); } if (L3PageTable[Index3] == 0) { *PageAttribute = PageNone; return NULL; } if ((L3PageTable[Index3] & IA32_PG_PS) != 0) { // 1G *PageAttribute = Page1G; return &L3PageTable[Index3]; } L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); if (L2PageTable[Index2] == 0) { *PageAttribute = PageNone; return NULL; } if ((L2PageTable[Index2] & IA32_PG_PS) != 0) { // 2M *PageAttribute = Page2M; return &L2PageTable[Index2]; } // 4k L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); if ((L1PageTable[Index1] == 0) && (Address != 0)) { *PageAttribute = PageNone; return NULL; } *PageAttribute = Page4K; return &L1PageTable[Index1]; } /** Set Page attribute to Supervisor if available. @param[in] Address The page base address. **/ VOID DgrSetPageEntrySupervisor ( IN PHYSICAL_ADDRESS Address ) { UINTN Index4; UINTN Index3; UINTN Index2; UINTN Index1; UINT64 *L4PageAddr; UINTN L4PageEntry; UINT64 *L3PageAddr; UINTN L3PageEntry; UINT64 *L2PageAddr; UINTN L2PageEntry; UINT64 *L1PageAddr; BOOLEAN MemAttrModified = FALSE; Index4 = ((UINTN) RShiftU64 (Address, 39)) & PAGING_PAE_INDEX_MASK; Index3 = ((UINTN) Address >> 30) & PAGING_PAE_INDEX_MASK; Index2 = ((UINTN) Address >> 21) & PAGING_PAE_INDEX_MASK; Index1 = ((UINTN) Address >> 12) & PAGING_PAE_INDEX_MASK; L4PageAddr = (UINT64 *) DgrGetPageTableBase (); L4PageEntry = L4PageAddr [Index4]; if ((L4PageEntry & IA32_PG_P) != 0) { L3PageAddr = (UINT64 *)(UINTN) (L4PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); L3PageEntry = L3PageAddr[Index3]; if (((L3PageEntry & IA32_PG_P) != 0) && ((L3PageEntry & IA32_PG_PS) == 0)) { L2PageAddr = (UINT64 *)(UINTN)(L3PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); L2PageEntry = L2PageAddr[Index2]; if (((L2PageEntry & IA32_PG_P) != 0) && ((L2PageEntry & IA32_PG_PS) == 0)) { L1PageAddr = (UINT64 *) (UINTN) (L2PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); if ((L2PageEntry & IA32_PG_RW) == 0) { L2PageAddr[Index2] |= (UINT64) IA32_PG_RW; MemAttrModified = TRUE; } // Set L1 page to supervisor L1PageAddr [Index1] &= ~(UINT64) IA32_PG_U; if (MemAttrModified) { L2PageAddr[Index2] &= ~(UINT64) IA32_PG_RW; MemAttrModified = FALSE; } } } } } /** Parse Page table as per CR3 and set Page table entry attributes to Supervisor. **/ VOID SmmSetPageTableEntriesAccessRight ( VOID ) { UINTN Index4; UINTN Index3; UINTN Index2; UINT64 *L4PageAddr; UINTN L4PageEntry; UINT64 *L3PageAddr; UINTN L3PageEntry; UINT64 *L2PageAddr; UINTN L2PageEntry; L4PageAddr = (UINT64 *) DgrGetPageTableBase (); for (Index4 = 0; Index4 < MAX_PAGE_ENTRIES; Index4++) { L4PageEntry = L4PageAddr [Index4]; if ((L4PageEntry & IA32_PG_P) != 0) { DgrSetPageEntrySupervisor ((PHYSICAL_ADDRESS) (L4PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64)); L3PageAddr = (UINT64 *) (UINTN) (L4PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); for (Index3 = 0; Index3 < MAX_PAGE_ENTRIES; Index3++) { L3PageEntry = L3PageAddr [Index3]; if (((L3PageEntry & IA32_PG_P) != 0) && ((L3PageEntry & IA32_PG_PS) == 0)) { DgrSetPageEntrySupervisor ((PHYSICAL_ADDRESS) (L3PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64)); L2PageAddr = (UINT64 *) (UINTN) (L3PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64); for (Index2 = 0; Index2 < MAX_PAGE_ENTRIES; Index2++) { L2PageEntry = L2PageAddr [Index2]; if (((L2PageEntry & IA32_PG_P) != 0) && ((L2PageEntry & IA32_PG_PS) == 0)) { DgrSetPageEntrySupervisor ((PHYSICAL_ADDRESS) (L2PageEntry & ~mAddressEncMask & PAGING_4K_ADDRESS_MASK_64)); } } } } } } } /** This function returns if there is need to split page entry. @param[in] BaseAddress The base address to be checked. @param[in] Length The length to be checked. @param[in] PageEntry The page entry to be checked. @param[in] PageAttribute The page attribute of the page entry. @retval SplitAttributes on if there is need to split page entry. **/ PAGE_ATTRIBUTE DgrNeedSplitPage ( IN PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN UINT64 *PageEntry, IN PAGE_ATTRIBUTE PageAttribute ) { UINT64 PageEntryLength; PageEntryLength = DgrPageAttributeToLength (PageAttribute); if (((BaseAddress & (PageEntryLength - 1)) == 0) && (Length >= PageEntryLength)) { return PageNone; } if (((BaseAddress & PAGING_2M_MASK) != 0) || (Length < SIZE_2MB)) { return Page4K; } return Page2M; } /** This function splits one page entry to small page entries. @param[in] PageEntry The page entry to be splitted. @param[in] PageAttribute The page attribute of the page entry. @param[in] SplitAttribute How to split the page entry. @retval RETURN_SUCCESS The page entry is splitted. @retval RETURN_UNSUPPORTED The page entry does not support to be splitted. @retval RETURN_OUT_OF_RESOURCES No resource to split page entry. **/ RETURN_STATUS DgrSplitPage ( IN UINT64 *PageEntry, IN PAGE_ATTRIBUTE PageAttribute, IN PAGE_ATTRIBUTE SplitAttribute ) { UINT64 BaseAddress; UINT64 *NewPageEntry; UINTN Index; ASSERT (PageAttribute == Page2M || PageAttribute == Page1G); if (PageAttribute == Page2M) { // // Split 2M to 4K // ASSERT (SplitAttribute == Page4K); if (SplitAttribute == Page4K) { NewPageEntry = AllocatePages (1); DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); if (NewPageEntry == NULL) { return RETURN_OUT_OF_RESOURCES; } BaseAddress = *PageEntry & PAGING_2M_ADDRESS_MASK_64; for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { NewPageEntry[Index] = (BaseAddress + SIZE_4KB * Index) | mAddressEncMask | ((*PageEntry) & PAGE_PROGATE_BITS); } (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS; return RETURN_SUCCESS; } else { return RETURN_UNSUPPORTED; } } else if (PageAttribute == Page1G) { // // Split 1G to 2M // No need support 1G->4K directly, we should use 1G->2M, then 2M->4K to get more compact page table. // ASSERT (SplitAttribute == Page2M || SplitAttribute == Page4K); if ((SplitAttribute == Page2M || SplitAttribute == Page4K)) { NewPageEntry = AllocatePages (1); DEBUG ((DEBUG_VERBOSE, "Split - 0x%x\n", NewPageEntry)); if (NewPageEntry == NULL) { return RETURN_OUT_OF_RESOURCES; } BaseAddress = *PageEntry & PAGING_1G_ADDRESS_MASK_64; for (Index = 0; Index < SIZE_4KB / sizeof(UINT64); Index++) { NewPageEntry[Index] = (BaseAddress + SIZE_2MB * Index) | mAddressEncMask | IA32_PG_PS | ((*PageEntry) & PAGE_PROGATE_BITS); } (*PageEntry) = (UINT64)(UINTN)NewPageEntry | mAddressEncMask | PAGE_ATTRIBUTE_BITS; return RETURN_SUCCESS; } else { return RETURN_UNSUPPORTED; } } else { return RETURN_UNSUPPORTED; } } /** Sets up page access right @param[in] BaseAddress Base address for the page that the access need to be changed. @param[in] Length Length of the memory range where page access need to be changed. @param[in] PageAccessRight User or Supervisor page access right. @param[out] IsModified Set to 1 if the access has been changes, 0 if no change to the attribute **/ RETURN_STATUS EFIAPI ConvertMemoryPageAccessRight ( IN PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN PAGE_ACCESS_RIGHT PageAccessRight, OUT BOOLEAN *IsModified OPTIONAL ) { UINT64 *PageEntry; UINT64 *ParentPageEntry; PAGE_ATTRIBUTE PageAttribute; PAGE_ATTRIBUTE ParentPageAttribute; UINTN PageEntryLength; PAGE_ATTRIBUTE SplitAttribute; RETURN_STATUS Status; BOOLEAN IsEntryModified; BOOLEAN MemAttrModified = FALSE; DEBUG ((DEBUG_INFO, "ConvertMemoryPageAccessRight - %016lx, %016lx, %x\n", BaseAddress, Length, PageAccessRight)); ASSERT ((PageAccessRight == PageAccessRightSupervisor) || (PageAccessRight == PageAccessRightUser)); ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0); ASSERT ((Length & (SIZE_4KB - 1)) == 0); if (Length == 0) { return RETURN_INVALID_PARAMETER; } if (IsModified != NULL) { *IsModified = FALSE; } // // Below logic is to check 2M/4K page to make sure we donot waist memory. // while (Length != 0) { PageEntry = DgrGetPageTableEntry (BaseAddress, &PageAttribute); if (PageEntry == NULL) { return RETURN_UNSUPPORTED; } PageEntryLength = DgrPageAttributeToLength (PageAttribute); SplitAttribute = DgrNeedSplitPage (BaseAddress, Length, PageEntry, PageAttribute); if (SplitAttribute == PageNone) { ParentPageEntry = DgrGetPageTableEntry ((PHYSICAL_ADDRESS)PageEntry, &ParentPageAttribute); ASSERT (ParentPageEntry != NULL); if (ParentPageEntry == NULL) { return RETURN_UNSUPPORTED; } if ((*ParentPageEntry & IA32_PG_RW) == 0) { *ParentPageEntry |= IA32_PG_RW; MemAttrModified = TRUE; } ConvertPageEntryAccessRight (PageEntry, PageAccessRight, &IsEntryModified); if (MemAttrModified) { *ParentPageEntry &= ~(UINT64)IA32_PG_RW; MemAttrModified = FALSE; } if (IsEntryModified) { if (IsModified != NULL) { *IsModified = TRUE; } } // // Convert success, move to next // BaseAddress += PageEntryLength; Length -= PageEntryLength; } else { Status = DgrSplitPage (PageEntry, PageAttribute, SplitAttribute); if (RETURN_ERROR (Status)) { return RETURN_UNSUPPORTED; } if (IsModified != NULL) { *IsModified = TRUE; } // // Just split current page // Convert success in next around // } } return RETURN_SUCCESS; } /** Sets up page access right @param[in] BaseAddress Base address for the page that the access need to be changed. @param[in] Length Length of the memory range where page access need to be changed. @param[in] PageAccessRight User or Supervisor page access right. **/ EFI_STATUS EFIAPI SmmSetPageAccessRight ( IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINT64 Length, IN PAGE_ACCESS_RIGHT PageAccessRight ) { BOOLEAN IsModified; ConvertMemoryPageAccessRight (BaseAddress, Length, PageAccessRight, &IsModified); return EFI_SUCCESS; } /** Setup the page access rights within SMRAM based on ring separation policy requirements **/ VOID SetPageTableAccessRight ( VOID ) { EFI_STATUS Status; UINTN Index; UINTN StackBase; // // Set GDT table with Non-Executable // Status = gSmmMemoryAttribute->SetMemoryAttributes ( gSmmMemoryAttribute, gGdtBuffer, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (gGdtBufferSize)), EFI_MEMORY_XP ); ASSERT_EFI_ERROR (Status); // // Set MSR Bitmap policy page as Non-executable // Status = gSmmMemoryAttribute->SetMemoryAttributes ( gSmmMemoryAttribute, gMsrBitMapBase, EFI_PAGES_TO_SIZE (1), EFI_MEMORY_XP ); ASSERT_EFI_ERROR (Status); // // Set SMM Info Table page as Non-executable // Status = gSmmMemoryAttribute->SetMemoryAttributes ( gSmmMemoryAttribute, (EFI_PHYSICAL_ADDRESS) mSmmEntryPointHeader, EFI_PAGES_TO_SIZE (1), EFI_MEMORY_XP ); ASSERT_EFI_ERROR (Status); // // Set Page tables with Ring0 (Supervisor) / Ring3 (User) Policies // for (Index = 0; Index < mNumberOfCpus; Index++) { StackBase = gSmmCpuStackArrayBase + Index * gSmmCpuStackSize; // Supervisor Stack SmmSetPageAccessRight ( StackBase + gSmmCpuStackSize - EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM), EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM), PageAccessRightSupervisor ); // Known Good Stack - used for exception in Ring0 SmmSetPageAccessRight ( StackBase, EFI_PAGES_TO_SIZE(1), PageAccessRightUser ); StackBase = (UINTN)gExceptionStack + Index * gExceptionStackSize; // Supervisor Exception Stack - used for exception in Ring3 SmmSetPageAccessRight ( StackBase + gExceptionStackSize - EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM), EFI_PAGES_TO_SIZE(SUPERVISOR_PAGE_NUM), PageAccessRightSupervisor ); // Set supervisor attribute for SMM entry point code SmmSetPageAccessRight ( mSmBase[Index] + SMM_HANDLER_OFFSET, EFI_PAGES_TO_SIZE (1), PageAccessRightSupervisor ); // Set Supervisor attribute for Save State Map page if (mSmmSpsStateSaveEnable == TRUE) { SmmSetPageAccessRight ( mSmBase [Index] + (SMRAM_SAVE_STATE_MAP_OFFSET & ~PAGING_4K_MASK), EFI_PAGES_TO_SIZE (1), PageAccessRightSupervisor ); } } // Set supervisor attribute for SPS binary. SmmSetPageAccessRight ( (EFI_PHYSICAL_ADDRESS) gSpsBin, gSpsBinSize, PageAccessRightSupervisor ); // Set supervisor attribute for GDT Descriptor Table (GDT, TSS) SmmSetPageAccessRight ( gGdtBuffer, EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (gGdtBufferSize)), PageAccessRightSupervisor ); // Set supervisor attribute for Interrupt Descriptor Table SmmSetPageAccessRight ( gSmmFeatureSmiHandlerIdtr.Base, EFI_PAGES_TO_SIZE(1), PageAccessRightSupervisor ); // Set supervisor attribute for MSR Bitmap SmmSetPageAccessRight ( (EFI_PHYSICAL_ADDRESS) gMsrBitMapBase, EFI_PAGES_TO_SIZE (1), PageAccessRightSupervisor ); // Set supervisor attribute for CR3 Page SmmSetPageAccessRight ( (EFI_PHYSICAL_ADDRESS) gSmmFeatureSmiCr3, EFI_PAGES_TO_SIZE (1), PageAccessRightSupervisor ); // Set supervisor attribute for SMM Information Table SmmSetPageAccessRight ( (EFI_PHYSICAL_ADDRESS) mSmmEntryPointHeader, EFI_PAGES_TO_SIZE (1), PageAccessRightSupervisor ); // Set supervisor attribute for SMM Page Table Entries SmmSetPageTableEntriesAccessRight (); // // Set page table itself to be read-only // SetPageTableAttributes (); } /** User mode exception handler. This handler can be called by SPS supervisor exception handler to log the exception contect. @param[in] InterruptType Defines which interrupt or exception to hook. @param[in] SystemContext Pointer to EFI_SYSTEM_CONTEXT. **/ VOID EFIAPI OemExceptionHandler ( IN CONST EFI_EXCEPTION_TYPE InterruptType, IN CONST EFI_SYSTEM_CONTEXT SystemContext ) { EFI_STATUS Status; // // Post code 0x0B23 to represent SMM Resource Access Violation // PostCode (0xB23); // // Check for OEM Hook to transfer controler to that function to handle Exception. // if (gSpsRing3ExceptionHandlerProtocol != NULL) { Status = gSpsRing3ExceptionHandlerProtocol->SpsRing3ExceptionHandler (InterruptType, SystemContext); if (Status == EFI_SUCCESS) { return ; } } while (!AcquireSpinLockOrFail (mInternalDebugLock)) { ; } DEBUG ((DEBUG_INFO, "OemExceptionHandler ...\n")); DumpCpuContext (InterruptType, SystemContext); DEBUG ((DEBUG_INFO, "OemExceptionHandler Done\n")); ReleaseSpinLock (mInternalDebugLock); CpuDeadLoop (); return ; } /** Restrict write access for available VTD MMIO regions within SMM. **/ VOID RestrictSmmVtdMmioAccess ( VOID ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS VtdBase; EFI_PHYSICAL_ADDRESS VtdBasePageTableEntry; UINT8 Index; UINT8 MaxVtdEngine; PAGE_ATTRIBUTE PageAttribute; MaxVtdEngine = GetMaxVtdEngineNumber (); for (Index = 0; Index < MaxVtdEngine; Index++) { VtdBase = GetVtdBaseAddress (Index); DEBUG ((DEBUG_ERROR, "Vtd Engine%d Base Address = 0x%016lx\n", Index + 1, VtdBase)); if (VtdBase != 0) { // // Restrict write access for Vtd region MMIO Page. // Status = gSmmMemoryAttribute->SetMemoryAttributes ( gSmmMemoryAttribute, VtdBase, EFI_PAGES_TO_SIZE (1), EFI_MEMORY_RO ); ASSERT_EFI_ERROR (Status); // // Get Vtd region MMIO Page Entry pointer and // restrict its page for write access and also set the page access right to Supervisor. // VtdBasePageTableEntry = (EFI_PHYSICAL_ADDRESS) DgrGetPageTableEntry (VtdBase, &PageAttribute); DEBUG ((DEBUG_ERROR, "Vtd Engine%d Base Page Table Entry = 0x%016lx\n", Index + 1, VtdBasePageTableEntry)); if (VtdBasePageTableEntry == 0) { ASSERT (FALSE); return ; } VtdBasePageTableEntry &= ~(UINT64) EFI_PAGE_MASK; Status = gSmmMemoryAttribute->SetMemoryAttributes ( gSmmMemoryAttribute, VtdBasePageTableEntry, EFI_PAGES_TO_SIZE (1), EFI_MEMORY_RO ); ASSERT_EFI_ERROR (Status); SmmSetPageAccessRight ( VtdBasePageTableEntry, EFI_PAGES_TO_SIZE (1), PageAccessRightSupervisor ); DEBUG ((DEBUG_ERROR, "Vtd Engine%d MMIO page and its entry point Page Attributes are updated for Read Only\n", Index+1)); } } return ; } /** Restrict write access for all TXT MMIO (TXT Private & Public Space). Also restrict Write access to its Page Table Entry and keep in supervisor access right. TXT Private Space 0xFED20000 - 0xFED2FFFF (64K = 16 * 4K = 16 Pages). TXT Public Space 0xFED30000 - 0xFED3FFFF (16 Pages). **/ VOID RestrictSmmTxtMmioAccess ( VOID ) { EFI_STATUS Status; DEBUG ((DEBUG_INFO, "RestrictSmmTxtMmioAccess...\n")); // // Write Protect TXT Private space // Status = gSmmMemoryAttribute->SetMemoryAttributes ( gSmmMemoryAttribute, TXT_PRIVATE_BASE, EFI_PAGE_SIZE * 16, EFI_MEMORY_RO ); ASSERT_EFI_ERROR (Status); // // Write Protect TXT Public space // Status = gSmmMemoryAttribute->SetMemoryAttributes ( gSmmMemoryAttribute, TXT_PUBLIC_BASE, EFI_PAGE_SIZE * 16, EFI_MEMORY_RO ); ASSERT_EFI_ERROR (Status); return ; } /** Restrict System Memory write access being inside SMM. **/ EFI_STATUS RestrictSmmSystemMemoryAccess ( VOID ) { EFI_STATUS Status; if ((gTxtDprMemoryBase == 0) || (gTxtDprMemorySize == 0)) { DEBUG ((DEBUG_INFO, "TXT DPR memory Base, Size values are not valid.\n")); return EFI_NOT_FOUND; } /// /// Write Protect TXT DPR Memory region (TXT Heap memory is within DPR Memory region). /// Status = gSmmMemoryAttribute->SetMemoryAttributes ( gSmmMemoryAttribute, gTxtDprMemoryBase, gTxtDprMemorySize, EFI_MEMORY_RO ); ASSERT_EFI_ERROR (Status); DEBUG ((DEBUG_INFO, "Write protected TXT DPR memory from 0x%016lx, size of 0x%x\n", gTxtDprMemoryBase, gTxtDprMemorySize)); return EFI_SUCCESS; } /** This function sets - Page access rights within SMRAM based on ring separation policy requirements. - MMIO access rights. - System Memory access rights which are being accessed from SMM. **/ EFI_STATUS SetSmmDgrPolicy ( VOID ) { EFI_STATUS Status; Status = gSmst->SmmLocateProtocol ( &gEdkiiSmmMemoryAttributeProtocolGuid, NULL, (VOID **) &gSmmMemoryAttribute ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "Failed to locate EdkiiSmmMemoryAttributeProtocol...\n")); return EFI_NOT_FOUND; } Status = gSmst->SmmLocateProtocol ( &gSpsRing3ExceptionHandlerProtocolGuid, NULL, (VOID **) &gSpsRing3ExceptionHandlerProtocol ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "Failed to locate gSpsRing3ExceptionHandlerProtocol...\n", Status)); } /// /// Restrict MMIO write access for VTD and TXT. /// RestrictSmmVtdMmioAccess (); RestrictSmmTxtMmioAccess (); /// /// Restrict System Memory write access. /// RestrictSmmSystemMemoryAccess (); /// /// Set page table and page access rights. /// SetPageTableAccessRight (); return EFI_SUCCESS; }