| /** @file | |
| PI SMM MemoryAttributes support | |
| Copyright (c) 2024, Intel Corporation. All rights reserved.<BR> | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include "StandaloneMmCore.h" | |
| #define PREVIOUS_MEMORY_DESCRIPTOR(MemoryDescriptor, Size) \ | |
| ((EFI_MEMORY_DESCRIPTOR *)((UINT8 *)(MemoryDescriptor) - (Size))) | |
| #define IMAGE_PROPERTIES_PRIVATE_DATA_SIGNATURE SIGNATURE_32 ('I','P','P','D') | |
| typedef struct { | |
| UINT32 Signature; | |
| UINTN ImageRecordCount; | |
| UINTN CodeSegmentCountMax; | |
| LIST_ENTRY ImageRecordList; | |
| } IMAGE_PROPERTIES_PRIVATE_DATA; | |
| IMAGE_PROPERTIES_PRIVATE_DATA mImagePropertiesPrivateData = { | |
| IMAGE_PROPERTIES_PRIVATE_DATA_SIGNATURE, | |
| 0, | |
| 0, | |
| INITIALIZE_LIST_HEAD_VARIABLE (mImagePropertiesPrivateData.ImageRecordList) | |
| }; | |
| #define EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA BIT0 | |
| UINT64 mMemoryProtectionAttribute = EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA; | |
| // | |
| // Below functions are for MemoryMap | |
| // | |
| /** | |
| Merge continuous memory map entries whose have same attributes. | |
| @param[in, out] MemoryMap A pointer to the buffer in which firmware places | |
| the current memory map. | |
| @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the | |
| MemoryMap buffer. On input, this is the size of | |
| the current memory map. On output, | |
| it is the size of new memory map after merge. | |
| @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. | |
| **/ | |
| STATIC | |
| VOID | |
| MergeMemoryMap ( | |
| IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, | |
| IN OUT UINTN *MemoryMapSize, | |
| IN UINTN DescriptorSize | |
| ) | |
| { | |
| EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; | |
| EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; | |
| UINT64 MemoryBlockLength; | |
| EFI_MEMORY_DESCRIPTOR *NewMemoryMapEntry; | |
| EFI_MEMORY_DESCRIPTOR *NextMemoryMapEntry; | |
| MemoryMapEntry = MemoryMap; | |
| NewMemoryMapEntry = MemoryMap; | |
| MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + *MemoryMapSize); | |
| while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) { | |
| CopyMem (NewMemoryMapEntry, MemoryMapEntry, sizeof (EFI_MEMORY_DESCRIPTOR)); | |
| NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); | |
| do { | |
| MemoryBlockLength = LShiftU64 (MemoryMapEntry->NumberOfPages, EFI_PAGE_SHIFT); | |
| if (((UINTN)NextMemoryMapEntry < (UINTN)MemoryMapEnd) && | |
| (MemoryMapEntry->Type == NextMemoryMapEntry->Type) && | |
| (MemoryMapEntry->Attribute == NextMemoryMapEntry->Attribute) && | |
| ((MemoryMapEntry->PhysicalStart + MemoryBlockLength) == NextMemoryMapEntry->PhysicalStart)) | |
| { | |
| MemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages; | |
| if (NewMemoryMapEntry != MemoryMapEntry) { | |
| NewMemoryMapEntry->NumberOfPages += NextMemoryMapEntry->NumberOfPages; | |
| } | |
| NextMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); | |
| continue; | |
| } else { | |
| MemoryMapEntry = PREVIOUS_MEMORY_DESCRIPTOR (NextMemoryMapEntry, DescriptorSize); | |
| break; | |
| } | |
| } while (TRUE); | |
| MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); | |
| NewMemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (NewMemoryMapEntry, DescriptorSize); | |
| } | |
| *MemoryMapSize = (UINTN)NewMemoryMapEntry - (UINTN)MemoryMap; | |
| return; | |
| } | |
| /** | |
| Enforce memory map attributes. | |
| This function will set EfiRuntimeServicesData/EfiMemoryMappedIO/EfiMemoryMappedIOPortSpace to be EFI_MEMORY_XP. | |
| @param[in, out] MemoryMap A pointer to the buffer in which firmware places | |
| the current memory map. | |
| @param[in] MemoryMapSize Size, in bytes, of the MemoryMap buffer. | |
| @param[in] DescriptorSize Size, in bytes, of an individual EFI_MEMORY_DESCRIPTOR. | |
| **/ | |
| STATIC | |
| VOID | |
| EnforceMemoryMapAttribute ( | |
| IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, | |
| IN UINTN MemoryMapSize, | |
| IN UINTN DescriptorSize | |
| ) | |
| { | |
| EFI_MEMORY_DESCRIPTOR *MemoryMapEntry; | |
| EFI_MEMORY_DESCRIPTOR *MemoryMapEnd; | |
| MemoryMapEntry = MemoryMap; | |
| MemoryMapEnd = (EFI_MEMORY_DESCRIPTOR *)((UINT8 *)MemoryMap + MemoryMapSize); | |
| while ((UINTN)MemoryMapEntry < (UINTN)MemoryMapEnd) { | |
| if (MemoryMapEntry->Attribute != 0) { | |
| // It is PE image, the attribute is already set. | |
| } else { | |
| switch (MemoryMapEntry->Type) { | |
| case EfiRuntimeServicesCode: | |
| MemoryMapEntry->Attribute = EFI_MEMORY_RO; | |
| break; | |
| case EfiRuntimeServicesData: | |
| default: | |
| MemoryMapEntry->Attribute |= EFI_MEMORY_XP; | |
| break; | |
| } | |
| } | |
| MemoryMapEntry = NEXT_MEMORY_DESCRIPTOR (MemoryMapEntry, DescriptorSize); | |
| } | |
| return; | |
| } | |
| /** | |
| This function for GetMemoryMap() with memory attributes table. | |
| It calls original GetMemoryMap() to get the original memory map information. Then | |
| plus the additional memory map entries for PE Code/Data separation. | |
| @param[in, out] MemoryMapSize A pointer to the size, in bytes, of the | |
| MemoryMap buffer. On input, this is the size of | |
| the buffer allocated by the caller. On output, | |
| it is the size of the buffer returned by the | |
| firmware if the buffer was large enough, or the | |
| size of the buffer needed to contain the map if | |
| the buffer was too small. | |
| @param[in, out] MemoryMap A pointer to the buffer in which firmware places | |
| the current memory map. | |
| @param[out] MapKey A pointer to the location in which firmware | |
| returns the key for the current memory map. | |
| @param[out] DescriptorSize A pointer to the location in which firmware | |
| returns the size, in bytes, of an individual | |
| EFI_MEMORY_DESCRIPTOR. | |
| @param[out] DescriptorVersion A pointer to the location in which firmware | |
| returns the version number associated with the | |
| EFI_MEMORY_DESCRIPTOR. | |
| @retval EFI_SUCCESS The memory map was returned in the MemoryMap | |
| buffer. | |
| @retval EFI_BUFFER_TOO_SMALL The MemoryMap buffer was too small. The current | |
| buffer size needed to hold the memory map is | |
| returned in MemoryMapSize. | |
| @retval EFI_INVALID_PARAMETER One of the parameters has an invalid value. | |
| **/ | |
| STATIC | |
| EFI_STATUS | |
| EFIAPI | |
| MmCoreGetMemoryMapMemoryAttributesTable ( | |
| IN OUT UINTN *MemoryMapSize, | |
| IN OUT EFI_MEMORY_DESCRIPTOR *MemoryMap, | |
| OUT UINTN *MapKey, | |
| OUT UINTN *DescriptorSize, | |
| OUT UINT32 *DescriptorVersion | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN OldMemoryMapSize; | |
| UINTN AdditionalRecordCount; | |
| // | |
| // If PE code/data is not aligned, just return. | |
| // | |
| if ((mMemoryProtectionAttribute & EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) == 0) { | |
| return MmCoreGetMemoryMap (MemoryMapSize, MemoryMap, MapKey, DescriptorSize, DescriptorVersion); | |
| } | |
| if (MemoryMapSize == NULL) { | |
| return EFI_INVALID_PARAMETER; | |
| } | |
| AdditionalRecordCount = (2 * mImagePropertiesPrivateData.CodeSegmentCountMax + 3) * mImagePropertiesPrivateData.ImageRecordCount; | |
| OldMemoryMapSize = *MemoryMapSize; | |
| Status = MmCoreGetMemoryMap (MemoryMapSize, MemoryMap, MapKey, DescriptorSize, DescriptorVersion); | |
| if (Status == EFI_BUFFER_TOO_SMALL) { | |
| *MemoryMapSize = *MemoryMapSize + (*DescriptorSize) * AdditionalRecordCount; | |
| } else if (Status == EFI_SUCCESS) { | |
| if (OldMemoryMapSize - *MemoryMapSize < (*DescriptorSize) * AdditionalRecordCount) { | |
| *MemoryMapSize = *MemoryMapSize + (*DescriptorSize) * AdditionalRecordCount; | |
| // | |
| // Need update status to buffer too small | |
| // | |
| Status = EFI_BUFFER_TOO_SMALL; | |
| } else { | |
| // | |
| // Split PE code/data | |
| // | |
| ASSERT (MemoryMap != NULL); | |
| SplitTable (MemoryMapSize, MemoryMap, *DescriptorSize, &mImagePropertiesPrivateData.ImageRecordList, AdditionalRecordCount); | |
| // | |
| // Set RuntimeData to XP | |
| // | |
| EnforceMemoryMapAttribute (MemoryMap, *MemoryMapSize, *DescriptorSize); | |
| // | |
| // Merge same type to save entry size | |
| // | |
| MergeMemoryMap (MemoryMap, MemoryMapSize, *DescriptorSize); | |
| } | |
| } | |
| return Status; | |
| } | |
| // | |
| // Below functions are for ImageRecord | |
| // | |
| /** | |
| Insert image record. | |
| @param[in] DriverEntry Driver information | |
| **/ | |
| VOID | |
| MmInsertImageRecord ( | |
| IN EFI_MM_DRIVER_ENTRY *DriverEntry | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| IMAGE_PROPERTIES_RECORD *ImageRecord; | |
| CHAR8 *PdbPointer; | |
| UINT32 RequiredAlignment; | |
| DEBUG ((DEBUG_VERBOSE, "MM InsertImageRecord - 0x%x\n", DriverEntry)); | |
| ImageRecord = AllocatePool (sizeof (*ImageRecord)); | |
| if (ImageRecord == NULL) { | |
| return; | |
| } | |
| InitializeListHead (&ImageRecord->Link); | |
| InitializeListHead (&ImageRecord->CodeSegmentList); | |
| PdbPointer = PeCoffLoaderGetPdbPointer ((VOID *)(UINTN)DriverEntry->ImageBuffer); | |
| if (PdbPointer != NULL) { | |
| DEBUG ((DEBUG_VERBOSE, "MM Image - %a\n", PdbPointer)); | |
| } | |
| RequiredAlignment = RUNTIME_PAGE_ALLOCATION_GRANULARITY; | |
| Status = CreateImagePropertiesRecord ( | |
| (VOID *)(UINTN)DriverEntry->ImageBuffer, | |
| LShiftU64 (DriverEntry->NumberOfPage, EFI_PAGE_SHIFT), | |
| &RequiredAlignment, | |
| ImageRecord | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| if (Status == EFI_ABORTED) { | |
| mMemoryProtectionAttribute &= | |
| ~((UINT64)EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA); | |
| } | |
| goto Finish; | |
| } | |
| if (ImageRecord->CodeSegmentCount == 0) { | |
| mMemoryProtectionAttribute &= | |
| ~((UINT64)EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA); | |
| DEBUG ((DEBUG_ERROR, "MM !!!!!!!! InsertImageRecord - CodeSegmentCount is 0 !!!!!!!!\n")); | |
| if (PdbPointer != NULL) { | |
| DEBUG ((DEBUG_ERROR, "MM !!!!!!!! Image - %a !!!!!!!!\n", PdbPointer)); | |
| } | |
| Status = EFI_ABORTED; | |
| goto Finish; | |
| } | |
| // | |
| // Check overlap all section in ImageBase/Size | |
| // | |
| if (!IsImageRecordCodeSectionValid (ImageRecord)) { | |
| DEBUG ((DEBUG_ERROR, "MM IsImageRecordCodeSectionValid - FAIL\n")); | |
| Status = EFI_ABORTED; | |
| goto Finish; | |
| } | |
| InsertTailList (&mImagePropertiesPrivateData.ImageRecordList, &ImageRecord->Link); | |
| mImagePropertiesPrivateData.ImageRecordCount++; | |
| if (mImagePropertiesPrivateData.CodeSegmentCountMax < ImageRecord->CodeSegmentCount) { | |
| mImagePropertiesPrivateData.CodeSegmentCountMax = ImageRecord->CodeSegmentCount; | |
| } | |
| SortImageRecord (&mImagePropertiesPrivateData.ImageRecordList); | |
| Finish: | |
| if (EFI_ERROR (Status) && (ImageRecord != NULL)) { | |
| DeleteImagePropertiesRecord (ImageRecord); | |
| } | |
| return; | |
| } | |
| /** | |
| Publish MemoryAttributesTable to MM configuration table. | |
| **/ | |
| VOID | |
| PublishMemoryAttributesTable ( | |
| VOID | |
| ) | |
| { | |
| UINTN MemoryMapSize; | |
| EFI_MEMORY_DESCRIPTOR *MemoryMap; | |
| UINTN MapKey; | |
| UINTN DescriptorSize; | |
| UINT32 DescriptorVersion; | |
| UINTN Index; | |
| EFI_STATUS Status; | |
| UINTN RuntimeEntryCount; | |
| EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE *MemoryAttributesTable; | |
| EFI_MEMORY_DESCRIPTOR *MemoryAttributesEntry; | |
| UINTN MemoryAttributesTableSize; | |
| MemoryMapSize = 0; | |
| MemoryMap = NULL; | |
| Status = MmCoreGetMemoryMapMemoryAttributesTable ( | |
| &MemoryMapSize, | |
| MemoryMap, | |
| &MapKey, | |
| &DescriptorSize, | |
| &DescriptorVersion | |
| ); | |
| ASSERT (Status == EFI_BUFFER_TOO_SMALL); | |
| do { | |
| DEBUG ((DEBUG_VERBOSE, "MemoryMapSize - 0x%x\n", MemoryMapSize)); | |
| MemoryMap = AllocatePool (MemoryMapSize); | |
| ASSERT (MemoryMap != NULL); | |
| DEBUG ((DEBUG_VERBOSE, "MemoryMap - 0x%x\n", MemoryMap)); | |
| Status = MmCoreGetMemoryMapMemoryAttributesTable ( | |
| &MemoryMapSize, | |
| MemoryMap, | |
| &MapKey, | |
| &DescriptorSize, | |
| &DescriptorVersion | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| FreePool (MemoryMap); | |
| } | |
| } while (Status == EFI_BUFFER_TOO_SMALL); | |
| // | |
| // Allocate MemoryAttributesTable | |
| // | |
| RuntimeEntryCount = MemoryMapSize/DescriptorSize; | |
| MemoryAttributesTableSize = sizeof (EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE) + DescriptorSize * RuntimeEntryCount; | |
| MemoryAttributesTable = AllocatePool (sizeof (EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE) + DescriptorSize * RuntimeEntryCount); | |
| ASSERT (MemoryAttributesTable != NULL); | |
| if (MemoryAttributesTable == NULL) { | |
| return; | |
| } | |
| MemoryAttributesTable->Version = EDKII_PI_SMM_MEMORY_ATTRIBUTES_TABLE_VERSION; | |
| MemoryAttributesTable->NumberOfEntries = (UINT32)RuntimeEntryCount; | |
| MemoryAttributesTable->DescriptorSize = (UINT32)DescriptorSize; | |
| MemoryAttributesTable->Reserved = 0; | |
| DEBUG ((DEBUG_VERBOSE, "MemoryAttributesTable:\n")); | |
| DEBUG ((DEBUG_VERBOSE, " Version - 0x%08x\n", MemoryAttributesTable->Version)); | |
| DEBUG ((DEBUG_VERBOSE, " NumberOfEntries - 0x%08x\n", MemoryAttributesTable->NumberOfEntries)); | |
| DEBUG ((DEBUG_VERBOSE, " DescriptorSize - 0x%08x\n", MemoryAttributesTable->DescriptorSize)); | |
| MemoryAttributesEntry = (EFI_MEMORY_DESCRIPTOR *)(MemoryAttributesTable + 1); | |
| for (Index = 0; Index < MemoryMapSize/DescriptorSize; Index++) { | |
| CopyMem (MemoryAttributesEntry, MemoryMap, DescriptorSize); | |
| DEBUG ((DEBUG_VERBOSE, "Entry (0x%x)\n", MemoryAttributesEntry)); | |
| DEBUG ((DEBUG_VERBOSE, " Type - 0x%x\n", MemoryAttributesEntry->Type)); | |
| DEBUG ((DEBUG_VERBOSE, " PhysicalStart - 0x%016lx\n", MemoryAttributesEntry->PhysicalStart)); | |
| DEBUG ((DEBUG_VERBOSE, " VirtualStart - 0x%016lx\n", MemoryAttributesEntry->VirtualStart)); | |
| DEBUG ((DEBUG_VERBOSE, " NumberOfPages - 0x%016lx\n", MemoryAttributesEntry->NumberOfPages)); | |
| DEBUG ((DEBUG_VERBOSE, " Attribute - 0x%016lx\n", MemoryAttributesEntry->Attribute)); | |
| MemoryAttributesEntry = NEXT_MEMORY_DESCRIPTOR (MemoryAttributesEntry, DescriptorSize); | |
| MemoryMap = NEXT_MEMORY_DESCRIPTOR (MemoryMap, DescriptorSize); | |
| } | |
| Status = MmInstallConfigurationTable (&gMmCoreMmst, &gEdkiiPiSmmMemoryAttributesTableGuid, MemoryAttributesTable, MemoryAttributesTableSize); | |
| ASSERT_EFI_ERROR (Status); | |
| } | |
| /** | |
| This function installs all MM image record information. | |
| **/ | |
| VOID | |
| MmInstallImageRecord ( | |
| VOID | |
| ) | |
| { | |
| EFI_STATUS Status; | |
| UINTN NoHandles; | |
| EFI_HANDLE *HandleBuffer; | |
| EFI_LOADED_IMAGE_PROTOCOL *LoadedImage; | |
| UINTN Index; | |
| EFI_MM_DRIVER_ENTRY DriverEntry; | |
| Status = MmLocateHandleBuffer ( | |
| ByProtocol, | |
| &gEfiLoadedImageProtocolGuid, | |
| NULL, | |
| &NoHandles, | |
| &HandleBuffer | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| return; | |
| } | |
| for (Index = 0; Index < NoHandles; Index++) { | |
| Status = MmHandleProtocol ( | |
| HandleBuffer[Index], | |
| &gEfiLoadedImageProtocolGuid, | |
| (VOID **)&LoadedImage | |
| ); | |
| if (EFI_ERROR (Status)) { | |
| continue; | |
| } | |
| DEBUG ((DEBUG_VERBOSE, "LoadedImage - 0x%x 0x%x ", LoadedImage->ImageBase, LoadedImage->ImageSize)); | |
| { | |
| VOID *PdbPointer; | |
| PdbPointer = PeCoffLoaderGetPdbPointer (LoadedImage->ImageBase); | |
| if (PdbPointer != NULL) { | |
| DEBUG ((DEBUG_VERBOSE, "(%a) ", PdbPointer)); | |
| } | |
| } | |
| DEBUG ((DEBUG_VERBOSE, "\n")); | |
| ZeroMem (&DriverEntry, sizeof (DriverEntry)); | |
| DriverEntry.ImageBuffer = (UINTN)LoadedImage->ImageBase; | |
| DriverEntry.NumberOfPage = EFI_SIZE_TO_PAGES ((UINTN)LoadedImage->ImageSize); | |
| MmInsertImageRecord (&DriverEntry); | |
| } | |
| FreePool (HandleBuffer); | |
| } | |
| /** | |
| Initialize MemoryAttributesTable support. | |
| **/ | |
| VOID | |
| EFIAPI | |
| MmCoreInitializeMemoryAttributesTable ( | |
| VOID | |
| ) | |
| { | |
| MmInstallImageRecord (); | |
| DEBUG ((DEBUG_VERBOSE, "MM MemoryProtectionAttribute - 0x%016lx\n", mMemoryProtectionAttribute)); | |
| if ((mMemoryProtectionAttribute & EFI_MEMORY_ATTRIBUTES_RUNTIME_MEMORY_PROTECTION_NON_EXECUTABLE_PE_DATA) == 0) { | |
| return; | |
| } | |
| DEBUG_CODE_BEGIN (); | |
| if ( mImagePropertiesPrivateData.ImageRecordCount > 0) { | |
| DEBUG ((DEBUG_INFO, "MM - Total Runtime Image Count - 0x%x\n", mImagePropertiesPrivateData.ImageRecordCount)); | |
| DEBUG ((DEBUG_INFO, "MM - Dump Runtime Image Records:\n")); | |
| DumpImageRecords (&mImagePropertiesPrivateData.ImageRecordList); | |
| } | |
| DEBUG_CODE_END (); | |
| PublishMemoryAttributesTable (); | |
| return; | |
| } |