/** @file Copyright (c) 2017, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #define NEXT_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) + (Size))) /// /// Page Table Entry /// #define IA32_PG_P BIT0 #define IA32_PG_RW BIT1 #define IA32_PG_U BIT2 #define IA32_PG_WT BIT3 #define IA32_PG_CD BIT4 #define IA32_PG_A BIT5 #define IA32_PG_D BIT6 #define IA32_PG_PS BIT7 #define IA32_PG_PAT_2M BIT12 #define IA32_PG_PAT_4K IA32_PG_PS #define IA32_PG_PMNT BIT62 #define IA32_PG_NX BIT63 #define PAGING_4K_MASK 0xFFF #define PAGING_2M_MASK 0x1FFFFF #define PAGING_1G_MASK 0x3FFFFFFF #define PAGING_PAE_INDEX_MASK 0x1FF #define PAGING_4K_ADDRESS_MASK_64 0x000FFFFFFFFFF000ull #define PAGING_2M_ADDRESS_MASK_64 0x000FFFFFFFE00000ull #define PAGING_1G_ADDRESS_MASK_64 0x000FFFFFC0000000ull typedef enum { PageNone, Page4K, Page2M, Page1G, } PAGE_ATTRIBUTE; typedef struct { PAGE_ATTRIBUTE Attribute; UINT64 Length; UINT64 AddressMask; } PAGE_ATTRIBUTE_TABLE; PAGE_ATTRIBUTE_TABLE mPageAttributeTable[] = { {Page4K, SIZE_4KB, PAGING_4K_ADDRESS_MASK_64}, {Page2M, SIZE_2MB, PAGING_2M_ADDRESS_MASK_64}, {Page1G, SIZE_1GB, PAGING_1G_ADDRESS_MASK_64}, }; EFI_STATUS EFIAPI SmmGetSystemConfigurationTable ( IN EFI_GUID *TableGuid, OUT VOID **Table ); /** Return page table base. @return page table base. **/ UINTN GetPageTableBase ( 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 PageAttributeToLength ( IN PAGE_ATTRIBUTE PageAttribute ) { UINTN Index; for (Index = 0; Index < sizeof(mPageAttributeTable)/sizeof(mPageAttributeTable[0]); Index++) { if (PageAttribute == mPageAttributeTable[Index].Attribute) { return (UINTN)mPageAttributeTable[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 * GetPageTableEntry ( 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 *)GetPageTableBase (); if (L4PageTable[Index4] == 0) { *PageAttribute = Page1G; return NULL; } L3PageTable = (UINT64 *)(UINTN)(L4PageTable[Index4] & PAGING_4K_ADDRESS_MASK_64); } else { L3PageTable = (UINT64 *)GetPageTableBase (); } if (L3PageTable[Index3] == 0) { *PageAttribute = Page1G; return NULL; } if ((L3PageTable[Index3] & IA32_PG_PS) != 0) { // 1G *PageAttribute = Page1G; return &L3PageTable[Index3]; } L2PageTable = (UINT64 *)(UINTN)(L3PageTable[Index3] & PAGING_4K_ADDRESS_MASK_64); if (L2PageTable[Index2] == 0) { *PageAttribute = Page2M; return NULL; } if ((L2PageTable[Index2] & IA32_PG_PS) != 0) { // 2M *PageAttribute = Page2M; return &L2PageTable[Index2]; } // 4k L1PageTable = (UINT64 *)(UINTN)(L2PageTable[Index2] & PAGING_4K_ADDRESS_MASK_64); if ((L1PageTable[Index1] == 0) && (Address != 0)) { *PageAttribute = Page4K; return NULL; } *PageAttribute = Page4K; return &L1PageTable[Index1]; } typedef struct { volatile BOOLEAN Valid; volatile UINT64 SmBase; } SMBASE_SHARED_BUFFER; VOID EFIAPI GetSmBaseOnCurrentProcessor ( IN OUT VOID *Buffer ) { SMBASE_SHARED_BUFFER *SmBaseBuffer; SmBaseBuffer = Buffer; SmBaseBuffer->SmBase = AsmReadMsr64 (MSR_IA32_SMBASE); SmBaseBuffer->Valid = TRUE; } UINT64 *mSmBaseBuffer; VOID SetupSmBaseBuffer ( VOID ) { volatile SMBASE_SHARED_BUFFER SmBaseBuffer; EFI_STATUS Status; UINTN Index; UINTN Base = 0; UINTN Delta = 0; if (mSmBaseBuffer != NULL) { return ; } mSmBaseBuffer = AllocatePool (sizeof(UINT64) * gSmst->NumberOfCpus); ASSERT(mSmBaseBuffer != NULL); for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { ZeroMem ((VOID *)&SmBaseBuffer, sizeof(SmBaseBuffer)); if (Index == gSmst->CurrentlyExecutingCpu) { GetSmBaseOnCurrentProcessor ((VOID *)&SmBaseBuffer); DEBUG ((DEBUG_INFO, "SmbaseBsp(%d) - 0x%x\n", Index, SmBaseBuffer.SmBase)); } else { Status = gSmst->SmmStartupThisAp (GetSmBaseOnCurrentProcessor, Index, (VOID *)&SmBaseBuffer); if (!FeaturePcdGet (PcdCpuHotPlugSupport)) { ASSERT_EFI_ERROR (Status); } if (EFI_ERROR(Status)) { SmBaseBuffer.SmBase = Base + Delta * Index; DEBUG ((DEBUG_INFO, "SmbaseAp(%d) - unknown, guess - 0x%x\n", Index, SmBaseBuffer.SmBase)); } else { while (!SmBaseBuffer.Valid) { CpuPause (); } DEBUG ((DEBUG_INFO, "SmbaseAp(%d) - 0x%x\n", Index, SmBaseBuffer.SmBase)); } } mSmBaseBuffer[Index] = SmBaseBuffer.SmBase; if (Base == 0) { Base = (UINTN)SmBaseBuffer.SmBase; DEBUG ((DEBUG_INFO, "-- Base - 0x%x\n", Base)); } else if (Delta == 0) { Delta = (UINTN)(SmBaseBuffer.SmBase - Base); DEBUG ((DEBUG_INFO, "-- Delta - 0x%x\n", Delta)); } } } BOOLEAN IsSmmSaveState ( IN EFI_PHYSICAL_ADDRESS BaseAddress ) { UINTN Index; UINTN TileCodeSize; UINTN TileDataSize; UINTN TileSize; SetupSmBaseBuffer (); TileCodeSize = SIZE_4KB; // BUGBUG: Assume 4KB TileCodeSize = ALIGN_VALUE(TileCodeSize, SIZE_4KB); TileDataSize = (SMRAM_SAVE_STATE_MAP_OFFSET - TXT_SMM_PSD_OFFSET) + sizeof (SMRAM_SAVE_STATE_MAP); TileDataSize = ALIGN_VALUE(TileDataSize, SIZE_4KB); TileSize = TileDataSize + TileCodeSize - 1; TileSize = 2 * GetPowerOfTwo32 ((UINT32)TileSize); for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { if (Index == gSmst->NumberOfCpus - 1) { TileSize = SIZE_32KB; } if ((BaseAddress >= mSmBaseBuffer[Index] + SMM_HANDLER_OFFSET + TileCodeSize) && (BaseAddress < mSmBaseBuffer[Index] + SMM_HANDLER_OFFSET + TileSize)) { return TRUE; } } return FALSE; } EFI_STATUS TestPointCheckPageTable ( IN EFI_PHYSICAL_ADDRESS BaseAddress, IN UINTN Length, IN BOOLEAN IsCode, IN BOOLEAN IsOutsideSmram ) { UINT64 *PageEntry; PAGE_ATTRIBUTE PageAttribute; UINTN PageEntryLength; EFI_PHYSICAL_ADDRESS BaseAddressEnd; ASSERT ((BaseAddress & (SIZE_4KB - 1)) == 0); ASSERT ((Length & (SIZE_4KB - 1)) == 0); BaseAddressEnd = BaseAddress + Length; // // Below logic is to check 2M/4K page to make sure we donot waist memory. // while (BaseAddress < BaseAddressEnd) { PageEntry = GetPageTableEntry (BaseAddress, &PageAttribute); ASSERT (PageAttribute != PageNone); if (IsOutsideSmram) { if ((PageEntry != NULL) && ((*PageEntry & IA32_PG_P) != 0)) { DEBUG ((DEBUG_ERROR, "OutsideSmram present - 0x%x\n", BaseAddress)); return EFI_INVALID_PARAMETER; } } if (IsCode) { if ((PageEntry == NULL) || ((*PageEntry & IA32_PG_P) == 0)) { DEBUG ((DEBUG_ERROR, "Code Page not exist - 0x%x\n", BaseAddress)); return EFI_INVALID_PARAMETER; } else if ((*PageEntry & IA32_PG_RW) != 0) { // // Check if it is SMM SaveState // if (!IsSmmSaveState (BaseAddress)) { DEBUG ((DEBUG_ERROR, "Code Page read write - 0x%x\n", BaseAddress)); return EFI_INVALID_PARAMETER; } } } else { if ((PageEntry == NULL) || ((*PageEntry & IA32_PG_P) == 0)) { // Pass } else if ((*PageEntry & IA32_PG_NX) == 0) { DEBUG ((DEBUG_ERROR, "Data Page executable - 0x%x\n", BaseAddress)); return EFI_INVALID_PARAMETER; } } PageEntryLength = PageAttributeToLength (PageAttribute); PageEntryLength = (UINTN)((BaseAddress & ~(PageEntryLength - 1)) + PageEntryLength - BaseAddress); // // move to next // BaseAddress += PageEntryLength; Length -= PageEntryLength; } return RETURN_SUCCESS; } EFI_STATUS TestPointCheckPagingWithMemoryAttributesTable ( IN EFI_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable ) { UINTN Index; EFI_MEMORY_DESCRIPTOR *Entry; EFI_STATUS Status; EFI_STATUS ReturnStatus; ReturnStatus = EFI_SUCCESS; Entry = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1); for (Index = 0; Index < MemoryAttributesTable->NumberOfEntries; Index++) { DEBUG ((DEBUG_INFO, "SmmMemoryAttribute Checking 0x%lx - 0x%x\n", Entry->PhysicalStart, EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages))); Status = TestPointCheckPageTable ( Entry->PhysicalStart, EFI_PAGES_TO_SIZE((UINTN)Entry->NumberOfPages), ((Entry->Attribute & EFI_MEMORY_RO) == 0) ? FALSE : TRUE, FALSE ); if (EFI_ERROR(Status)) { ReturnStatus = Status; } Entry = NEXT_MEMORY_DESCRIPTOR (Entry, MemoryAttributesTable->DescriptorSize); } return ReturnStatus; } EFI_STATUS TestPointCheckSmmPaging ( VOID ) { EFI_STATUS Status; VOID *MemoryAttributesTable; DEBUG ((DEBUG_INFO, "==== TestPointCheckSmmPaging - Enter\n")); Status = SmmGetSystemConfigurationTable (&gEdkiiPiSmmMemoryAttributesTableGuid, (VOID **)&MemoryAttributesTable); if (!EFI_ERROR (Status)) { Status = TestPointCheckPagingWithMemoryAttributesTable(MemoryAttributesTable); } if (EFI_ERROR (Status)) { TestPointLibAppendErrorString ( PLATFORM_TEST_POINT_ROLE_PLATFORM_IBV, NULL, TEST_POINT_BYTE6_SMM_READY_TO_BOOT_SMM_PAGE_LEVEL_PROTECTION_ERROR_CODE \ TEST_POINT_SMM_READY_TO_BOOT \ TEST_POINT_BYTE6_SMM_READY_TO_BOOT_SMM_PAGE_LEVEL_PROTECTION_ERROR_STRING ); } DEBUG ((DEBUG_INFO, "==== TestPointCheckSmmPaging - Exit\n")); return EFI_SUCCESS; }