/** @file ACPI Platform Library HMAT @copyright Copyright 2016 - 2020 Intel Corporation.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ // // Statements that include other files // #include "AcpiPlatformLibLocal.h" #include #include #include // // The represented latency/bandwidth in 'System Locality Latency and Bandwidth // Information Structure' is expressed in multiples of Entry Base Unit. // Unit of latency is picoseconds and bandwidth is megabytes per second // Below #defines are be base units and values for calcualting the latency/bandwidth // For example: read latency of DDRT is 180ns which is 180000 picoseconds. // This is expressed as DDRT_LATENCY_BASE_UNIT of 1000 and DDRT_READ_LATENCY of 180 // #define MEMORY_LATENCY_BASE_UNIT 100 #define MEMORY_BANDWIDTH_BASE_UNIT 1 #define DDR2LMCACHE_LATENCY_BASE_UNIT 100 #define DDR2LMCACHE_BANDWIDTH_BASE_UNIT 1 #define XSOCKET_LATENCY_BASE_UNIT 100 #define XSOCKET_BANDWIDTH_BASE_UNIT 1 // // DDRT values // #define DDRT_ACCESS_LATENCY 0 #define DDRT_1LM_READ_LATENCY 2535 #define DDRT_2LM_READ_LATENCY 3285 #define DDRT_1LM_WRITE_LATENCY 2535 #define DDRT_2LM_WRITE_LATENCY 3285 #define DDRT_ACCESS_BANDWIDTH 0 #define DDRT_1LM_READ_BANDWIDTH 4625 #define DDRT_2LM_READ_BANDWIDTH 4625 #define DDRT_1LM_WRITE_BANDWIDTH 1375 #define DDRT_2LM_WRITE_BANDWIDTH 1375 // // X-SOCKET values // #define XSOCKET_DDRT_1LM_ACCESS_LATENCY 0 #define XSOCKET_DDRT_1LM_READ_LATENCY 3160 #define XSOCKET_DDRT_1LM_WRITE_LATENCY 3160 #define XSOCKET_DDRT_1LM_ACCESS_BANDWIDTH 0 #define XSOCKET_DDRT_1LM_READ_BANDWIDTH 4625 #define XSOCKET_DDRT_1LM_WRITE_BANDWIDTH 1375 #define XSOCKET_DDRT_2LM_ACCESS_LATENCY 0 #define XSOCKET_DDRT_2LM_READ_LATENCY 3885 #define XSOCKET_DDRT_2LM_WRITE_LATENCY 3885 #define XSOCKET_DDRT_2LM_ACCESS_BANDWIDTH 0 #define XSOCKET_DDRT_2LM_READ_BANDWIDTH 4625 #define XSOCKET_DDRT_2LM_WRITE_BANDWIDTH 1375 // // DDR values 1LM or flat mode // #define DDR_ACCESS_LATENCY 0 #define DDR_READ_LATENCY 760 #define DDR_WRITE_LATENCY 760 #define DDR_ACCESS_BANDWIDTH 0 #define DDR_READ_BANDWIDTH 17900 #define DDR_WRITE_BANDWIDTH 19100 // // X-Socket DDR values 1LM or flat mode // #define XSOCKET_DDR_ACCESS_LATENCY 0 #define XSOCKET_DDR_READ_LATENCY 1356 #define XSOCKET_DDR_WRITE_LATENCY 1356 #define XSOCKET_DDR_ACCESS_BANDWIDTH 0 #define XSOCKET_DDR_READ_BANDWIDTH 17900 #define XSOCKET_DDR_WRITE_BANDWIDTH 19100 // // DDR/X-Socket DDR values 2LM when acting as cache // #define DDR2LMCACHE_ACCESS_LATENCY 0 #define DDR2LMCACHE_READ_LATENCY 760 #define DDR2LMCACHE_WRITE_LATENCY 760 #define DDR2LMCACHE_ACCESS_BANDWIDTH 0 #define DDR2LMCACHE_READ_BANDWIDTH 17900 #define DDR2LMCACHE_WRITE_BANDWIDTH 12691 extern struct SystemMemoryMapHob *mSystemMemoryMap; extern EFI_IIO_UDS_PROTOCOL *mIioUds; extern SOCKET_MEMORY_CONFIGURATION mSocketMemoryConfiguration; extern CPU_CSR_ACCESS_VAR *mCpuCsrAccessVarPtr; UINT8 SkippedEntries = 0; typedef enum { DDR = 0x00, DDRT, DDR2LMCACHE } MemoryType; /** Dump HMAT Header @param [in] HdrPtr Pointer to HMAT Header @retval None **/ VOID DumpHeader ( EFI_ACPI_HETEROGENEOUS_MEMORY_ATTRIBUTE_TABLE_HEADER *HdrPtr ) { DEBUG ((DEBUG_INFO, "=========== HMAT header ==========\n")); DEBUG ((DEBUG_INFO, " Signature: %.4a\n", (CHAR8 *)&HdrPtr->Header.Signature)); DEBUG ((DEBUG_INFO, " Length: %d\n", HdrPtr->Header.Length)); DEBUG ((DEBUG_INFO, " Revision: %Xh\n", HdrPtr->Header.Revision)); DEBUG ((DEBUG_INFO, " Checksum: N/A - CHECKSUM ADDED LATER\n")); DEBUG ((DEBUG_INFO, " OemId: %.6a\n", HdrPtr->Header.OemId)); DEBUG ((DEBUG_INFO, " OemTableId: %.8a\n", (CHAR8 *)&HdrPtr->Header.OemTableId)); DEBUG ((DEBUG_INFO, " OemRevision: %Xh\n", HdrPtr->Header.OemRevision)); DEBUG ((DEBUG_INFO, " CreatorId: %.4a\n", (CHAR8 *)&HdrPtr->Header.CreatorId)); DEBUG ((DEBUG_INFO, " CreatorRevision: %Xh\n", HdrPtr->Header.CreatorRevision)); DEBUG ((DEBUG_INFO, "==================================\n")); DEBUG ((DEBUG_INFO, "\n")); } /** Dump MSARS Structure @param [in] MsarsPtr Pointer to MSARS Structure @retval None **/ VOID DumpMsars ( MEMORY_SUBSYSTEM_ADDRESS_RANGE_STRUCTURE *MsarsPtr ) { DEBUG ((DEBUG_INFO, "=========== MSARS Table =============================\n")); DEBUG ((DEBUG_INFO, " Type: %d\n", MsarsPtr->Type)); DEBUG ((DEBUG_INFO, " Length: %d\n", MsarsPtr->Length)); DEBUG ((DEBUG_INFO, " ProcessorDomainValid: %d\n", MsarsPtr->Flags.Bits.ProcessorDomainValid)); DEBUG ((DEBUG_INFO, " MemoryDomainValid: %d\n", MsarsPtr->Flags.Bits.MemoryDomainValid)); DEBUG ((DEBUG_INFO, " ReservationHint: %d\n", MsarsPtr->Flags.Bits.ReservationHint)); DEBUG ((DEBUG_INFO, " ProcessorProximityDomain: %Xh\n", MsarsPtr->ProcessorProximityDomain)); DEBUG ((DEBUG_INFO, " MemoryProximityDomain: %Xh\n", MsarsPtr->MemoryProximityDomain)); DEBUG ((DEBUG_INFO, " SystemPhysicalAddressRangeBase: %llXh\n", MsarsPtr->AddrBase)); DEBUG ((DEBUG_INFO, " SystemPhysicalAddressRangeLength: %llXh\n", MsarsPtr->AddrLength)); DEBUG ((DEBUG_INFO, "=====================================================\n")); DEBUG ((DEBUG_INFO, "\n")); } /** Dump HSCIS Structure @param [in] MscisPtr Pointer to HSCIS Structure @retval None **/ VOID DumpMscis ( MEMORY_SIDE_CACHE_INFORMATION_STRUCTURE *MscisPtr ) { UINT8 SmbiosHandleIndex; DEBUG ((DEBUG_INFO, "=========== MSCIS Table =============================\n")); DEBUG ((DEBUG_INFO, " Type: %d\n", MscisPtr->Type)); DEBUG ((DEBUG_INFO, " Length: %d\n", MscisPtr->Length)); DEBUG ((DEBUG_INFO, " MemoryProximityDomain: %Xh\n", MscisPtr->MemoryProximityDomain)); DEBUG ((DEBUG_INFO, " MemorySideCacheSize: %llXh\n", MscisPtr->MemorySideCacheSize)); DEBUG ((DEBUG_INFO, " TotalCacheLevels: %d\n", MscisPtr->CacheAttributes.Bits.TotalCacheLevels)); DEBUG ((DEBUG_INFO, " CacheLevel: %d\n", MscisPtr->CacheAttributes.Bits.CacheLevel)); DEBUG ((DEBUG_INFO, " CacheAssociativity: %d\n", MscisPtr->CacheAttributes.Bits.CacheAssociativity)); DEBUG ((DEBUG_INFO, " WritePolicy: %d\n", MscisPtr->CacheAttributes.Bits.WritePolicy)); DEBUG ((DEBUG_INFO, " CacheLineSize: %d\n", MscisPtr->CacheAttributes.Bits.CacheLineSize)); DEBUG ((DEBUG_INFO, " NumSmbiosHandles: %d\n", MscisPtr->NumSmbiosHandles)); for (SmbiosHandleIndex = 0; SmbiosHandleIndex < MscisPtr->NumSmbiosHandles; SmbiosHandleIndex++) { DEBUG ((DEBUG_INFO, " SmbiosHandle[%d]: %xh\n", SmbiosHandleIndex + 1 ,MscisPtr->SmbiosHandles[SmbiosHandleIndex])); } DEBUG ((DEBUG_INFO, "=====================================================\n")); DEBUG ((DEBUG_INFO, "\n")); } /** Dump LBIS Structure @param [in] LbisPtr Pointer to LBIS Structure @retval None **/ VOID DumpLbis ( LATENCY_BANDWIDTH_INFO_STRUCTURE *LbisPtr ) { UINTN Index, Index1; UINT32 *TargetProximityDomainList; UINT16 *Entry; DEBUG ((DEBUG_INFO, "=========== MLBIS Table =============================\n")); DEBUG ((DEBUG_INFO, " Type: %d\n", LbisPtr->Type)); DEBUG ((DEBUG_INFO, " Length: %d\n", LbisPtr->Length)); DEBUG ((DEBUG_INFO, " Flags: %d\n", LbisPtr->Flags)); DEBUG ((DEBUG_INFO, " DataType: %d\n", LbisPtr->DataType)); DEBUG ((DEBUG_INFO, " InitiatorProximityDomainsNumber: %d\n", LbisPtr->InitiatorProximityDomainsNumber)); DEBUG ((DEBUG_INFO, " TargetProximityDomainsNumber: %d\n", LbisPtr->TargetProximityDomainsNumber)); DEBUG ((DEBUG_INFO, " EntryBaseUnit: %lXh\n", LbisPtr->EntryBaseUnit)); DEBUG ((DEBUG_INFO, " InitiatorProximityDomainList:\n")); for (Index = 0; Index < LbisPtr->InitiatorProximityDomainsNumber; Index++) { DEBUG ((DEBUG_INFO, " %d ", LbisPtr->InitiatorProximityDomainList[Index])); } TargetProximityDomainList = (UINT32*)&(LbisPtr->InitiatorProximityDomainList[Index]); DEBUG ((DEBUG_INFO, "\n")); DEBUG ((DEBUG_INFO, " TargetProximityDomainList:\n")); for (Index = 0; Index < LbisPtr->TargetProximityDomainsNumber; Index++) { DEBUG ((DEBUG_INFO, " %d ", TargetProximityDomainList[Index])); } Entry = (UINT16*)(TargetProximityDomainList + Index); DEBUG ((DEBUG_INFO, "\n")); DEBUG ((DEBUG_INFO, "RelativeDistanceEntry:\n")); for (Index = 0; Index < LbisPtr->InitiatorProximityDomainsNumber; Index++) { for (Index1 = 0; Index1 < LbisPtr->TargetProximityDomainsNumber; Index1++) { DEBUG ((DEBUG_INFO, " %d ", *(Entry + (Index * LbisPtr->TargetProximityDomainsNumber) + Index1))); } DEBUG ((DEBUG_INFO, "\n")); } DEBUG ((DEBUG_INFO, "\n")); DEBUG ((DEBUG_INFO, "=====================================================\n")); DEBUG ((DEBUG_INFO, "\n")); } /** Dump HMAT table @param [in] HmatAcpiTable Pointer to HMAT table. @retval None **/ VOID DumpHmat ( EFI_ACPI_HETEROGENEOUS_MEMORY_ATTRIBUTE_TABLE *HmatAcpiTable ) { UINT32 TotalLength = HmatAcpiTable->HmatHeader.Header.Length; UINT8 *Table = (UINT8 *)HmatAcpiTable; UINT32 Length; UINT16 Type; DumpHeader (&HmatAcpiTable->HmatHeader); Length = sizeof (EFI_ACPI_HETEROGENEOUS_MEMORY_ATTRIBUTE_TABLE_HEADER); Table += Length; TotalLength -= Length; // // Dump tables // while (TotalLength) { Type = ((MEMORY_SUBSYSTEM_ADDRESS_RANGE_STRUCTURE *)Table)->Type; // // For HBM will need to add LBIS structure // if (Type == MEMORY_SUBSYSTEM_ADDRESS_RANGE_STRUCTURE_TYPE) { DumpMsars ((MEMORY_SUBSYSTEM_ADDRESS_RANGE_STRUCTURE *)Table); } else if (Type == MEMORY_SIDE_CACHE_INFORMATION_STRUCTURE_TYPE) { DumpMscis ((MEMORY_SIDE_CACHE_INFORMATION_STRUCTURE *)Table); } else { DumpLbis ((LATENCY_BANDWIDTH_INFO_STRUCTURE *)Table); } // // Goto next entry // Length = ((MEMORY_SUBSYSTEM_ADDRESS_RANGE_STRUCTURE *)Table)->Length; Table += Length; TotalLength -= Length; } } /** Update SMBIOS handles and number of SMBIOS handles which is related to specified NodeId to MEMORY_DOMAIN_LIST_INFO SMBIOS Type 17 handles are formed in sequence order. First handle is S0 C0 D0, Second handle is S0 C0 D1 and so on. So we loop in this order to find the handles of DDR which is part of the specific 2LM cache memory. @param [in] HmatData Points to HMAT structure @param [in] NodeId SMBIOS handles related to this NodeId will be retrieved @retval None **/ VOID UpdateSmbiosHandles( IN HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData, IN UINT32 NodeId, IN UINT8 Socket, IN UINT32 ImcBitMap ) { EFI_STATUS Status; STATIC EFI_SMBIOS_PROTOCOL *Smbios = NULL; SMBIOS_TABLE_TYPE17 *Type17Record; EFI_SMBIOS_TABLE_HEADER *SmbiosRecord; EFI_SMBIOS_HANDLE SmbiosHandle; EFI_SMBIOS_TYPE SmbiosType; UINT8 Loop = 1; UINT8 SlotsPerImc = MAX_DIMM * MAX_MC_CH; UINT8 DimmsPerImc = 0; Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID **) &Smbios); if (EFI_ERROR (Status)) { return ; } SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED; SmbiosType = 17; // // Adjust ImcBitMap according to the socket number // if (Socket != 0) { ImcBitMap <<= (Socket * SlotsPerImc); } if (!PcdGetBool (PcdHalfWidth)) { DimmsPerImc = SlotsPerImc; } else { DimmsPerImc = SlotsPerImc - 1; } do { Status = Smbios->GetNext(Smbios, &SmbiosHandle, &SmbiosType, &SmbiosRecord, NULL); if (!EFI_ERROR (Status) && (SmbiosHandle != SMBIOS_HANDLE_PI_RESERVED)) { Type17Record = (SMBIOS_TABLE_TYPE17 *)SmbiosRecord; // // 1) check whether the Memory device of this record is cache capable(ie, 2LM DDR cache) and // 2) this memory handle is to the current looping IMC // if ((Type17Record->TypeDetail.CacheDram) && (ImcBitMap & BIT0)) { HmatData->MemoryDomainList[NodeId].SmbiosHandles[HmatData->MemoryDomainList[NodeId].NumSmbiosHandles] = Type17Record->Hdr.Handle ; HmatData->MemoryDomainList[NodeId].NumSmbiosHandles++; } } // // if loop done for this IMC then move for next // if (Loop % DimmsPerImc == 0) { ImcBitMap >>= 1; } ++Loop; } while (!EFI_ERROR (Status) && (SmbiosHandle != SMBIOS_HANDLE_PI_RESERVED) && (ImcBitMap != 0)); } /** Remove slack by moving this substructure lower which removes unused memory. @param [in] SlackSize Points to cumulative unused memory size @param [in] Src Points to memory to move @param [in] SrcLen Size of memory to move @retval None **/ VOID RemoveSlack ( IN UINTN *SlackSize, IN VOID *Src, IN UINTN SrcLen ) { VOID *Dst; Dst = (UINT8 *)Src - (*SlackSize); CopyMem (Dst, Src, SrcLen); } /** Calculate the Memory Proximity Domain Number and generate its associate list. @param [in, out] HmatData Pointer to HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE to be filled @retval None. **/ VOID GetMemoryDomains ( IN OUT HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData ) { UINT64 MemorySideCacheSize; UINT32 NodeId; UINT32 LastDomainId = 0; UINT8 Index; UINT8 PrevIndex; UINT8 LastIndex = 0; UINT8 Mc; UINT8 Socket; UINT8 McBitmap[EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS]; UINT32 PmemEntry[EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS]; UINT64 MemoryAddressStore[EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS]; UINT64 MemoryAddress; UINT8 MaxEnabledImc = 0; UINTN ImcIndex; UINT8 MemSocketBitmap = 0; UINT8 NoMemSocketBitmap; BOOLEAN SkipEntry; BOOLEAN UpdateSmbiosHandle = FALSE; EFI_STATUS Status = EFI_SUCCESS; DYNAMIC_SI_LIBARY_PROTOCOL2 *DynamicSiLibraryProtocol2 = NULL; Status = gBS->LocateProtocol (&gDynamicSiLibraryProtocol2Guid, NULL, (VOID **) &DynamicSiLibraryProtocol2); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); return; } HmatData->MemoryDomainNumber = 0; ZeroMem (McBitmap, sizeof(McBitmap)); ZeroMem (MemoryAddressStore, sizeof(MemoryAddressStore)); // // Memory Proximity Domain must match the Domain in the SRAT Memory Affinity structure // for (Index = 0; Index < mSystemMemoryMap->numberEntries; Index++) { // // Skip any memory region marked reserved // if (DynamicSiLibraryProtocol2->IsMemTypeReserved (mSystemMemoryMap->Element[Index].Type)) { continue; } if (!DynamicSiLibraryProtocol2->IsMemTypeVolatile (mSystemMemoryMap->Element[Index].Type)) { // // Skip all non volatile regions // PmemEntry[SkippedEntries] = Index; SkippedEntries++; continue; } SkipEntry = FALSE; MemoryAddress = LShiftU64 (mSystemMemoryMap->Element[Index].BaseAddress, MEM_ADDR_SHFT_VAL); // // Skip duplicate entries // if (Index) { for (PrevIndex = 0; PrevIndex < Index; PrevIndex++) { if (MemoryAddress == MemoryAddressStore[PrevIndex]) { SkipEntry = TRUE; break; } } } if (SkipEntry) { continue; } MemoryAddressStore[Index] = MemoryAddress; LastIndex = Index; for (ImcIndex = 0, MaxEnabledImc = 0; ImcIndex < MAX_IMC; ImcIndex++) { if (mSystemMemoryMap->Socket[mSystemMemoryMap->Element[Index].SocketId].imcEnabled[ImcIndex] != 0) { MaxEnabledImc ++; } } // // Update bitmap for sockets with memory populated // MemSocketBitmap |= BIT0 << mSystemMemoryMap->Element[Index].SocketId; // // Get memory domain (must match SRAT memory domain) // NodeId = ProximityDomainOf ( mSystemMemoryMap->Element[Index].SocketId, mSystemMemoryMap->Element[Index].Type, MaxEnabledImc, HmatData->SncEnabled, HmatData->SncNumOfCluster, mSystemMemoryMap->Element[Index].ImcInterBitmap, mSystemMemoryMap->volMemMode, LastDomainId ); NodeId = (NodeId * HmatData->VirtualNumOfCluster) + (Index % HmatData->VirtualNumOfCluster); if (LastDomainId < NodeId) { LastDomainId = NodeId; } if (NodeId >= EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS) { DEBUG ((DEBUG_ERROR, "[ACPI] (HMAT) ERROR: Invalid Memory Proximity Domain (0x%x)\n", NodeId)); ASSERT (FALSE); } // // Update map of indexes included in this memory domain // HmatData->MemoryDomainList[NodeId].MemMapIndexMap |= LShiftU64 (BIT0, Index); // // Update Memory domain count and set domain to valid // Prevent counting repeated entries with the same Proximity Domain ID // if ((Index == 0) || (HmatData->MemoryDomainList[NodeId].Valid == 0)) { HmatData->MemoryDomainNumber++; HmatData->MemoryDomainList[NodeId].PhysicalSocketId = mSystemMemoryMap->Element[Index].SocketId; HmatData->MemoryDomainList[NodeId].Valid = 1; McBitmap[NodeId] = 0; } if (HmatData->MemoryDomainList[NodeId].Valid) { if (mSystemMemoryMap->Element[Index].Type == MemType2lmDdrCacheMemoryMode) { // // Set up side cache info for 2LM // HmatData->MemoryDomainList[NodeId].Cacheable = 1; // // Calculate MemorySideCacheSize // MemorySideCacheSize for each MC is the DDR4 memSize // MemorySideCacheSize = 0; UpdateSmbiosHandle = FALSE; for (Mc = 0; Mc < MAX_IMC; Mc++) { if ((mSystemMemoryMap->Element[Index].ImcInterBitmap & (BIT0 << Mc)) && !(McBitmap[NodeId] & (BIT0 << Mc))) { UpdateSmbiosHandle = TRUE; if (mSystemMemoryMap->volMemMode == VOL_MEM_MODE_2LM) { MemorySideCacheSize += mSystemMemoryMap->Socket[mSystemMemoryMap->Element[Index].SocketId].imc[Mc].MemSize; } else{ // VOL_MEM_MODE_MIX_1LM2LM MemorySideCacheSize += mSystemMemoryMap->DdrCacheSize[mSystemMemoryMap->Element[Index].SocketId][Mc]; } } } if (UpdateSmbiosHandle) { UpdateSmbiosHandles ( HmatData, NodeId, mSystemMemoryMap->Element[Index].SocketId, mSystemMemoryMap->Element[Index].ImcInterBitmap ); } HmatData->MemoryDomainList[NodeId].MemorySideCacheSize += MemorySideCacheSize; } McBitmap[NodeId] |= mSystemMemoryMap->Element[Index].ImcInterBitmap; } DEBUG ((DEBUG_INFO, "MemoryDomainList[%x] Valid = %x Cacheable = %x MemorySideCacheSize = %x\n", NodeId, HmatData->MemoryDomainList[NodeId].Valid, HmatData->MemoryDomainList[NodeId].Cacheable, HmatData->MemoryDomainList[NodeId].MemorySideCacheSize )); } // // Update LastDomainId for enabled sockets with no memory // NoMemSocketBitmap = mCpuCsrAccessVarPtr->socketPresentBitMap & ~MemSocketBitmap; for (Socket = 0; Socket < MAX_SOCKET; Socket++) { if ((BIT0 << Socket) & NoMemSocketBitmap) { LastDomainId += HmatData->SncNumOfCluster; } } // // Add skipped entries // for (Index = 0; Index < SkippedEntries; Index++) { SkipEntry = FALSE; MemoryAddress = LShiftU64 (mSystemMemoryMap->Element[PmemEntry[Index]].BaseAddress, MEM_ADDR_SHFT_VAL); // // Skip duplicate entries // for (PrevIndex = 0; PrevIndex < LastIndex; PrevIndex++) { if (MemoryAddress == MemoryAddressStore[PrevIndex]) { SkipEntry = TRUE; break; } } if (SkipEntry) { continue; } MemoryAddressStore[LastIndex++] = MemoryAddress; // // Get memory domain (must match SRAT memory domain) // NodeId = ProximityDomainOf ( mSystemMemoryMap->Element[PmemEntry[Index]].SocketId, mSystemMemoryMap->Element[PmemEntry[Index]].Type, MaxEnabledImc, HmatData->SncEnabled, HmatData->SncNumOfCluster, mSystemMemoryMap->Element[PmemEntry[Index]].ImcInterBitmap, mSystemMemoryMap->volMemMode, LastDomainId ); NodeId = (NodeId * HmatData->VirtualNumOfCluster) + (Index % HmatData->VirtualNumOfCluster); if (LastDomainId < NodeId) { LastDomainId = NodeId; } if (NodeId >= EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS) { DEBUG ((DEBUG_ERROR, "[ACPI] (HMAT) ERROR: Invalid Memory Proximity Domain (0x%x)\n", NodeId)); ASSERT (FALSE); } // // Update map of indexes included in this memory domain // HmatData->MemoryDomainList[NodeId].MemMapIndexMap |= LShiftU64 (BIT0, PmemEntry[Index]); // // Update Memory domain count and set domain to valid // Prevent counting repeated entries with the same Proximity Domain ID // if ((PmemEntry[Index] == 0) || (HmatData->MemoryDomainList[NodeId].Valid == 0)) { HmatData->MemoryDomainNumber++; HmatData->MemoryDomainList[NodeId].PhysicalSocketId = mSystemMemoryMap->Element[PmemEntry[Index]].SocketId; HmatData->MemoryDomainList[NodeId].Valid = 1; } DEBUG ((DEBUG_INFO, "[ACPI] (HMAT) MemoryDomainList[%x] Valid = %x Cacheable = %x MemorySideCacheSize = %x\n", NodeId, HmatData->MemoryDomainList[NodeId].Valid, HmatData->MemoryDomainList[NodeId].Cacheable, HmatData->MemoryDomainList[NodeId].MemorySideCacheSize )); } } /** Calculate the Processor Proximity Domain Number and generate its associate list. @param [in, out] HmatData Pointer to HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE to be filled @retval None. **/ VOID GetProcessorDomains ( IN OUT HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData ) { UINT32 NodeId; UINT8 Index; INTN FirstImc; UINT8 SocketId; UINT8 SocketLogicalId; UINT8 MemSocketBitmap; UINT8 NoMemSocketBitmap; EFI_STATUS Status = EFI_SUCCESS; DYNAMIC_SI_LIBARY_PROTOCOL2 *DynamicSiLibraryProtocol2 = NULL; Status = gBS->LocateProtocol (&gDynamicSiLibraryProtocol2Guid, NULL, (VOID **) &DynamicSiLibraryProtocol2); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); return; } MemSocketBitmap = 0; NoMemSocketBitmap = 0; HmatData->ProcessorDomainNumber = 0; // // Processor Proximity Domain must match the Domain in the SRAT APIC or X2APIC Affinity Structure // for (Index = 0; Index < mSystemMemoryMap->numberEntries; Index++) { // // Skip any memory region marked reserved or FPGA // if (DynamicSiLibraryProtocol2->IsMemTypeReserved (mSystemMemoryMap->Element[Index].Type) || DynamicSiLibraryProtocol2->IsMemTypeFpga (mSystemMemoryMap->Element[Index].Type)) { continue; } SocketId = mSystemMemoryMap->Element[Index].SocketId; SocketLogicalId = GetSocketLogicalId (SocketId); // // Get processor proximity domain // if (HmatData->SncEnabled || HmatData->VirtualNumaEnabled) { FirstImc = LowBitSet32 (mSystemMemoryMap->Element[Index].ImcInterBitmap); if (FirstImc == -1) { FirstImc = 0; } // // Find the 1st IMC according the interbitmap // if (MAX_IMC <= HmatData->SncNumOfCluster) { NodeId = (SocketLogicalId * HmatData->SncNumOfCluster) + (UINT32)FirstImc; } else { NodeId = (SocketLogicalId * HmatData->SncNumOfCluster) + ((UINT32)FirstImc) / HmatData->SncNumOfCluster; } NodeId = (NodeId * HmatData->VirtualNumOfCluster) + (Index % HmatData->VirtualNumOfCluster); DEBUG ((DEBUG_INFO, "%a: SocketId: 0x%x SncNumOfCluster: 0x%x ImcInterBitmap:0x%x and NodeId:0x%x\n", __FUNCTION__, mSystemMemoryMap->Element[Index].SocketId, HmatData->SncNumOfCluster, mSystemMemoryMap->Element[Index].ImcInterBitmap, NodeId)); } else { NodeId = SocketLogicalId; } if (NodeId >= EFI_ACPI_HMAT_NUMBER_OF_PROCESSOR_DOMAINS) { DEBUG ((DEBUG_ERROR, "[ACPI] (HMAT) ERROR: Invalid Processor Proximity Domain (0x%x)\n", NodeId)); return; } // // Update processor domain count and set domain to valid // Prevent counting repeated entries with the same Proximity Domain ID // if ((Index == 0) || (HmatData->ProcessorDomainList[NodeId] == 0)) { HmatData->ProcessorDomainNumber++; HmatData->ProcessorDomainSocketIdList[NodeId] = SocketId; HmatData->ProcessorDomainList[NodeId] = 1; // Domain is valid MemSocketBitmap |= (BIT0 << mSystemMemoryMap->Element[Index].SocketId); } DEBUG ((DEBUG_INFO, "[ACPI] (HMAT) ProcessorDomainList[%x] Valid = %x\n", NodeId, HmatData->ProcessorDomainList[NodeId])); } // // Update processor domain for enabled sockets without memory // NoMemSocketBitmap = mCpuCsrAccessVarPtr->socketPresentBitMap & ~MemSocketBitmap; for (SocketId = 0; SocketId < MAX_SOCKET; SocketId++) { if ((BIT0 << SocketId) & NoMemSocketBitmap) { for (Index = 0; Index < (HmatData->SncNumOfCluster * HmatData->VirtualNumOfCluster); Index++) { NodeId = GetSocketLogicalId (SocketId) * HmatData->SncNumOfCluster * HmatData->VirtualNumOfCluster + Index; HmatData->ProcessorDomainNumber++; HmatData->ProcessorDomainSocketIdList[NodeId] = SocketId; HmatData->ProcessorDomainList[NodeId] = 1; } } } } /** Initialize HMAT Data to be consumed when populating tables. This Functions allocates buffer for HMAT Data, so it is caller responsibility to free it. @param None. @retval Pointer to allocated HMAT Data if it was initialized correctly. @retval NULL if not allocated and not initialized correctly. **/ UINTN * InitializeHmatData ( VOID ) { HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData = NULL; HmatData = AllocateZeroPool (sizeof (HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE)); if (HmatData == NULL) { DEBUG ((DEBUG_ERROR, "[ACPI] (HMAT) ERROR: Could not allocate HmatData structure pointer\n")); return (UINTN *) HmatData; } if (mIioUds->IioUdsPtr->SystemStatus.OutSncEn) { HmatData->SncEnabled = 1; HmatData->SncNumOfCluster = mIioUds->IioUdsPtr->SystemStatus.OutNumOfCluster; } else { HmatData->SncNumOfCluster = 1; } if (mSystemMemoryMap->VirtualNumaEnable) { HmatData->VirtualNumaEnabled = 1; HmatData->VirtualNumOfCluster = mSystemMemoryMap->VirtualNumOfCluster; } else { HmatData->VirtualNumOfCluster = 1; } GetProcessorDomains (HmatData); GetMemoryDomains (HmatData); return (UINTN *)HmatData; } /** Patch HMAT MSARS substructure. @param [in, out] HmatAcpiTable Points to HMAT table to be modified @param [in, out] SlackSize Points to cumulative slack size @param [in] HmatData HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE to be consumed @retval None **/ VOID PatchHmatMsars ( IN OUT EFI_ACPI_HETEROGENEOUS_MEMORY_ATTRIBUTE_TABLE *HmatAcpiTable, IN OUT UINTN *SlackSize, IN HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData ) { UINT32 Index; UINT32 PrevIndex; UINT32 MemoryNodeId; UINT32 ProcessorNodeId; UINT32 Count = 0; UINT32 UnusedCount; UINT8 MemoryNodeValid; UINT16 ProcessorNodeValid; UINT64 MemoryAddress; BOOLEAN SkipEntry; INTN FirstImc; UINT8 SocketLogicalId; EFI_STATUS Status = EFI_SUCCESS; DYNAMIC_SI_LIBARY_PROTOCOL2 *DynamicSiLibraryProtocol2 = NULL; Status = gBS->LocateProtocol (&gDynamicSiLibraryProtocol2Guid, NULL, (VOID **) &DynamicSiLibraryProtocol2); if (EFI_ERROR (Status)) { ASSERT_EFI_ERROR (Status); return; } DEBUG ((DEBUG_INFO, "[ACPI] (HMAT) Populating Memory Subsystem Address Range Structure\n")); for (Index = 0; Index < mSystemMemoryMap->numberEntries; Index++) { // // Skip any memory region marked reserved // DEBUG ((DEBUG_INFO, "[ACPI] mSystemMemoryMap->Element[%d].Type = 0x%x\n",Index, mSystemMemoryMap->Element[Index].Type)); if (DynamicSiLibraryProtocol2->IsMemTypeReserved (mSystemMemoryMap->Element[Index].Type)) { continue; } if (Count >= EFI_ACPI_HMAT_MSARS_COUNT) { DEBUG ((DEBUG_INFO, "[ACPI] (HMAT) Invalid MSARS entry with Index = 0x%x\n", Count)); ASSERT (FALSE); break; } SkipEntry = FALSE; // // Skip duplicate entries // MemoryAddress = LShiftU64 (mSystemMemoryMap->Element[Index].BaseAddress, MEM_ADDR_SHFT_VAL); if (Count) { for (PrevIndex = 0; PrevIndex < Count; PrevIndex++) { if (MemoryAddress == HmatAcpiTable->Msars[PrevIndex].AddrBase) { SkipEntry = TRUE; break; } } } if (SkipEntry) { continue; } // // Find memory domain for this index // for (MemoryNodeId = 0; MemoryNodeId < EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS; MemoryNodeId++) { if (HmatData->MemoryDomainList[MemoryNodeId].MemMapIndexMap & LShiftU64(BIT0, Index)) { break; } } // // Fill in valid field // if (MemoryNodeId < EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS) { MemoryNodeValid = HmatData->MemoryDomainList[MemoryNodeId].Valid; } else { MemoryNodeValid = 0; } // // Get processor domain for this index // if (DynamicSiLibraryProtocol2->IsMemTypeFpga (mSystemMemoryMap->Element[Index].Type)) { ProcessorNodeValid = 0; // There is no processor for FPGA. ProcessorNodeId = 0; // This field will be ingored for FPGA. } else { SocketLogicalId = GetSocketLogicalId (mSystemMemoryMap->Element[Index].SocketId); if (HmatData->SncEnabled || HmatData->VirtualNumaEnabled) { FirstImc = LowBitSet32 (mSystemMemoryMap->Element[Index].ImcInterBitmap); if (FirstImc == -1) { FirstImc = 0; } // // Find the 1st IMC according the interbitmap // if (MAX_IMC <= HmatData->SncNumOfCluster) { ProcessorNodeId = (SocketLogicalId * HmatData->SncNumOfCluster) + (UINT32)FirstImc; } else { ProcessorNodeId = (SocketLogicalId * HmatData->SncNumOfCluster) + ((UINT32)FirstImc) / HmatData->SncNumOfCluster; } ProcessorNodeId = (ProcessorNodeId * HmatData->VirtualNumOfCluster) + (Index % HmatData->VirtualNumOfCluster); DEBUG ((DEBUG_INFO, "%a: SocketId: 0x%x SncNumOfCluster: 0x%x ImcInterBitmap:0x%x and NodeId:0x%x \n", __FUNCTION__, mSystemMemoryMap->Element[Index].SocketId, HmatData->SncNumOfCluster, mSystemMemoryMap->Element[Index].ImcInterBitmap, ProcessorNodeId)); } else { ProcessorNodeId = SocketLogicalId; } // // Fill in valid field // if (ProcessorNodeId < EFI_ACPI_HMAT_NUMBER_OF_PROCESSOR_DOMAINS) { ProcessorNodeValid = HmatData->ProcessorDomainList[ProcessorNodeId]; } else { ProcessorNodeValid = 0; } } // // The HOB has base addr in 64 MB chunks // HmatAcpiTable->Msars[Count].AddrBase = MemoryAddress; HmatAcpiTable->Msars[Count].AddrLength = LShiftU64 (mSystemMemoryMap->Element[Index].ElementSize, MEM_ADDR_SHFT_VAL); HmatAcpiTable->Msars[Count].Flags.Value = 0; // // Fill in Proximity Domains // HmatAcpiTable->Msars[Count].ProcessorProximityDomain = ProcessorNodeId; HmatAcpiTable->Msars[Count].Flags.Bits.ProcessorDomainValid = ProcessorNodeValid; HmatAcpiTable->Msars[Count].MemoryProximityDomain = MemoryNodeId; HmatAcpiTable->Msars[Count].Flags.Bits.MemoryDomainValid = MemoryNodeValid; DEBUG ((DEBUG_INFO, "[ACPI] (HMAT) MSARS[%x] AddrBase = 0x%lx, AddrLength = 0x%lx, MemDomain = %x, ProcDomain = %x\n", Count, HmatAcpiTable->Msars[Count].AddrBase, HmatAcpiTable->Msars[Count].AddrLength, MemoryNodeId, ProcessorNodeId )); Count++; } // // Update SlackSize for unused MSARS entries // UnusedCount = EFI_ACPI_HMAT_MSARS_COUNT - Count; *SlackSize += UnusedCount * sizeof(HmatAcpiTable->Msars[0]); } /** Prints HMAT LATENCY_BANDWIDTH_INFO_STRUCTURE for debug purposes. @param [in] Lbis Points to HMAT LATENCY_BANDWIDTH_INFO_STRUCTURE to be printed. @retval None. **/ VOID PrintLbisHmat ( IN LATENCY_BANDWIDTH_INFO_STRUCTURE *Lbis ) { UINT32 Row = 0; UINT32 Col = 0; UINT32 RowBaseIndex = 0; UINT16 *Entry; Entry = (UINT16 *) Lbis->RelativeDistanceEntry; DEBUG ((DEBUG_INFO, "[ACPI] (HMAT) SLLBIS Data: ")); DEBUG ((DEBUG_INFO, "InitiatorNumber = %d, TargetNumber = %d. ", Lbis->InitiatorProximityDomainsNumber, Lbis->TargetProximityDomainsNumber)); DEBUG ((DEBUG_INFO, "Entry Base Unit = %d \n", Lbis->EntryBaseUnit)); DEBUG ((DEBUG_INFO, " ")); for (Col = 0; Col < Lbis->TargetProximityDomainsNumber; Col++) { DEBUG ((DEBUG_INFO, "%02d ", Lbis->TargetProximityDomainList[Col])); } DEBUG ((DEBUG_INFO, "\n")); for (Row = 0; Row < Lbis->InitiatorProximityDomainsNumber; Row++) { RowBaseIndex = Row * Lbis->TargetProximityDomainsNumber; DEBUG ((DEBUG_INFO, "%02d ", Lbis->InitiatorProximityDomainList[Row])); for (Col = 0; Col < Lbis->TargetProximityDomainsNumber; Col++) { DEBUG ((DEBUG_INFO, "%08d ", Entry[RowBaseIndex + Col])); } DEBUG ((DEBUG_INFO, "\n")); } } /** Get the Read/Write/Access bandwidth value of the memory type. @param [in] DataType Type of bandwidth value that is required. (Read/Write/Access) @param [in] Type Type of memory for which Bandwidth is required. (DDR/DDRT) @retval Value of the bandwidth associated with memory type. @retval 0xFF Input parameters are invalid. **/ UINT16 GetMemoryBandWidth ( IN UINT8 DataType, IN MemoryType Type, IN UINT8 volMemMode ) { if (Type == DDRT) { // MemoryType == DDRT switch (DataType) { case EFI_ACPI_HMAT_ACCESS_BANDWIDTH : return DDRT_ACCESS_BANDWIDTH; case EFI_ACPI_HMAT_READ_BANDWIDTH : if (volMemMode == VOL_MEM_MODE_1LM) { return DDRT_1LM_READ_BANDWIDTH; } else { return DDRT_2LM_READ_BANDWIDTH; } case EFI_ACPI_HMAT_WRITE_BANDWIDTH : if (volMemMode == VOL_MEM_MODE_1LM) { return DDRT_1LM_WRITE_BANDWIDTH; } else { return DDRT_2LM_WRITE_BANDWIDTH; } default: return 0xFF; } } else if (Type == DDR2LMCACHE) { switch (DataType) { case EFI_ACPI_HMAT_ACCESS_BANDWIDTH : return DDR2LMCACHE_ACCESS_BANDWIDTH; case EFI_ACPI_HMAT_READ_BANDWIDTH : return DDR2LMCACHE_READ_BANDWIDTH; case EFI_ACPI_HMAT_WRITE_BANDWIDTH : return DDR2LMCACHE_WRITE_BANDWIDTH; default: return 0xFF; } } else { switch (DataType) { case EFI_ACPI_HMAT_ACCESS_BANDWIDTH : return DDR_ACCESS_BANDWIDTH; case EFI_ACPI_HMAT_READ_BANDWIDTH : return DDR_READ_BANDWIDTH; case EFI_ACPI_HMAT_WRITE_BANDWIDTH : return DDR_WRITE_BANDWIDTH; default: return 0xFF; } } } /** Get the Read/Write/Access bandwidth value of the memory type across sockets. @param [in] DataType Type of bandwidth value that is required. (Read/Write/Access) @param [in] Type Type of memory for which Bandwidth is required. (DDR/DDRT) @retval Value of the bandwidth associated with memory type. @retval 0xFF Input parameters are invalid. **/ UINT16 GetXSocketMemoryBandWidth ( IN UINT8 DataType, IN MemoryType Type, IN UINT8 volMemMode ) { if (Type == DDRT) { // MemoryType == DDRT switch (DataType) { case EFI_ACPI_HMAT_ACCESS_BANDWIDTH: return DDRT_ACCESS_BANDWIDTH; case EFI_ACPI_HMAT_READ_BANDWIDTH: if (volMemMode == VOL_MEM_MODE_1LM) { return XSOCKET_DDRT_1LM_READ_BANDWIDTH; } else { return XSOCKET_DDRT_2LM_READ_BANDWIDTH; } case EFI_ACPI_HMAT_WRITE_BANDWIDTH: if (volMemMode == VOL_MEM_MODE_1LM) { return XSOCKET_DDRT_1LM_WRITE_BANDWIDTH; } else { return XSOCKET_DDRT_2LM_WRITE_BANDWIDTH; } default: return 0xFF; } } else if (Type == DDR2LMCACHE) { switch (DataType) { case EFI_ACPI_HMAT_ACCESS_BANDWIDTH: return DDR2LMCACHE_ACCESS_BANDWIDTH; case EFI_ACPI_HMAT_READ_BANDWIDTH: return DDR2LMCACHE_READ_BANDWIDTH; case EFI_ACPI_HMAT_WRITE_BANDWIDTH: return DDR2LMCACHE_WRITE_BANDWIDTH; default: return 0xFF; } } else { switch (DataType) { case EFI_ACPI_HMAT_ACCESS_BANDWIDTH: return XSOCKET_DDR_ACCESS_BANDWIDTH; case EFI_ACPI_HMAT_READ_BANDWIDTH: return XSOCKET_DDR_READ_BANDWIDTH; case EFI_ACPI_HMAT_WRITE_BANDWIDTH: return XSOCKET_DDR_WRITE_BANDWIDTH; default: return 0xFF; } } } /** Get the Read/Write/Access Latency value of the memory type. @param [in] DataType Type of latency value that is required (Read/Write/Access) @param [in] Type Type of memory for which Latency is required. (DDR/DDRT) @retval Value of the Latency associated with memory type. @retval 0xFF Input parameters are invalid. **/ UINT16 GetMemoryLatency ( IN UINT8 DataType, IN MemoryType Type, IN UINT8 volMemMode ) { if (Type == DDRT) { // MemoryType == DDRT switch (DataType) { case EFI_ACPI_HMAT_ACCESS_LATENCY : return DDRT_ACCESS_LATENCY; case EFI_ACPI_HMAT_READ_LATENCY : if (volMemMode == VOL_MEM_MODE_1LM) { return DDRT_1LM_READ_LATENCY; } else { return DDRT_2LM_READ_LATENCY; } case EFI_ACPI_HMAT_WRITE_LATENCY : if (volMemMode == VOL_MEM_MODE_1LM) { return DDRT_1LM_WRITE_LATENCY; } else { return DDRT_2LM_WRITE_LATENCY; } default: return 0xFF; } } else if (Type == DDR2LMCACHE){ switch (DataType) { case EFI_ACPI_HMAT_ACCESS_LATENCY : return DDR2LMCACHE_ACCESS_LATENCY; case EFI_ACPI_HMAT_READ_LATENCY : return DDR2LMCACHE_READ_LATENCY; case EFI_ACPI_HMAT_WRITE_LATENCY : return DDR2LMCACHE_WRITE_LATENCY; default: return 0xFF; } } else { switch (DataType) { case EFI_ACPI_HMAT_ACCESS_LATENCY : return DDR_ACCESS_LATENCY; case EFI_ACPI_HMAT_READ_LATENCY : return DDR_READ_LATENCY; case EFI_ACPI_HMAT_WRITE_LATENCY : return DDR_WRITE_LATENCY; default: return 0xFF; } } } /** Get the Read/Write/Access Latency value of the memory type between sockets. @param [in] DataType Type of latency value that is required (Read/Write/Access) @param [in] Type Type of memory for which Latency is required. (DDR/DDRT) @retval Value of the Latency associated with memory type. @retval 0xFF Input parameters are invalid. **/ UINT16 GetXSocketMemoryLatency ( IN UINT8 DataType, IN MemoryType Type, IN UINT8 volMemMode ) { if (Type == DDRT) { // MemoryType == DDRT switch (DataType) { case EFI_ACPI_HMAT_ACCESS_LATENCY : return DDRT_ACCESS_LATENCY; case EFI_ACPI_HMAT_READ_LATENCY : if (volMemMode == VOL_MEM_MODE_1LM) { return XSOCKET_DDRT_1LM_READ_LATENCY; } else { return XSOCKET_DDRT_2LM_READ_LATENCY; } case EFI_ACPI_HMAT_WRITE_LATENCY : if (volMemMode == VOL_MEM_MODE_1LM) { return XSOCKET_DDRT_1LM_WRITE_LATENCY; } else { return XSOCKET_DDRT_2LM_WRITE_LATENCY; } default: return 0xFF; } } else if (Type == DDR2LMCACHE){ switch (DataType) { case EFI_ACPI_HMAT_ACCESS_LATENCY : return DDR2LMCACHE_ACCESS_LATENCY; case EFI_ACPI_HMAT_READ_LATENCY : return DDR2LMCACHE_READ_LATENCY; case EFI_ACPI_HMAT_WRITE_LATENCY : return DDR2LMCACHE_WRITE_LATENCY; default: return 0xFF; } } else { switch (DataType) { case EFI_ACPI_HMAT_ACCESS_LATENCY : return XSOCKET_DDR_ACCESS_LATENCY; case EFI_ACPI_HMAT_READ_LATENCY : return XSOCKET_DDR_READ_LATENCY; case EFI_ACPI_HMAT_WRITE_LATENCY : return XSOCKET_DDR_WRITE_LATENCY; default: return 0xFF; } } } /** Patch HMAT LATENCY_BANDWIDTH_INFO_STRUCTURE structure. @param [in] Lbis Points to HMAT LATENCY_BANDWIDTH_INFO_STRUCTURE to be modified @param [in, out] LibsSlackSize Points to cummulative LIBS slack size @param [in] HmatData HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE to be consumed @retval None **/ VOID PatchBandWidthLbis ( IN LATENCY_BANDWIDTH_INFO_STRUCTURE **LbisPtr, IN OUT UINTN *LibsSlackSize, IN HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData, IN UINT8 DataType, IN MemoryType Type, IN UINT8 volMemMode ) { UINT32 Row = 0; UINT32 Col = 0; UINT32 RowSocketId = 0; UINT32 ColSocketId = 0; UINT32 RowBaseIndex = 0; UINT32 StructSize = 0; UINT32 InitiatorIndex = 0; UINT32 TargetIndex = 0; LATENCY_BANDWIDTH_INFO_STRUCTURE *Lbis = *LbisPtr; UINT32 *InitiatorProximityDomainList = (UINT32*)Lbis; UINT32 *TargetProximityDomainList = (UINT32*)Lbis; UINT16 *RelativeDistanceEntry = (UINT16*)Lbis; Lbis->Type = SYSTEM_LOCALITY_LATENCY_BANDWIDTH_INFORMATION_STRUCTURE_TYPE; Lbis->DataType = DataType; Lbis->InitiatorProximityDomainsNumber = HmatData->ProcessorDomainNumber; Lbis->TargetProximityDomainsNumber = HmatData->MemoryDomainNumber; switch (Type) { case DDR2LMCACHE: Lbis->EntryBaseUnit = DDR2LMCACHE_BANDWIDTH_BASE_UNIT; Lbis->Flags = EFI_ACPI_HMAT_MEMORY_HIERACHY_LAST_LEVEL_MEMORY; break; default: Lbis->EntryBaseUnit = MEMORY_BANDWIDTH_BASE_UNIT; Lbis->Flags = EFI_ACPI_HMAT_MEMORY_HIERACHY_MEMORY; } InitiatorProximityDomainList = (UINT32*)&(Lbis->InitiatorProximityDomainList[0]); StructSize = (UINT32)((UINT8*)InitiatorProximityDomainList - (UINT8*)Lbis); StructSize += (Lbis->InitiatorProximityDomainsNumber * (sizeof (UINT32))); TargetProximityDomainList = (UINT32*)((UINT8*)TargetProximityDomainList + StructSize); StructSize += (Lbis->TargetProximityDomainsNumber * (sizeof (UINT32))); RelativeDistanceEntry = (UINT16*)((UINT8*)RelativeDistanceEntry + StructSize); StructSize += (Lbis->InitiatorProximityDomainsNumber * Lbis->TargetProximityDomainsNumber * (sizeof (UINT16))); Lbis->Length = StructSize; for (Row = 0; Row < Lbis->InitiatorProximityDomainsNumber; Row++) { *(InitiatorProximityDomainList + Row) = Row; } for (Row = 0; Row < Lbis->TargetProximityDomainsNumber; Row++) { *(TargetProximityDomainList + Row) = Row; } Row = 0; for (InitiatorIndex = 0; InitiatorIndex < EFI_ACPI_HMAT_NUMBER_OF_PROCESSOR_DOMAINS; InitiatorIndex++) { if (HmatData->ProcessorDomainList[InitiatorIndex] == 1) { RowSocketId = HmatData->ProcessorDomainSocketIdList[InitiatorIndex]; RowBaseIndex = Row * Lbis->TargetProximityDomainsNumber; Col = 0; for (TargetIndex = 0; TargetIndex < EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS; TargetIndex++) { if (HmatData->MemoryDomainList[TargetIndex].Valid == 1) { ColSocketId = HmatData->MemoryDomainList[TargetIndex].PhysicalSocketId; if (Type == DDR2LMCACHE) { // // DDR acting as cache for DDRT //SocketsLinked // if (SocketsLinked (RowSocketId, ColSocketId)) { RelativeDistanceEntry[RowBaseIndex + Col] = GetXSocketMemoryBandWidth (DataType, Type, volMemMode); } else { RelativeDistanceEntry[RowBaseIndex + Col] = GetMemoryBandWidth (DataType, Type, volMemMode); } } else if (mSystemMemoryMap->volMemMode == VOL_MEM_MODE_2LM) { // // Add only details for DDRT as entire DDR is acting as Cache // if (SocketsLinked (RowSocketId, ColSocketId)) { RelativeDistanceEntry[RowBaseIndex + Col] = GetXSocketMemoryBandWidth (DataType, DDRT, volMemMode); } else { RelativeDistanceEntry[RowBaseIndex + Col] = GetMemoryBandWidth (DataType, DDRT, volMemMode); } } else { // // Neither cache nor 2LM, we need to add details for both DDR and DDRT // if ((Col / (HmatData->MemoryDomainNumber- SkippedEntries)) == 0) { // // DDR entries // if (SocketsLinked (RowSocketId, ColSocketId)) { RelativeDistanceEntry[RowBaseIndex + Col] = GetXSocketMemoryBandWidth (DataType, DDR, volMemMode); } else { RelativeDistanceEntry[RowBaseIndex + Col] = GetMemoryBandWidth (DataType, DDR, volMemMode); } } else { // // DDRT entries // if (SocketsLinked (RowSocketId, ColSocketId)) { RelativeDistanceEntry[RowBaseIndex + Col] = GetXSocketMemoryBandWidth (DataType, DDRT, volMemMode); } else { RelativeDistanceEntry[RowBaseIndex + Col] = GetMemoryBandWidth (DataType, DDRT, volMemMode); } } } Col++; } //if (HmatData->MemoryDomainList[TargetIndex].Valid == 1) } // for (TargetIndex) Row++; } // if (HmatData->ProcessorDomainList[InitiatorIndex] == 1) } //for (InitiatorIndex) *LibsSlackSize += StructSize; *LbisPtr = (LATENCY_BANDWIDTH_INFO_STRUCTURE *)((UINT8*)Lbis + StructSize); } /** Patch HMAT LATENCY_BANDWIDTH_INFO_STRUCTURE structure. @param [in] Lbis Points to HMAT LATENCY_BANDWIDTH_INFO_STRUCTURE to be modified @param [in, out] LibsSlackSize Points to cumulative LIBS slack size @param [in] HmatData HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE to be consumed @retval None **/ VOID PatchMemoryLatencyLbis ( IN LATENCY_BANDWIDTH_INFO_STRUCTURE **LbisPtr, IN OUT UINTN *LibsSlackSize, IN HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData, IN UINT8 DataType, IN MemoryType Type, IN UINT8 volMemMode ) { UINT32 Row = 0; UINT32 Col = 0; UINT32 RowSocketId = 0; UINT32 ColSocketId = 0; UINT32 RowBaseIndex = 0; UINT32 StructSize = 0; UINT32 InitiatorIndex = 0; UINT32 TargetIndex = 0; LATENCY_BANDWIDTH_INFO_STRUCTURE *Lbis = *LbisPtr; UINT32 *InitiatorProximityDomainList = (UINT32*)Lbis; UINT32 *TargetProximityDomainList = (UINT32*)Lbis; UINT16 *RelativeDistanceEntry = (UINT16*)Lbis; Lbis->Type = SYSTEM_LOCALITY_LATENCY_BANDWIDTH_INFORMATION_STRUCTURE_TYPE; Lbis->DataType = DataType; Lbis->InitiatorProximityDomainsNumber = HmatData->ProcessorDomainNumber; Lbis->TargetProximityDomainsNumber = HmatData->MemoryDomainNumber; switch (Type) { case DDR2LMCACHE: Lbis->EntryBaseUnit = DDR2LMCACHE_BANDWIDTH_BASE_UNIT; Lbis->Flags = EFI_ACPI_HMAT_MEMORY_HIERACHY_LAST_LEVEL_MEMORY; break; default: Lbis->EntryBaseUnit = MEMORY_LATENCY_BASE_UNIT; Lbis->Flags = EFI_ACPI_HMAT_MEMORY_HIERACHY_MEMORY; } InitiatorProximityDomainList = (UINT32*)&(Lbis->InitiatorProximityDomainList[0]); StructSize = (UINT32)((UINT8*)InitiatorProximityDomainList - (UINT8*)Lbis); StructSize += (Lbis->InitiatorProximityDomainsNumber * (sizeof (UINT32))); TargetProximityDomainList = (UINT32*)((UINT8*)TargetProximityDomainList + StructSize); StructSize += (Lbis->TargetProximityDomainsNumber * (sizeof (UINT32))); RelativeDistanceEntry = (UINT16*)((UINT8*)RelativeDistanceEntry + StructSize); StructSize += (Lbis->InitiatorProximityDomainsNumber * Lbis->TargetProximityDomainsNumber * (sizeof (UINT16))); Lbis->Length = StructSize; for (Row = 0; Row < Lbis->InitiatorProximityDomainsNumber; Row++) { *(InitiatorProximityDomainList + Row) = Row; } for (Row = 0; Row < Lbis->TargetProximityDomainsNumber; Row++) { *(TargetProximityDomainList + Row) = Row; } Row = 0; for (InitiatorIndex = 0; InitiatorIndex < EFI_ACPI_HMAT_NUMBER_OF_PROCESSOR_DOMAINS; InitiatorIndex++) { if (HmatData->ProcessorDomainList[InitiatorIndex] == 1) { RowSocketId = HmatData->ProcessorDomainSocketIdList[InitiatorIndex]; RowBaseIndex = Row * Lbis->TargetProximityDomainsNumber; Col = 0; for (TargetIndex = 0; TargetIndex < EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS; TargetIndex++) { if (HmatData->MemoryDomainList[TargetIndex].Valid == 1) { ColSocketId = HmatData->MemoryDomainList[TargetIndex].PhysicalSocketId; if (Type == DDR2LMCACHE) { // // DDR acting as cache for DDRT // if (SocketsLinked (RowSocketId, ColSocketId)) { RelativeDistanceEntry[RowBaseIndex + Col] = GetXSocketMemoryLatency (DataType, Type, volMemMode); } else { RelativeDistanceEntry[RowBaseIndex + Col] = GetMemoryLatency (DataType, Type, volMemMode); } } else if (mSystemMemoryMap->volMemMode == VOL_MEM_MODE_2LM) { // // Add only details for DDRT as entire DDR is acting as Cache // if (SocketsLinked (RowSocketId, ColSocketId)) { RelativeDistanceEntry[RowBaseIndex + Col] = GetXSocketMemoryLatency (DataType, DDRT, volMemMode); } else { RelativeDistanceEntry[RowBaseIndex + Col] = GetMemoryLatency (DataType, DDRT, volMemMode); } } else { // // Neither cache nor 2LM, we need to add details for both DDR and DDRT // if ((Col / (HmatData->MemoryDomainNumber - SkippedEntries)) == 0) { // // DDR entries // if (SocketsLinked (RowSocketId, ColSocketId)) { RelativeDistanceEntry[RowBaseIndex + Col] = GetXSocketMemoryLatency (DataType, DDR, volMemMode); } else { RelativeDistanceEntry[RowBaseIndex + Col] = GetMemoryLatency (DataType, DDR, volMemMode); } } else { // // DDRT entries // if (SocketsLinked (RowSocketId, ColSocketId)) { RelativeDistanceEntry[RowBaseIndex + Col] = GetXSocketMemoryLatency (DataType, DDRT, volMemMode); } else { RelativeDistanceEntry[RowBaseIndex + Col] = GetMemoryLatency (DataType, DDRT, volMemMode); } } } Col++; } //if (HmatData->MemoryDomainList[TargetIndex].Valid == 1) } // for (TargetIndex) Row++; } // if (HmatData->ProcessorDomainList[InitiatorIndex] == 1) } //for (InitiatorIndex) *LibsSlackSize += StructSize; *LbisPtr = (LATENCY_BANDWIDTH_INFO_STRUCTURE *)((UINT8*)Lbis + StructSize); } /** Patch HMAT LATENCY_BANDWIDTH_INFO_STRUCTURE structure. @param [in] Lbis Points to HMAT LATENCY_BANDWIDTH_INFO_STRUCTURE to be modified @param [in, out] SlackSize Points to cummulative slack size @param [in] HmatData HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE to be consumed @retval None **/ VOID PatchHmatLbis ( IN LATENCY_BANDWIDTH_INFO_STRUCTURE *Lbis, IN OUT UINTN *SlackSize, IN HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData ) { UINTN LibsStructSize=0; UINT8 volMemMode; LATENCY_BANDWIDTH_INFO_STRUCTURE *OrginalLbis = Lbis; volMemMode = mSystemMemoryMap->volMemMode; DEBUG ((DEBUG_INFO, "[ACPI] (HMAT) Populating System Locality Latency and Bandwidth Information Structure\n")); PatchMemoryLatencyLbis (&Lbis, &LibsStructSize, HmatData, EFI_ACPI_HMAT_READ_LATENCY, DDR, volMemMode); PatchMemoryLatencyLbis (&Lbis, &LibsStructSize, HmatData, EFI_ACPI_HMAT_WRITE_LATENCY, DDR, volMemMode); PatchBandWidthLbis (&Lbis, &LibsStructSize, HmatData, EFI_ACPI_HMAT_READ_BANDWIDTH, DDR, volMemMode); PatchBandWidthLbis (&Lbis, &LibsStructSize, HmatData, EFI_ACPI_HMAT_WRITE_BANDWIDTH, DDR, volMemMode); if (mSystemMemoryMap->volMemMode != VOL_MEM_MODE_1LM) { PatchMemoryLatencyLbis (&Lbis, &LibsStructSize, HmatData, EFI_ACPI_HMAT_READ_LATENCY, DDR2LMCACHE, volMemMode); PatchMemoryLatencyLbis (&Lbis, &LibsStructSize, HmatData, EFI_ACPI_HMAT_WRITE_LATENCY, DDR2LMCACHE, volMemMode); PatchBandWidthLbis (&Lbis, &LibsStructSize, HmatData, EFI_ACPI_HMAT_READ_BANDWIDTH, DDR2LMCACHE, volMemMode); PatchBandWidthLbis (&Lbis, &LibsStructSize, HmatData, EFI_ACPI_HMAT_WRITE_BANDWIDTH, DDR2LMCACHE, volMemMode); } RemoveSlack (SlackSize, OrginalLbis, LibsStructSize); *SlackSize += EFI_ACPI_HMAT_LBIS_COUNT * sizeof(Lbis[0]) - LibsStructSize; } /** Get Type 17 Handles for HBM devices used as cache. @param [in, out] SmbiosHandles Points to SmbiosHandles array to be modified @param [in] NumaNode Value for NUMA Node whose memory is being cached for the specific HBM devices @retval None **/ VOID GetHbmCacheHandles ( IN UINT16 *SmbiosHandles, IN UINT8 NumaNode ) { EFI_STATUS Status = EFI_SUCCESS; SMBIOS_TABLE_TYPE17 *SmbiosType17Record; EFI_SMBIOS_HANDLE SmbiosHandle = 0; EFI_SMBIOS_PROTOCOL *mSmbios; EFI_SMBIOS_TYPE SmbiosType = EFI_SMBIOS_TYPE_MEMORY_DEVICE; EFI_SMBIOS_TABLE_HEADER *SmbiosRecord; UINT8 HbmHandleCount = 0; Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID **) &mSmbios); if (!(EFI_ERROR (Status))) { while ((!(EFI_ERROR (Status))) && (SmbiosHandle != SMBIOS_HANDLE_PI_RESERVED) ){ Status = mSmbios->GetNext (mSmbios, &SmbiosHandle, &SmbiosType, &SmbiosRecord, NULL); SmbiosType17Record = (SMBIOS_TABLE_TYPE17 *) SmbiosRecord; if ((SmbiosType17Record->TypeDetail.CacheDram == 1) && (SmbiosType17Record->DeviceSet == NumaNode) ) { SmbiosHandles[HbmHandleCount] = SmbiosType17Record->Hdr.Handle; HbmHandleCount++; } } } } /** Patch HMAT MEMORY_SIDE_CACHE_INFORMATION_STRUCTURE substructure. @param [in, out] Mscis Points to HMAT MEMORY_SIDE_CACHE_INFORMATION_STRUCTURE to be modified @param [in, out] SlackSize Points to cumulative slack size @param [in] HmatData HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE to be consumed @retval None **/ VOID PatchHmatMscis ( IN OUT MEMORY_SIDE_CACHE_INFORMATION_STRUCTURE *Mscis, IN OUT UINTN *SlackSize, IN HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData ) { UINT32 Index; UINT32 Count = 0; UINT32 UnusedCount; UINT8 SmbiosHandleIndex; DEBUG ((DEBUG_INFO, "[ACPI] (HMAT) Populating Memory Side Cache Information Structure\n")); for (Index = 0; Index < EFI_ACPI_HMAT_NUMBER_OF_MEMORY_DOMAINS; Index++) { if ((HmatData->MemoryDomainList[Index].Valid == 0) || (HmatData->MemoryDomainList[Index].Cacheable == 0)) { continue; } // // Fill in fields for Cached Memory Domains // Mscis[Count].MemoryProximityDomain = Index; Mscis[Count].CacheAttributes.Bits.TotalCacheLevels = EFI_ACPI_HMAT_ONE_LEVEL_CACHE; Mscis[Count].CacheAttributes.Bits.CacheLevel = EFI_ACPI_HMAT_ONE_LEVEL_CACHE; Mscis[Count].CacheAttributes.Bits.CacheLineSize = DDR4_CACHE_LINE_SIZE; Mscis[Count].CacheAttributes.Bits.CacheAssociativity = EFI_ACPI_HMAT_CACHE_ASSOCIATIVITY_DIRECT_MAPPED; Mscis[Count].CacheAttributes.Bits.WritePolicy = EFI_ACPI_HMAT_WRITE_POLICY_WB; Mscis[Count].MemorySideCacheSize = LShiftU64 (HmatData->MemoryDomainList[Index].MemorySideCacheSize, MEM_ADDR_SHFT_VAL); Mscis[Count].NumSmbiosHandles = HmatData->MemoryDomainList[Index].NumSmbiosHandles; for(SmbiosHandleIndex = 0 ; SmbiosHandleIndex < HmatData->MemoryDomainList[Index].NumSmbiosHandles; SmbiosHandleIndex++){ Mscis[Count].SmbiosHandles[SmbiosHandleIndex] = HmatData->MemoryDomainList[Index].SmbiosHandles[SmbiosHandleIndex]; } DEBUG ((DEBUG_INFO, "[ACPI] (HMAT) MSCIS[%x] MemorySideCacheSize = 0x%lx, ProximityDomain = %x\n", Count, Mscis[Count].MemorySideCacheSize, Index)); Count++; } // // Move MSCIS entries up to replace removed MSARS entries // if (*SlackSize && Count) { RemoveSlack (SlackSize, Mscis, Count * sizeof(Mscis[0])); } // // Update SlackSize for unused MSCIS entries // UnusedCount = EFI_ACPI_HMAT_MSCIS_COUNT - Count; *SlackSize += UnusedCount * sizeof(Mscis[0]); } /** Update the HMAT table. @param [in, out] HmatAcpiTable The table to be set. @retval EFI SUCCESS Procedure returned successfully. **/ EFI_STATUS PatchHmatAcpiTable ( IN OUT EFI_ACPI_COMMON_HEADER *Table ) { UINTN SlackSize = 0; HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *HmatData = NULL; EFI_ACPI_HETEROGENEOUS_MEMORY_ATTRIBUTE_TABLE *HmatAcpiTable; if (mSystemMemoryMap == NULL) { ASSERT (FALSE); return EFI_UNSUPPORTED; } HmatAcpiTable = (EFI_ACPI_HETEROGENEOUS_MEMORY_ATTRIBUTE_TABLE *)Table; HmatData = (HMAT_PROXIMITY_DOMAIN_DATA_STRUCTURE *) InitializeHmatData (); if (HmatData == NULL) { DEBUG ((DEBUG_ERROR, "ACPI (HMAT) HMAT Data could not be initialized... skipping HMAT Patch\n")); return EFI_OUT_OF_RESOURCES; } // // Patch the MSARS, SLLBIS, and MSCIS Structure from HMAT Table. // PatchHmatMsars (HmatAcpiTable, &SlackSize, HmatData); PatchHmatLbis (HmatAcpiTable->Lbis, &SlackSize, HmatData); PatchHmatMscis (HmatAcpiTable->MemSideCache, &SlackSize, HmatData); if (HmatData != NULL) { FreePool (HmatData); } // // Update HMAT table size // HmatAcpiTable->HmatHeader.Header.Length -= (UINT32)SlackSize; // // Dump HMAT // DumpHmat (HmatAcpiTable); return EFI_SUCCESS; }