/* $NoKeywords:$ */ /** * @file * * AMD SRAT, ACPI table related API functions. * * Contains code that Create the APCI SRAT Table after early reset. * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: CPU * @e \$Revision: 84150 $ @e \$Date: 2012-12-12 15:46:25 -0600 (Wed, 12 Dec 2012) $ * */ /* ****************************************************************************** * * Copyright (c) 2008 - 2013, Advanced Micro Devices, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Advanced Micro Devices, Inc. nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL ADVANCED MICRO DEVICES, INC. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************/ /*---------------------------------------------------------------------------- * This file provides functions, that will generate SRAT tables *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------------------- * M O D U L E S U S E D *---------------------------------------------------------------------------------------- */ #include "AGESA.h" #include "amdlib.h" #include "cpuServices.h" #include "OptionSrat.h" #include "heapManager.h" #include "cpuRegisters.h" #include "cpuLateInit.h" #include "Ids.h" #include "Filecode.h" CODE_GROUP (G3_DXE) RDATA_GROUP (G3_DXE) #define FILECODE PROC_CPU_FEATURE_CPUSRAT_FILECODE /*---------------------------------------------------------------------------------------- * D E F I N I T I O N S A N D M A C R O S *---------------------------------------------------------------------------------------- */ extern OPTION_SRAT_CONFIGURATION OptionSratConfiguration; // global user config record #define NodeID 0x60 #define FOURGB 0x010000ul /*---------------------------------------------------------------------------------------- * T Y P E D E F S A N D S T R U C T U R E S *---------------------------------------------------------------------------------------- */ AGESA_STATUS GetAcpiSratStub ( IN OUT AMD_CONFIG_PARAMS *StdHeader, IN OUT VOID **SratPtr ); AGESA_STATUS GetAcpiSratMain ( IN OUT AMD_CONFIG_PARAMS *StdHeader, IN OUT VOID **SratPtr ); /*---------------------------------------------------------------------------- * All of the DATA should be defined in _CODE segment. * Use ROMDATA to specify that it belongs to _CODE. *---------------------------------------------------------------------------- */ STATIC CPU_SRAT_HEADER ROMDATA CpuSratHdrStruct = { {'S','R','A','T'}, 0, 2, 0, {0}, {0}, 1, {'A','M','D',' '}, 1, 1, {0, 0, 0, 0, 0, 0, 0, 0} }; /*---------------------------------------------------------------------------------------- * P R O T O T Y P E S O F L O C A L F U N C T I O N S *---------------------------------------------------------------------------------------- */ UINT8 STATIC *MakeApicEntry ( IN UINT8 ApicId, IN UINT8 Domain, IN UINT8 *BufferLocPtr ); UINT8 STATIC *FillMemoryForCurrentNode ( IN UINT8 *PDomain, IN OUT UINT8 *PDomainForBase640K, IN UINT8 Node, IN OUT UINT8 *BufferLocPtr, IN OUT AMD_CONFIG_PARAMS *StdHeader ); UINT8 STATIC *MakeMemEntry ( IN UINT8 PDomain, IN UINT8 Node, IN UINT32 Base, IN UINT32 Size, IN UINT8 *BufferLocPtr ); /*---------------------------------------------------------------------------------------- * E X P O R T E D F U N C T I O N S *---------------------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------------------*/ /** * * This function will generate a complete Static Resource Affinity Table * i.e. SRAT into a memory buffer. After completion, this table must be set * by the system BIOS into its internal ACPI name space. * * @param[in, out] StdHeader Standard Head Pointer * @param[in, out] SratPtr Point to Srat Struct including buffer address and length * * @retval AGESA_STATUS */ AGESA_STATUS CreateAcpiSrat ( IN OUT AMD_CONFIG_PARAMS *StdHeader, IN OUT VOID **SratPtr ) { AGESA_TESTPOINT (TpProcCpuEntrySrat, StdHeader); return ((*(OptionSratConfiguration.SratFeature)) (StdHeader, SratPtr)); } /*---------------------------------------------------------------------------------------*/ /** * * This is the default routine for use when the SRAT option is NOT requested. * * The option install process will create and fill the transfer vector with * the address of the proper routine (Main or Stub). The link optimizer will * strip out of the .DLL the routine that is not used. * * @param[in, out] StdHeader Standard Head Pointer * @param[in, out] SratPtr Point to Srat Struct including buffer address and length * * @retval AGESA_STATUS */ AGESA_STATUS GetAcpiSratStub ( IN OUT AMD_CONFIG_PARAMS *StdHeader, IN OUT VOID **SratPtr ) { return AGESA_UNSUPPORTED; } /*---------------------------------------------------------------------------------------*/ /** * * This function will generate a complete Static Resource Affinity Table * i.e. SRAT into a memory buffer. After completion, this table must be set * by the system BIOS into its internal ACPI name space. * * @param[in, out] StdHeader Standard Head Pointer * @param[in, out] SratPtr Point to Srat Struct including buffer address and length * * @retval AGESA_STATUS */ AGESA_STATUS GetAcpiSratMain ( IN OUT AMD_CONFIG_PARAMS *StdHeader, IN OUT VOID **SratPtr ) { UINT8 *BufferPtr; UINT8 NodeNum; UINT8 NodeCount; UINT8 PDomain; UINT8 PDomainForBase640K; UINT32 Socket; UINT32 Module; UINT32 LowCore; UINT32 HighCore; UINT32 CoreNum; UINT32 tempVar_32; AMD_APIC_PARAMS ApicParams; CPU_SRAT_HEADER *CpuSratHeaderStructPtr; ALLOCATE_HEAP_PARAMS AllocParams; // Get Node count NodeCount = 1; // The worst-case buffer size to request is for the SRAT table header, one // entree for special region (base 640k block), two memory // regions per node, and APIC entries for each core in the system. tempVar_32 = (sizeof (CPU_SRAT_HEADER)) + (sizeof (CPU_SRAT_MEMORY_ENTRY)) + ((UINT32) NodeCount * (2 * (sizeof (CPU_SRAT_MEMORY_ENTRY)) + ((UINT32) GetActiveCoresInCurrentModule (StdHeader) * sizeof (CPU_SRAT_APIC_ENTRY)))); if (*SratPtr == NULL) { // // Allocate a buffer // AllocParams.RequestedBufferSize = tempVar_32; AllocParams.BufferHandle = AMD_SRAT_INFO_BUFFER_HANDLE; AllocParams.Persist = HEAP_SYSTEM_MEM; AGESA_TESTPOINT (TpProcCpuBeforeAllocateSratBuffer, StdHeader); if (HeapAllocateBuffer (&AllocParams, StdHeader) != AGESA_SUCCESS) { return AGESA_ERROR; } AGESA_TESTPOINT (TpProcCpuAfterAllocateSratBuffer, StdHeader); *SratPtr = AllocParams.BufferPtr; } IDS_HDT_CONSOLE (CPU_TRACE, " SRAT is created\n"); CpuSratHeaderStructPtr = (CPU_SRAT_HEADER *) *SratPtr; BufferPtr = (UINT8 *) *SratPtr; // Copy acpiSRATHeader -> data buffer LibAmdMemCopy (*SratPtr, (VOID *) &CpuSratHdrStruct, (UINTN) (sizeof (CPU_SRAT_HEADER)), StdHeader); // Update table OEM fields. LibAmdMemCopy ((VOID *) &CpuSratHeaderStructPtr->OemId, (VOID *) &OptionSratConfiguration.OemIdString, sizeof (OptionSratConfiguration.OemIdString), StdHeader); LibAmdMemCopy ((VOID *) &CpuSratHeaderStructPtr->OemTableId, (VOID *) &OptionSratConfiguration.OemTableIdString, sizeof (OptionSratConfiguration.OemTableIdString), StdHeader); BufferPtr += sizeof (CPU_SRAT_HEADER); // Place all memory and IO affinity entries NodeNum = 0; PDomain = 0; PDomainForBase640K = 0xFF; ApicParams.StdHeader = *StdHeader; while (NodeNum < NodeCount) { GetSocketModuleOfNode ((UINT32) NodeNum, &Socket, &Module, StdHeader); GetGivenModuleCoreRange (Socket, Module, &LowCore, &HighCore, StdHeader); BufferPtr = FillMemoryForCurrentNode (&PDomain, &PDomainForBase640K, NodeNum, BufferPtr, StdHeader); for (CoreNum = LowCore; CoreNum <= HighCore; CoreNum++) { ApicParams.Socket = (UINT8) Socket; ApicParams.Core = (UINT8) CoreNum; AmdGetApicId (&ApicParams); if (ApicParams.IsPresent) { BufferPtr = MakeApicEntry (ApicParams.ApicAddress, PDomain, BufferPtr); } } NodeNum++; PDomain = NodeNum; } // Store size in table (current buffer offset - buffer start offset) CpuSratHeaderStructPtr->TableLength = (UINT32) (BufferPtr - (UINT8 *) CpuSratHeaderStructPtr); //Update SSDT header Checksum ChecksumAcpiTable ((ACPI_TABLE_HEADER *) CpuSratHeaderStructPtr, StdHeader); return AGESA_SUCCESS; } /*---------------------------------------------------------------------------------------- * L O C A L F U N C T I O N S *---------------------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------------------*/ /** * * This function will build Memory entry for current node. * Note that we only create a memory affinity entry if we find one * that matches the current node. This makes an easier to read table * though it is not necessary. * * @param[in] PDomain Proximity Domain * @param[in, out] PDomainForBase640K The PDomain for Base 640K * @param[in] Node The number of Node * @param[in, out] BufferLocPtr Point to the address of buffer * @param[in, out] StdHeader Standard Head Pointer * * @retval UINT8 *(New buffer location ptr) */ UINT8 STATIC *FillMemoryForCurrentNode ( IN UINT8 *PDomain, IN OUT UINT8 *PDomainForBase640K, IN UINT8 Node, IN OUT UINT8 *BufferLocPtr, IN OUT AMD_CONFIG_PARAMS *StdHeader ) { UINT32 ValueLimit; UINT32 ValueTOM; BOOLEAN isModified; UINT8 Domain; UINT32 RegVal; UINT32 DramLeng; UINT32 DramBase; UINT32 DramLimit; UINT32 OffsetRegs; PCI_ADDR PciAddress; UINT64 MsrValue; UINT32 TopOfMemoryAbove4Gb; Domain = *PDomain; PciAddress.Address.Segment = 0; PciAddress.Address.Bus = 0; PciAddress.Address.Device = LOW_NODE_DEVICEID; PciAddress.Address.Function = FUNC_1; for (OffsetRegs = DRAMBase0; OffsetRegs < MMIOBase0; OffsetRegs += 8) { isModified = FALSE; // FALSE means normal update procedure // Get DRAM Base Address PciAddress.Address.Register = OffsetRegs; LibAmdPciRead (AccessWidth32, PciAddress, &DramBase, StdHeader); if ((DramBase & 3) != 3) { // 0:1 set if memory range enabled // Not set, so we don't have an enabled range continue; // Proceed to next Base register } // Get DRAM Limit PciAddress.Address.Register = OffsetRegs + 4; LibAmdPciRead (AccessWidth32, PciAddress, &DramLimit, StdHeader); if (DramLimit == 0xFFFFFFFF) { // Node not installed(all FF's)? continue; // Proceed to next Base register } if ((DramLimit & 0xFF) != Node) { // Check if Destination Node ID is current node continue; // Proceed to next Base register } // We only add an entry now if detected range belongs to current node/PDomain PciAddress.Address.Register = OffsetRegs + 0x104; LibAmdPciRead (AccessWidth32, PciAddress, &RegVal, StdHeader); DramLimit = (((RegVal & 0xFF) << 16) | (DramLimit >> 16)); // Get DRAM Limit addr [47:24] DramLimit++; // Add 1 for potential length DramLimit <<= 8; // Get DRAM Base Address PciAddress.Address.Register = OffsetRegs + 0x100; LibAmdPciRead (AccessWidth32, PciAddress, &RegVal, StdHeader); DramBase = ((((RegVal & 0xFF) << 24) | (DramBase >> 8)) & 0xFFFFFF00); // Get DRAM Base Base value [47:24] DramLeng = DramLimit - DramBase; // Subtract base from limit to get length // Leave hole for conventional memory (Less than 640K). It must be on CPU 0. if (DramBase == 0) { if (*PDomainForBase640K == 0xFF) { // It is the first time that the range start at 0. // If Yes, then Place 1MB memory gap and save Domain to PDomainForBase640K BufferLocPtr = MakeMemEntry ( Domain, Node, 0, // Base = 0 0xA0000 >> 16, // Put it into format used in DRAM regs.. BufferLocPtr ); DramBase += 0x10; // Add 1MB, so range = 1MB to Top of Region DramLeng -= 0x10; // Also subtract 1MB from the length *PDomainForBase640K = Domain; // Save Domain number for memory Less than 640K } else { // If No, there are more than one memory range less than 640K, it should that // node interleaving is enabled. All nodes have the same memory ranges // and all cores in these nodes belong to the same domain. *PDomain = *PDomainForBase640K; return (BufferLocPtr); } } LibAmdMsrRead (TOP_MEM, &MsrValue, StdHeader); ValueTOM = (UINT32) MsrValue >> 16; // Save it in 39:24 format ValueLimit = DramBase + DramLeng; // We need to know how large region is LibAmdMsrRead (SYS_CFG, &MsrValue, StdHeader); if ((MsrValue & BIT21) != 0) { LibAmdMsrRead (TOP_MEM2, &MsrValue, StdHeader); TopOfMemoryAbove4Gb = (UINT32) (MsrValue >> 16); // Save it in 47:16 format } else { TopOfMemoryAbove4Gb = 0xFFFFFFFF; } // SPECIAL CASES: // // Several conditions require that we process the values of the memory range differently. // Here are descriptions of the corner cases. // // 1. TRUNCATE LOW - Memory range starts below TOM, ends in TOM (memory hole). For this case, // the range must be truncated to end at TOM. // ******************************* ******************************* // * * * -> * * // ******************************* ******************************* // 2 TOM 4 2 TOM // // 2. TRUNCATE HIGH - Memory range starts below 4GB, ends above 4GB. This is handled by changing the // start base to 4GB. // **************** ********** // * * * -> * * // **************** ********** // TOM 3.8 4 6 TOM 3.8 4 6 // // 3. Memory range starts below TOM, ends above 4GB. For this case, the range must be truncated // to end at TOM. Note that this scenario creates two ranges, as the second comparison below // will find that it ends above 4GB since base and limit have been restored after first truncation, // and a second range will be written based at 4GB ending at original end address. // ******************************* **************** ********** // * * * * -> * * * * // ******************************* **************** ********** // 2 TOM 4 6 2 TOM 4 6 // // 4. Memory range starts above TOM, ends below or equal to 4GB. This invalid range should simply // be ignored. // ******* // * * -> < NULL > // ******* // TOM 3.8 4 // // 5. Memory range starts below TOM2, and ends beyond TOM2. This range must be truncated to TOM2. // ************************ ******************************* // * * * -> * * // ************************ ******************************* // 768 TOM2 1024 768 TOM2 // // 6. Memory range starts above TOM2. This invalid range should simply be ignored. // ******************** // * * -> < NULL > // ******************** // TOM2 1024 1280 if (((DramBase < ValueTOM) && (ValueLimit <= FOURGB) && (ValueLimit > ValueTOM)) || ((DramBase < ValueTOM) && (ValueLimit > FOURGB))) { // TRUNCATE LOW!!! Shrink entry below TOM... // Base = DramBase, Size = TOM - DramBase BufferLocPtr = MakeMemEntry (Domain, Node, DramBase, (ValueTOM - DramBase), BufferLocPtr); isModified = TRUE; } if ((ValueLimit > FOURGB) && (DramBase < FOURGB)) { // TRUNCATE HIGH!!! Shrink entry above 4GB... // Size = Base + Size - 4GB, Base = 4GB BufferLocPtr = MakeMemEntry (Domain, Node, FOURGB, (DramLeng + DramBase - FOURGB), BufferLocPtr); isModified = TRUE; } if ((DramBase >= ValueTOM) && (ValueLimit <= FOURGB)) { // IGNORE!!! Entry located entirely within memory hole isModified = TRUE; } if ((DramBase < TopOfMemoryAbove4Gb) && (ValueLimit > TopOfMemoryAbove4Gb)) { // Truncate to TOM2 // Base = DramBase, Size = TOM2 - DramBase BufferLocPtr = MakeMemEntry (Domain, Node, DramBase, (TopOfMemoryAbove4Gb - DramBase), BufferLocPtr); isModified = TRUE; } if (DramBase >= TopOfMemoryAbove4Gb) { // IGNORE!!! Entry located entirely above TOM2 isModified = TRUE; } // If special range(isModified), we are done. // If not, finally write the memory entry. if (isModified == FALSE) { // Finally write the memory entry. BufferLocPtr = MakeMemEntry (Domain, Node, DramBase, DramLeng, BufferLocPtr); } } // for ( OffsetRegs = DRAMBase0; ... ) return (BufferLocPtr); } // FillMemoryForCurrentNode() /*---------------------------------------------------------------------------------------*/ /** * This function will add APIC entry. * * @param[in] ApicId APIC ID number * @param[in] Domain Domain number * @param[in] BufferLocPtr Point to the address of buffer * * @retval UINT8 *(New buffer location ptr) */ UINT8 STATIC *MakeApicEntry ( IN UINT8 ApicId, IN UINT8 Domain, IN UINT8 *BufferLocPtr ) { CPU_SRAT_APIC_ENTRY *psSratApicEntry; UINT8 ReservedBytes; psSratApicEntry = (CPU_SRAT_APIC_ENTRY *)BufferLocPtr; psSratApicEntry->Type = AE_APIC; psSratApicEntry->Length = (UINT8)sizeof (CPU_SRAT_APIC_ENTRY); psSratApicEntry->Domain = Domain; psSratApicEntry->ApicId = ApicId; psSratApicEntry->Flags = ENABLED; psSratApicEntry->LSApicEid = 0; for (ReservedBytes = 0; ReservedBytes < (UINT8)sizeof (psSratApicEntry->Reserved); ReservedBytes++) { psSratApicEntry->Reserved[ReservedBytes] = 0; } return (BufferLocPtr + (UINT8)sizeof (CPU_SRAT_APIC_ENTRY)); } // MakeApicEntry /*---------------------------------------------------------------------------------------*/ /** * * This function will add Memory entry. * * Parameters: * @param[in] PDomain Proximity Domain * @param[in] Node The number of Node * @param[in] Base Memory Base * @param[in] Size Memory Size * @param[in] BufferLocPtr Point to the address of buffer * * @retval UINT8 * (new buffer location ptr) */ UINT8 STATIC *MakeMemEntry ( IN UINT8 PDomain, IN UINT8 Node, IN UINT32 Base, IN UINT32 Size, IN UINT8 *BufferLocPtr ) { CPU_SRAT_MEMORY_ENTRY *psSratMemEntry; UINT8 ReservedBytes; psSratMemEntry = (CPU_SRAT_MEMORY_ENTRY *)BufferLocPtr; psSratMemEntry->Type = AE_MEMORY; // [0] = Memory Entry psSratMemEntry->Length = (UINT8)sizeof (CPU_SRAT_MEMORY_ENTRY); // [1] = 40 psSratMemEntry->Domain = PDomain; // [2] = Proximity Domain // [6-7] = Reserved for (ReservedBytes = 0; ReservedBytes < (UINT8)sizeof (psSratMemEntry->Reserved1); ReservedBytes++) { psSratMemEntry->Reserved1[ReservedBytes] = 0; } // [8-11] = Keep 31:0 of address only -> Base Addr Low psSratMemEntry->BaseAddrLow = Base << 16; // [12-15] = Keep 39:32 of address only -> Base Addr High psSratMemEntry->BaseAddrHigh = Base >> 16; // [16-19] = Keep 31:0 of address only -> Length Low psSratMemEntry->LengthAddrLow = Size << 16; // [20-23] = Keep 39:32 of address only -> Length High psSratMemEntry->LengthAddrHigh = Size >> 16; // [24-27] = Reserved for (ReservedBytes = 0; ReservedBytes < (UINT8)sizeof (psSratMemEntry->Reserved2); ReservedBytes++) { psSratMemEntry->Reserved2[ReservedBytes] = 0; } // [28-31] = Flags psSratMemEntry->Flags = ENABLED; // [32-40] = Reserved for (ReservedBytes = 0; ReservedBytes < (UINT8)sizeof (psSratMemEntry->Reserved3); ReservedBytes++) { psSratMemEntry->Reserved3[ReservedBytes] = 0; } return (BufferLocPtr + (UINT8)sizeof (CPU_SRAT_MEMORY_ENTRY)); } // MakeMemEntry()