blob: bf136043d2686cf74c9e9417ad39355accdc589d [file] [log] [blame]
/**
* @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: 44323 $ @e \$Date: 2010-12-22 01:24:58 -0700 (Wed, 22 Dec 2010) $
*
*/
/*
******************************************************************************
*
* Copyright (c) 2011, 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 "cpuRegisters.h"
#include "cpuLateInit.h"
#include "Ids.h"
#include "Filecode.h"
#define FILECODE PROC_CPU_FEATURE_CPUSRAT_FILECODE
/*----------------------------------------------------------------------------------------
* EXPORTED FUNCTIONS
*----------------------------------------------------------------------------------------
*/
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
);
/*----------------------------------------------------------------------------------------
* 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 0x010000
/*----------------------------------------------------------------------------------------
* T Y P E D E F S A N D S T R U C T U R E S
*----------------------------------------------------------------------------------------
*/
/*----------------------------------------------------------------------------
* 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,
{'A','M','D',' ',' ',' '},
{'F','1','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 RegVal;
UINT32 tempVar_32;
AMD_APIC_PARAMS ApicParams;
PCI_ADDR PciAddress;
CPU_SRAT_HEADER *CpuSratHeaderStructPtr;
AGESA_BUFFER_PARAMS AllocParams;
// Get Node count
PciAddress.AddressValue = MAKE_SBDFO (0, 0, LOW_NODE_DEVICEID, FUNC_0, NodeID);
LibAmdPciRead (AccessWidth32 , PciAddress, &RegVal, StdHeader);
NodeCount = (UINT8) (((RegVal >> 4) & 0x7) + 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))
+ (MAX_NUMBER_CORES * sizeof (CPU_SRAT_APIC_ENTRY))));
if (*SratPtr == NULL) {
//
// Allocate a buffer by callback function
//
AllocParams.StdHeader = *StdHeader;
AllocParams.BufferLength = tempVar_32;
AllocParams.BufferHandle = AMD_SRAT_INFO_BUFFER_HANDLE;
AGESA_TESTPOINT (TpProcCpuBeforeAllocateSratBuffer, StdHeader);
if (AgesaAllocateBuffer (0, &AllocParams) != AGESA_SUCCESS) {
return AGESA_ERROR;
}
AGESA_TESTPOINT (TpProcCpuAfterAllocateSratBuffer, StdHeader);
*SratPtr = AllocParams.BufferPointer;
}
CpuSratHeaderStructPtr = (CPU_SRAT_HEADER *) *SratPtr;
BufferPtr = (UINT8 *) *SratPtr;
// Copy acpiSRATHeader -> data buffer
LibAmdMemCopy (*SratPtr, (VOID *) &CpuSratHdrStruct, (UINTN) (sizeof (CPU_SRAT_HEADER)), 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()