/* $NoKeywords:$ */
/**
 * @file
 *
 * Routing Routines
 *
 * Contains routines for isomorphic topology matching,
 * routing determination, and routing initialization.
 *
 * @xrefitem bom "File Content Label" "Release Content"
 * @e project:      AGESA
 * @e sub-project:  HyperTransport
 * @e \$Revision: 56279 $   @e \$Date: 2011-07-11 13:11:28 -0600 (Mon, 11 Jul 2011) $
 *
 */
/*
*****************************************************************************
*
* Copyright (C) 2012 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 "Ids.h"
#include "Topology.h"
#include "htFeat.h"
#include "htInterface.h"
#include "htNotify.h"
#include "htNb.h"
#include "htGraph.h"
#include "htFeatRouting.h"
#include "htTopologies.h"
#include "Filecode.h"
CODE_GROUP (G1_PEICC)
RDATA_GROUP (G2_PEI)

#define FILECODE PROC_HT_FEATURES_HTFEATROUTING_FILECODE
/*----------------------------------------------------------------------------
 *                          DEFINITIONS AND MACROS
 *
 *----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *                           TYPEDEFS AND STRUCTURES
 *
 *----------------------------------------------------------------------------
 */
typedef struct {
  UINT8   **CurrentPosition;
  BOOLEAN IsCustomList;
} TOPOLOGY_CONTEXT;

/*----------------------------------------------------------------------------
 *                        PROTOTYPES OF LOCAL FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *                            EXPORTED FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */

/*----------------------------------------------------------------------------
 *                              LOCAL FUNCTIONS
 *
 *----------------------------------------------------------------------------
 */

/***************************************************************************
 ***          ISOMORPHISM BASED ROUTING TABLE GENERATION CODE            ***
 ***************************************************************************/

/*----------------------------------------------------------------------------------------*/
/**
 * Return the Link on source Node which connects to target Node
 *
 * @param[in] SourceNode   The Node on which to find the Link
 * @param[in] TargetNode   The Link will connect to this Node
 * @param[in] State        Our global state
 *
 * @return    the Link to target
 */
UINT8
STATIC
FindLinkToNode (
  IN       UINT8        SourceNode,
  IN       UINT8        TargetNode,
  IN       STATE_DATA   *State
  )
{
  UINT8 TargetLink;
  UINT8 k;

  // A node linked to itself is not a supported topology graph, this is probably an error in the
  // topology data.  There is not going to be a portlist match for it.
  ASSERT (SourceNode != TargetNode);
  TargetLink = INVALID_LINK;
  for (k = 0; k < State->TotalLinks*2; k += 2) {
    if (((*State->PortList)[k].NodeID == SourceNode) && ((*State->PortList)[k + 1].NodeID == TargetNode)) {
      TargetLink = (*State->PortList)[k].Link;
      break;
    } else if (((*State->PortList)[k + 1].NodeID == SourceNode) && ((*State->PortList)[k].NodeID == TargetNode)) {
      TargetLink = (*State->PortList)[k + 1].Link;
      break;
    }
  }
  ASSERT (TargetLink != INVALID_LINK);

  return TargetLink;
}

/*----------------------------------------------------------------------------------------*/
/**
 *      Is graphA isomorphic to graphB?
 *
 *  If this function returns true, then Perm will contain the permutation
 *  required to transform graphB into graphA.
 *  We also use the degree of each Node, that is the number of connections it has, to
 *  speed up rejection of non-isomorphic graphs (if there is a Node in graphA with n
 *  connections, there must be at least one unmatched in graphB with n connections).
 *
 * @param[in]     Node     the discovered Node which we are trying to match
 *                      with a permutation the topology
 * @param[in,out] State our global state, degree and adjacency matrix,
 *                      output a permutation if successful
 * @retval TRUE         the graphs are isomorphic
 * @retval FALSE        the graphs are not isomorphic
 *
 */
