/* $NoKeywords:$ */ /** * @file * * External Interface implementation, general purpose features. * * Contains routines for implementing the interface to the client BIOS. This file * includes the interface support which is not removed with various build options. * * @xrefitem bom "File Content Label" "Release Content" * @e project: AGESA * @e sub-project: HyperTransport * @e \$Revision: 48937 $ @e \$Date: 2011-03-15 03:37:15 +0800 (Tue, 15 Mar 2011) $ * */ /* ***************************************************************************** * * 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. * *************************************************************************** * */ /* *---------------------------------------------------------------------------- * MODULES USED * *---------------------------------------------------------------------------- */ #include "AGESA.h" #include "amdlib.h" #include "OptionMultiSocket.h" #include "Ids.h" #include "Topology.h" #include "htFeat.h" #include "htInterface.h" #include "htInterfaceGeneral.h" #include "htNb.h" #include "cpuRegisters.h" #include "cpuServices.h" #include "cpuFeatures.h" #include "heapManager.h" #include "Filecode.h" CODE_GROUP (G1_PEICC) RDATA_GROUP (G1_PEICC) #define FILECODE PROC_HT_HTINTERFACEGENERAL_FILECODE /*---------------------------------------------------------------------------- * DEFINITIONS AND MACROS * *---------------------------------------------------------------------------- */ extern OPTION_MULTISOCKET_CONFIGURATION OptionMultiSocketConfiguration; /*---------------------------------------------------------------------------- * TYPEDEFS AND STRUCTURES * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * PROTOTYPES OF LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * EXPORTED FUNCTIONS * *---------------------------------------------------------------------------- */ /*---------------------------------------------------------------------------- * LOCAL FUNCTIONS * *---------------------------------------------------------------------------- */ /*----------------------------------------------------------------------------------------*/ /** * Is PackageLink an Internal Link? * * This is a test for the logical link match codes in the user interface, not a test for * the actual northbridge links. * * @param[in] PackageLink The link * * @retval TRUE This is an internal link * @retval FALSE This is not an internal link */ BOOLEAN IsPackageLinkInternal ( IN UINT8 PackageLink ) { return (BOOLEAN) ((PackageLink <= HT_LIST_MATCH_INTERNAL_LINK_2) && (PackageLink >= HT_LIST_MATCH_INTERNAL_LINK_0)); } /*----------------------------------------------------------------------------------------*/ /** * Ignore a Link. * * @HtInterfaceMethod{::F_GET_IGNORE_LINK} * * This routine is called every time a coherent Link is found and then every time a * non-coherent Link from a CPU is found. Any coherent or non-coherent Link from a * CPU can be ignored and not used for discovery or initialization. Useful for * connection based systems. * * @note not called for IO device to IO Device Links. * * @param[in] Node The Node on which this Link is located * @param[in] Link The Link about to be initialized * @param[in] NbIgnoreLinkList The northbridge default ignore link list * @param[in] State the input data * * @retval MATCHED ignore this Link and skip it * @retval POWERED_OFF ignore this link and power it off. * @retval UNMATCHED initialize the Link normally */ FINAL_LINK_STATE GetIgnoreLink ( IN UINT8 Node, IN UINT8 Link, IN IGNORE_LINK *NbIgnoreLinkList, IN STATE_DATA *State ) { IGNORE_LINK *p; FINAL_LINK_STATE Result; BOOLEAN IsFound; UINT8 Socket; UINT8 PackageLink; ASSERT ((Node < MAX_NODES) && (Link < MAX_NODES)); Result = UNMATCHED; IsFound = FALSE; Socket = State->HtInterface->GetSocketFromMap (Node, State); PackageLink = State->Nb->GetPackageLink (Node, Link, State->Nb); if (State->HtBlock->IgnoreLinkList != NULL) { p = State->HtBlock->IgnoreLinkList; while (p->Socket != HT_LIST_TERMINAL) { if (((p->Socket == Socket) || (p->Socket == HT_LIST_MATCH_ANY)) && ((p->Link == PackageLink) || ((p->Link == HT_LIST_MATCH_ANY) && (!IsPackageLinkInternal (PackageLink))) || ((p->Link == HT_LIST_MATCH_INTERNAL_LINK) && (IsPackageLinkInternal (PackageLink))))) { // Found a match return the desired link state. ASSERT (Result < MaxFinalLinkState); Result = p->LinkState; IsFound = TRUE; break; } else { p++; } } } // If there wasn't a match in the user interface, see if the northbridge provides one. if (!IsFound && (NbIgnoreLinkList != NULL)) { p = NbIgnoreLinkList; while (p->Socket != HT_LIST_TERMINAL) { if (((p->Socket == Socket) || (p->Socket == HT_LIST_MATCH_ANY)) && ((p->Link == PackageLink) || ((p->Link == HT_LIST_MATCH_ANY) && (!IsPackageLinkInternal (PackageLink))) || ((p->Link == HT_LIST_MATCH_INTERNAL_LINK) && (IsPackageLinkInternal (PackageLink))))) { // Found a match return the desired link state. ASSERT (Result < MaxFinalLinkState); Result = p->LinkState; break; } else { p++; } } } return Result; } /*----------------------------------------------------------------------------------------*/ /** * Get the Socket number for a given Node number. * * @HtInterfaceMethod{::F_GET_SOCKET_FROM_MAP} * * Return the id. * * @param[in] Node The Node to translate * @param[in] State reference to Node to socket map * * @return the socket id * */ UINT8 GetSocketFromMap ( IN UINT8 Node, IN STATE_DATA *State ) { UINT8 Socket; ASSERT (State->NodeToSocketDieMap != NULL); Socket = (*State->NodeToSocketDieMap)[Node].Socket; return Socket; } /*----------------------------------------------------------------------------------------*/ /** * Get a new Socket Die to Node Map. * * @HtInterfaceMethod{::F_NEW_NODE_AND_SOCKET_TABLES} * * Put the Socket Die Table in heap with a known handle. Content will be generated as * each node is discovered. * * @param[in,out] State global state */ VOID NewNodeAndSocketTables ( IN OUT STATE_DATA *State ) { UINT8 i; UINT8 j; ALLOCATE_HEAP_PARAMS AllocHeapParams; // Allocate heap for the table State->SocketDieToNodeMap = NULL; AllocHeapParams.RequestedBufferSize = (((MAX_SOCKETS) * (MAX_DIES)) * sizeof (SOCKET_DIE_TO_NODE_ITEM)); AllocHeapParams.BufferHandle = SOCKET_DIE_MAP_HANDLE; AllocHeapParams.Persist = HEAP_SYSTEM_MEM; if (HeapAllocateBuffer (&AllocHeapParams, State->ConfigHandle) == AGESA_SUCCESS) { State->SocketDieToNodeMap = (SOCKET_DIE_TO_NODE_MAP)AllocHeapParams.BufferPtr; // Initialize shared data structures for (i = 0; i < MAX_SOCKETS; i++) { for (j = 0; j < MAX_DIES; j++) { (*State->SocketDieToNodeMap)[i][j].Node = HT_LIST_TERMINAL; (*State->SocketDieToNodeMap)[i][j].LowCore = HT_LIST_TERMINAL; (*State->SocketDieToNodeMap)[i][j].HighCore = HT_LIST_TERMINAL; } } } // Allocate heap for the table State->NodeToSocketDieMap = NULL; AllocHeapParams.RequestedBufferSize = (MAX_NODES * sizeof (NODE_TO_SOCKET_DIE_ITEM)); AllocHeapParams.BufferHandle = NODE_ID_MAP_HANDLE; AllocHeapParams.Persist = HEAP_SYSTEM_MEM; if (HeapAllocateBuffer (&AllocHeapParams, State->ConfigHandle) == AGESA_SUCCESS) { State->NodeToSocketDieMap = (NODE_TO_SOCKET_DIE_MAP)AllocHeapParams.BufferPtr; // Initialize shared data structures for (i = 0; i < MAX_NODES; i++) { (*State->NodeToSocketDieMap)[i].Socket = HT_LIST_TERMINAL; (*State->NodeToSocketDieMap)[i].Die = HT_LIST_TERMINAL; } } } /*----------------------------------------------------------------------------------------*/ /** * Get the minimum Northbridge frequency for the system. * * @HtInterfaceMethod{::F_GET_MIN_NB_CORE_FREQ} * * Invoke the CPU component power mgt interface. * * @param[in] PlatformConfig Platform profile/build option config structure. * @param[in] StdHeader Config for library and services. * * @return Frequency in MHz. * */ UINT32 GetMinNbCoreFreq ( IN PLATFORM_CONFIGURATION *PlatformConfig, IN AMD_CONFIG_PARAMS *StdHeader ) { UINT32 MinSysNbFreq; UINT32 MinP0NbFreq; OptionMultiSocketConfiguration.GetMinNbCof (PlatformConfig, &MinSysNbFreq, &MinP0NbFreq, StdHeader); ASSERT (MinSysNbFreq != 0); return MinSysNbFreq; } /** * @page physicalsockethowto Physical Socket Map, How To Create * * To create a physical system socket map for a platform: * * - Start at the Node which will be the BSP. * * - Begin a breadth first enumeration of all the coherent Links between sockets * by creating a socket structure for each socket connection from the BSP. * For example, if the BSP is in socket zero and Link one connects to socket two, * create socket {0, 1, 2}. * * - When all Links from the BSP are described, go to the first socket connected * to the BSP and continue the breadth first enumeration. * * - It should not be necessary to describe the back Links; in the example above, there * should be no need to create {2, 1, 0} (assuming socket two connects back to * socket zero on its Link one). * * - When completed: * * - Every socket except the BSP's (usually zero) must be listed as a targetSocket, * at least once. Some sockets may be listed more than once. * * - There usually should be at least as many entries as Links. An exception is a * fully connected system, only the Links from the BSP are needed. * * - Every socket but the last one in the breadth first order should usually have one * or more entries listing it as a currentSocket. (The last one has only back Links.) * * There are no strict assumptions about the ordering of the socket structures. */ /*----------------------------------------------------------------------------------------*/ /** * Update maps between Sockets and Nodes for a specific newly discovered node. * * @HtInterfaceMethod{::F_SET_NODE_TO_SOCKET_MAP} * * There are two methods for providing socket naming of nodes. * * Hardware Method (preferred): A value strapped in hardware by the board is read and * passed to this routine. * * Software Method: The current node's socket is looked up, since it was * previously a new node and went through this process. The link is converted to * a package level link. A user data structure describing the package level * layout of the system is searched for the current node's socket and package link, * and now we know the new node's socket. * * In either case, the Socket, Module to Node map and the Node to Socket, Module * map are updated with the new node, socket, and module. * * Data needed to do this is passed in to the routine as arguments rather than read by this routine, * so that it is not necessary to know a valid temporary route to either node at the time this code runs. * * @param[in] Node Node from which a new node was discovered * @param[in] CurrentNodeModule The current node's module id in it's processor. * @param[in] PackageLink The package link for the current node's link. * @param[in] NewNode The new node's id * @param[in] HardwareSocket If we use the hardware method (preferred), this is the socket of new node. * @param[in] Module The new node's module id in it's processor. * @param[in] State our State */ VOID SetNodeToSocketMap ( IN UINT8 Node, IN UINT8 CurrentNodeModule, IN UINT8 PackageLink, IN UINT8 NewNode, IN UINT8 HardwareSocket, IN UINT8 Module, IN STATE_DATA *State ) { UINT8 SourceSocket; UINT8 TargetSocket; SYSTEM_PHYSICAL_SOCKET_MAP *Map; // While this code could be written to recover from a NULL socket map, AGESA cannot function without one. ASSERT (State->SocketDieToNodeMap != NULL); if (State->HtBlock->SystemPhysicalSocketMap != NULL) { if (NewNode != 0) { // Find the logical Node from which a new Node was discovered in the Node field of // some socket. It must already be there, Nodes are assigned ascending. // for (SourceSocket = 0; SourceSocket < MAX_SOCKETS; SourceSocket++) { if ((*State->SocketDieToNodeMap)[SourceSocket][CurrentNodeModule].Node == Node) { break; } } // This ASSERT should be understood as "the Node did not have a match", not as a limit check on SourceSocket. ASSERT (SourceSocket != MAX_SOCKETS); // Find the sourceSocket in the CurrentSocket field, for the Link on which a new Node // was discovered. When we find an entry with that socket and Link number, update the // Node for that socket. // if (IsPackageLinkInternal (PackageLink)) { // Internal Nodes are in the same socket, don't search the physical system map. TargetSocket = SourceSocket; } else { // Find the target socket in the physical system map. Map = State->HtBlock->SystemPhysicalSocketMap; while ((Map->CurrentSocket != 0xFF) && ((Map->CurrentSocket != SourceSocket) || (Map->CurrentLink != PackageLink))) { Map++; } ASSERT (Map->CurrentSocket != 0xFF); TargetSocket = Map->TargetSocket; } } else { // The BSP (BSN, if you will) has no predecessor node from which it is discovered. TargetSocket = 0; } } else { // Use the hardware method // The hardware strapped socket id is passed to us in this case. TargetSocket = HardwareSocket; } // If the target socket, module is already mapped to something, that's not good. Socket labeling conflict. // Check that the board is strapped correctly. If not you need a SystemPhysicalSocketMap. If you have one, // check it for correctness. ASSERT ((*State->SocketDieToNodeMap)[TargetSocket][Module].Node == 0xFF); // Update the map for the rest of agesa (*State->SocketDieToNodeMap)[TargetSocket][Module].Node = NewNode; // and the node to socket map ASSERT (State->NodeToSocketDieMap != NULL); (*State->NodeToSocketDieMap)[NewNode].Socket = TargetSocket; (*State->NodeToSocketDieMap)[NewNode].Die = Module; } /*----------------------------------------------------------------------------------------*/ /** * Clean up the map structures after severe event has caused a fall back to 1 node. * * @HtInterfaceMethod{::F_CLEAN_MAPS_AFTER_ERROR} * * @param[in] State Our state, access to socket, node maps * */ VOID CleanMapsAfterError ( IN STATE_DATA *State ) { UINTN Socket; UINTN Module; UINTN Node; ASSERT (State->NodeToSocketDieMap != NULL); ASSERT (State->SocketDieToNodeMap != NULL); // Clear all the socket, module items except for the socket and module containing node zero. for (Socket = 0; Socket < MAX_SOCKETS; Socket++) { for (Module = 0; Module < MAX_DIES; Module++) { if (((*State->NodeToSocketDieMap)[0].Socket != Socket) || ((*State->NodeToSocketDieMap)[0].Die != Module)) { (*State->SocketDieToNodeMap)[Socket][Module].Node = HT_LIST_TERMINAL; (*State->SocketDieToNodeMap)[Socket][Module].LowCore = HT_LIST_TERMINAL; (*State->SocketDieToNodeMap)[Socket][Module].HighCore = HT_LIST_TERMINAL; } } } // Clear all the node items except for node zero. for (Node = 1; Node < MAX_NODES; Node++) { (*State->NodeToSocketDieMap)[Node].Socket = HT_LIST_TERMINAL; (*State->NodeToSocketDieMap)[Node].Die = HT_LIST_TERMINAL; } } /*----------------------------------------------------------------------------------------*/ /** * Post Node id and other context info to AP cores via mailbox. * * @HtInterfaceMethod{::F_POST_MAP_TO_AP} * * Since Ap's can not view map until after mp communication is established, * provide them with initial context info via a mailbox register. A mailbox * register is one that can be written in PCI space and read in MSR space. * * @param[in] State Our state, access to socket, node maps */ VOID PostMapToAp ( IN STATE_DATA *State ) { UINT8 ModuleType; UINT8 Module; AP_MAILBOXES ApMailboxes; UINT8 Node; UINT32 Degree; AGESA_STATUS CalledStatus; // Dispatch any features (such as Preserve Mailbox) that need to run as soon as discovery is completed. IDS_HDT_CONSOLE (CPU_TRACE, " Dispatch CPU features after HT discovery\n"); CalledStatus = DispatchCpuFeatures (CPU_FEAT_AFTER_COHERENT_DISCOVERY, State->PlatformConfiguration, State->ConfigHandle); ASSERT (State->Fabric != NULL); Degree = 0; // Compute the degree of the system by finding the maximum degree of any node. for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) { if (State->Fabric->SysDegree[Node] > Degree) { Degree = State->Fabric->SysDegree[Node]; } } // Post the information on all nodes. for (Node = 0; Node < (State->NodesDiscovered + 1); Node++) { ModuleType = 0; Module = 0; State->Nb->GetModuleInfo (Node, &ModuleType, &Module, State->Nb); ApMailboxes.ApMailInfo.Info = 0; ApMailboxes.ApMailInfo.Fields.Node = Node; ApMailboxes.ApMailInfo.Fields.Socket = State->HtInterface->GetSocketFromMap (Node, State); ApMailboxes.ApMailInfo.Fields.ModuleType = ModuleType; ApMailboxes.ApMailInfo.Fields.Module = Module; ApMailboxes.ApMailExtInfo.Info = 0; ApMailboxes.ApMailExtInfo.Fields.SystemDegree = Degree; // other fields of the extended info are used during ap init, and will be initialized at that time. State->Nb->PostMailbox (Node, ApMailboxes, State->Nb); } // Now that the mailboxes have been initialized, cache the info on the BSC. The APs // will cache during heap initialization. CacheApMailbox (State->ConfigHandle); }