BOOLEAN
STATIC
IsIsomorphic (
  IN       UINT8         Node,
  IN OUT   STATE_DATA    *State
  )
{
  UINT8 j;
  UINT8 k;
  UINT8 Nodecnt;

  // We have only been called if Nodecnt == pSelected->size !
  Nodecnt = State->NodesDiscovered + 1;

  if (Node != Nodecnt) {
    // Keep building the permutation
    for (j = 0; j < Nodecnt; j++) {
      // Make sure the degree matches
      if (State->Fabric->SysDegree[Node] != State->Fabric->DbDegree[j]) {
        continue;
      }

      // Make sure that j hasn't been used yet (ought to use a "used"
      // array instead, might be faster)
      for (k = 0; k < Node; k++) {
        if (State->Fabric->Perm[k] == j) {
          break;
        }
      }
      if (k != Node) {
        continue;
      }
      State->Fabric->Perm[Node] = j;
      if (IsIsomorphic (Node + 1, State)) {
        return TRUE;
      }
    }
    return FALSE;
  } else {
    // Test to see if the permutation is isomorphic
    for (j = 0; j < Nodecnt; j++) {
      for (k = 0; k < Nodecnt; k++) {
        if (State->Fabric->SysMatrix[j][k] != State->Fabric->DbMatrix[State->Fabric->Perm[j]][State->Fabric->Perm[k]] ) {
          return FALSE;
        }
      }
    }
    return TRUE;
  }
}

/*----------------------------------------------------------------------------------------*/
/**
 * Set Topology List iterator context to the Beginning and provide the first topology.
 *
 * Check the interface for a custom topology list.  If one is found, set context to the
 * first item, and return that item. Otherwise return the first item in the built in list.
 *
 * @param[in,out] TopologyContextHandle  Initialize this context to beginning of lists.
 * @param[out]     NextTopology           The next topology, NULL if end.
 * @param[in]      State                  Access to interface, handles.
 *
 */
VOID
STATIC
BeginTopologies (
     OUT   TOPOLOGY_CONTEXT  *TopologyContextHandle,
     OUT   UINT8             **NextTopology,
  IN       STATE_DATA        *State
  )
{
  if (State->HtBlock->Topolist != NULL) {
    // Start with a custom list
    TopologyContextHandle->CurrentPosition = State->HtBlock->Topolist;
    TopologyContextHandle->IsCustomList = TRUE;
  } else {
    // Start with the built in list
    GetAmdTopolist (&TopologyContextHandle->CurrentPosition);
    TopologyContextHandle->IsCustomList = FALSE;
  }
  *NextTopology = *TopologyContextHandle->CurrentPosition;
}

/*----------------------------------------------------------------------------------------*/
/**
 * Iterate through available topologies.
 *
 * Increment to the next list item.  If we are doing a custom list, when we reach the end
 * switch to the built in list.
 *
 * @param[in,out]  TopologyContextHandle  Maintain iterator's context from one call to the next
 * @param[out]     NextTopology           The next topology, NULL if end.
 *
 */
VOID
STATIC
GetNextTopology (
  IN OUT   TOPOLOGY_CONTEXT  *TopologyContextHandle,
     OUT   UINT8             **NextTopology
  )
{
  // Not valid to continue calling this routine after reaching the end.
  ASSERT (TopologyContextHandle->CurrentPosition != NULL);

  if (TopologyContextHandle->IsCustomList) {
    // We are iterating the custom list from the interface.
    TopologyContextHandle->CurrentPosition++;
    if (*TopologyContextHandle->CurrentPosition == NULL) {
      // We are at the end of the custom list, switch to the built in list.
      TopologyContextHandle->IsCustomList = FALSE;
      GetAmdTopolist (&TopologyContextHandle->CurrentPosition);
    }
  } else {
    // We are iterating the built in list
    TopologyContextHandle->CurrentPosition++;
    // If we are at the end of the built in list, NextTopology == NULL is the AtEnd.
  }
  *NextTopology = *TopologyContextHandle->CurrentPosition;
}

/*----------------------------------------------------------------------------------------*/
/**
 * Using the description of the fabric topology we discovered, try to find a match
 * among the supported topologies.
 *
 * @HtFeatMethod{::F_LOOKUP_COMPUTE_AND_LOAD_ROUTING_TABLES}
 *
 * A supported topology description matches the discovered fabric if the Nodes can be
 * matched in such a way that all the Nodes connected in one set are exactly the
 * Nodes connected in the other (formally, that the graphs are isomorphic).  Which
 * Links are used is not really important to matching.  If the graphs match, then
 * there is a permutation of one that translates the Node positions and Linkages to
 * the other.
 *
 * In order to make the isomorphism test efficient, we test for matched number of Nodes
 * (a 4 Node fabric is not isomorphic to a 2 Node topology), and provide degrees of Nodes
 * to the isomorphism test.
 *
 * The generic routing table solution for any topology is predetermined and represented
 * as part of the topology.  The permutation we computed tells us how to interpret the
 * routing onto the fabric we discovered.  We do this working backward from the last
 * Node discovered to the BSP, writing the routing tables as we go.
 *
 * @param[in,out]    State    the discovered fabric, degree matrix, permutation
 *
 */
VOID
LookupComputeAndLoadRoutingTables (
  IN OUT   STATE_DATA    *State
  )
{
  TOPOLOGY_CONTEXT   TopologyContextHandle;
  UINT8 *Selected;
  UINT8 Size;
  UINT8 PairCounter;
  UINT8 ReqTargetLink;
  UINT8 RspTargetLink;
  UINT8 ReqTargetNode;
  UINT8 RspTargetNode;
  UINT8 AbstractBcTargetNodes;
  UINT32 BcTargetLinks;
  UINT8 NodeCounter;
  UINT8 NodeBeingRouted;
  UINT8 NodeRoutedTo;
  UINT8 BroadcastSourceNode;

  Size = State->NodesDiscovered + 1;
  BeginTopologies (&TopologyContextHandle, &Selected, State);
  while (Selected != NULL) {
    if (GraphHowManyNodes (Selected) == Size) {
      // Build Degree vector and Adjacency Matrix for this entry
      for (NodeCounter = 0; NodeCounter < Size; NodeCounter++) {
        State->Fabric->DbDegree[NodeCounter] = 0;
        for (PairCounter = 0; PairCounter < Size; PairCounter++) {
          if (GraphIsAdjacent (Selected, NodeCounter, PairCounter)) {
            State->Fabric->DbMatrix[NodeCounter][PairCounter] = TRUE;
            State->Fabric->DbDegree[NodeCounter]++;
          } else {
            State->Fabric->DbMatrix[NodeCounter][PairCounter] = FALSE;
          }
        }
      }

      if (IsIsomorphic (0, State)) {
        break;  // A matching topology was found
      }
    }
    GetNextTopology (&TopologyContextHandle, &Selected);
  }

  if (Selected != NULL) {
    // Compute the reverse Permutation
    for (NodeCounter = 0; NodeCounter < Size; NodeCounter++) {
      State->Fabric->ReversePerm[State->Fabric->Perm[NodeCounter]] = NodeCounter;
    }

    // Start with the last discovered Node, and move towards the BSP
    for (NodeCounter = 0; NodeCounter < Size; NodeCounter++) {
      NodeBeingRouted = ((Size - 1) - NodeCounter);
      for (NodeRoutedTo = 0; NodeRoutedTo < Size; NodeRoutedTo++) {
        BcTargetLinks = 0;
        AbstractBcTargetNodes = GraphGetBc (Selected, State->Fabric->Perm[NodeBeingRouted], State->Fabric->Perm[NodeRoutedTo]);

        for (BroadcastSourceNode = 0; BroadcastSourceNode < MAX_NODES; BroadcastSourceNode++) {
          if ((AbstractBcTargetNodes & ((UINT32)1 << BroadcastSourceNode)) != 0) {
            // Accepting broadcast from yourself is handled in Nb, so in the topology graph it is an error.
            ASSERT (NodeBeingRouted != State->Fabric->ReversePerm[BroadcastSourceNode]);
            BcTargetLinks |= (UINT32)1 << FindLinkToNode (NodeBeingRouted, State->Fabric->ReversePerm[BroadcastSourceNode], State);
          }
        }

        if (NodeBeingRouted == NodeRoutedTo) {
          ReqTargetLink = ROUTE_TO_SELF;
          RspTargetLink = ROUTE_TO_SELF;
        } else {
          ReqTargetNode = GraphGetReq (Selected, State->Fabric->Perm[NodeBeingRouted], State->Fabric->Perm[NodeRoutedTo]);
          ReqTargetLink = FindLinkToNode (NodeBeingRouted, State->Fabric->ReversePerm[ReqTargetNode], State);

          RspTargetNode = GraphGetRsp (Selected, State->Fabric->Perm[NodeBeingRouted], State->Fabric->Perm[NodeRoutedTo]);
          RspTargetLink = FindLinkToNode (NodeBeingRouted, State->Fabric->ReversePerm[RspTargetNode], State);
        }
        State->Nb->WriteFullRoutingTable (NodeBeingRouted, NodeRoutedTo, ReqTargetLink, RspTargetLink, BcTargetLinks, State->Nb);
      }
      //  Clean up discovery 'footprint' that otherwise remains in the routing table.  It didn't hurt
      // anything, but might cause confusion during debug and validation.  Do this by setting the
      // route back to all self routes. Since it's the Node that would be one more than actually installed,
      // this only applies if less than MaxNodes were found.
      //
      if (Size < MAX_NODES) {
        State->Nb->WriteFullRoutingTable (NodeBeingRouted, Size, ROUTE_TO_SELF, ROUTE_TO_SELF, 0, State->Nb);
      }
    }
  } else {
    //
    // No Matching Topology was found
    // Error Strategy:
    // Auto recovery doesn't seem likely, Force boot as 1P.
    // For reporting, logging, provide number of Nodes
    // If not implemented or returns, boot as BSP uniprocessor.
    //
    // This can be caused by not supplying an additional topology list, if your board is not one of the built-in topologies.
    //
    NotifyErrorCohNoTopology (State->NodesDiscovered, State);
    IDS_ERROR_TRAP;
    // Force 1P
    State->NodesDiscovered = 0;
    State->TotalLinks = 0;
    State->Nb->EnableRoutingTables (0, State->Nb);
    State->HtInterface->CleanMapsAfterError (State);
  }
  // Save the topology pointer, or NULL, for other features
  State->Fabric->MatchedTopology = Selected;
  IDS_HDT_CONSOLE (
    HT_TRACE,
    "System routed as %s.\n",
    ((TopologyContextHandle.IsCustomList) ?
     "custom topology" :
     (((Selected == amdHtTopologySingleNode) || (Selected == NULL)) ?
      "single node" :
      ((Selected == amdHtTopologyDualNode) ?
       "dual node" :
       ((Selected == amdHtTopologyFourSquare) ?
        "four node box" :
        ((Selected == amdHtTopologyFourKite) ?
         "four node kite" :
         ((Selected == amdHtTopologyFourFully) ?
          "fully connected four-way" :
          ((Selected == amdHtTopologyEightDoubloon) ?
           "MCM max performance" :
           ((Selected == amdHtTopologyEightTwinFullyFourWays) ?
            "MCM max I/O" :
            "AMD builtin topology"))))))))
    );
}

/*----------------------------------------------------------------------------------------*/
/**
 * Make a Hop Count Table for the installed topology.
 *
 * @HtFeatMethod{::F_MAKE_HOP_COUNT_TABLE}
 *
 * For SLIT, create a node x node matrix with the number of hops.  We can do this
 * using the topology and the permutation, counting the nodes visited in the routes between
 * nodes.
 *
 * @param[in,out]   State    access topology, permutation, update hop table
 *
 */
VOID
MakeHopCountTable (
  IN OUT   STATE_DATA    *State
  )
{
  UINT8 Origin;
  UINT8 Target;
  UINT8 Current;
  UINT8 Hops;
  UINT8 Size;

  ASSERT (State->Fabric != NULL);
  if (State->HopCountTable != NULL) {
    if (State->Fabric->MatchedTopology != NULL) {
      Size = GraphHowManyNodes (State->Fabric->MatchedTopology);
      State->HopCountTable->Size = Size;
      //
      // For each node, targeting each node, follow the request path through the database graph,
      // counting the number of edges.
      //
      for (Origin = 0; Origin < Size; Origin++) {
        for (Target = 0; Target < Size; Target++) {
          // If both nodes are the same the answer will be zero
          Hops = 0;
          // Current starts as the database node corresponding to system node Origin.
          Current = State->Fabric->Perm[Origin];
          // Stop if Current is the database node corresponding to system node Target
          while (Current != State->Fabric->Perm[Target]) {
            // This is a hop, so count it. Move Current to the next intermediate database node.
            Hops++;
            Current = GraphGetReq (State->Fabric->MatchedTopology, Current, State->Fabric->Perm[Target]);
          }
          // Put the hop count in the table.
          State->HopCountTable->Hops[ ((Origin * Size) + Target)] = Hops;
        }
      }
    }
  }
}
