/** @file
  Configuration Manager object token fixer

  Copyright (c) 2021, Arm Limited. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

  @par Glossary:
    - Cm or CM   - Configuration Manager
    - Obj or OBJ - Object
**/

#include <Library/DebugLib.h>
#include <Protocol/ConfigurationManagerProtocol.h>
#include "CmObjectTokenFixer.h"

/** Token fixer not implemented.

  Most of the objects are not generated by this parser. Add the missing
  functions when needed.

  CmObjectToken fixer function that updates the Tokens in the CmObjects.

  @param [in]  CmObject    Pointer to the Configuration Manager Object.
  @param [in]  Token       Token to be updated in the CmObject.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_UNSUPPORTED       Not supported.
**/
STATIC
EFI_STATUS
EFIAPI
TokenFixerNotImplemented (
  IN  CM_OBJ_DESCRIPTOR  *CmObject,
  IN  CM_OBJECT_TOKEN    Token
  )
{
  ASSERT (0);
  return EFI_UNSUPPORTED;
}

/** EArmObjItsGroup token fixer.

  CmObjectToken fixer function that updates the Tokens in the CmObjects.

  @param [in]  CmObject    Pointer to the Configuration Manager Object.
  @param [in]  Token       Token to be updated in the CmObject.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_UNSUPPORTED       Not supported.
**/
STATIC
EFI_STATUS
EFIAPI
TokenFixerItsGroup (
  IN  CM_OBJ_DESCRIPTOR  *CmObject,
  IN  CM_OBJECT_TOKEN    Token
  )
{
  ASSERT (CmObject != NULL);
  ((CM_ARM_ITS_GROUP_NODE *)CmObject->Data)->Token = Token;
  return EFI_SUCCESS;
}

/** EArmObjNamedComponent token fixer.

  CmObjectToken fixer function that updates the Tokens in the CmObjects.

  @param [in]  CmObject    Pointer to the Configuration Manager Object.
  @param [in]  Token       Token to be updated in the CmObject.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_UNSUPPORTED       Not supported.
**/
STATIC
EFI_STATUS
EFIAPI
TokenFixerNamedComponentNode (
  IN  CM_OBJ_DESCRIPTOR  *CmObject,
  IN  CM_OBJECT_TOKEN    Token
  )
{
  ASSERT (CmObject != NULL);
  ((CM_ARM_NAMED_COMPONENT_NODE *)CmObject->Data)->Token = Token;
  return EFI_SUCCESS;
}

/** EArmObjRootComplex token fixer.

  CmObjectToken fixer function that updates the Tokens in the CmObjects.

  @param [in]  CmObject    Pointer to the Configuration Manager Object.
  @param [in]  Token       Token to be updated in the CmObject.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_UNSUPPORTED       Not supported.
**/
STATIC
EFI_STATUS
EFIAPI
TokenFixerRootComplexNode (
  IN  CM_OBJ_DESCRIPTOR  *CmObject,
  IN  CM_OBJECT_TOKEN    Token
  )
{
  ASSERT (CmObject != NULL);
  ((CM_ARM_ROOT_COMPLEX_NODE *)CmObject->Data)->Token = Token;
  return EFI_SUCCESS;
}

/** EArmObjSmmuV3 token fixer.

  CmObjectToken fixer function that updates the Tokens in the CmObjects.

  @param [in]  CmObject    Pointer to the Configuration Manager Object.
  @param [in]  Token       Token to be updated in the CmObject.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_UNSUPPORTED       Not supported.
**/
STATIC
EFI_STATUS
EFIAPI
TokenFixerSmmuV3Node (
  IN  CM_OBJ_DESCRIPTOR  *CmObject,
  IN  CM_OBJECT_TOKEN    Token
  )
{
  ASSERT (CmObject != NULL);
  ((CM_ARM_SMMUV3_NODE *)CmObject->Data)->Token = Token;
  return EFI_SUCCESS;
}

/** ERiscVObjIsaString token fixer.

  CmObjectToken fixer function that updates the Tokens in the CmObjects.

  @param [in]  CmObject    Pointer to the Configuration Manager Object.
  @param [in]  Token       Token to be updated in the CmObject.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_UNSUPPORTED       Not supported.
**/
STATIC
EFI_STATUS
EFIAPI
TokenFixerIsaStringNode (
  IN  CM_OBJ_DESCRIPTOR  *CmObject,
  IN  CM_OBJECT_TOKEN    Token
  )
{
  ASSERT (CmObject != NULL);
  ((CM_RISCV_ISA_STRING_NODE *)CmObject->Data)->Token = Token;
  return EFI_SUCCESS;
}

/** ERiscVObjCmoNode token fixer.

  CmObjectToken fixer function that updates the Tokens in the CmObjects.

  @param [in]  CmObject    Pointer to the Configuration Manager Object.
  @param [in]  Token       Token to be updated in the CmObject.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_UNSUPPORTED       Not supported.
**/
STATIC
EFI_STATUS
EFIAPI
TokenFixerCmoNode (
  IN  CM_OBJ_DESCRIPTOR  *CmObject,
  IN  CM_OBJECT_TOKEN    Token
  )
{
  ASSERT (CmObject != NULL);
  ((CM_RISCV_CMO_NODE *)CmObject->Data)->Token = Token;
  return EFI_SUCCESS;
}

/** ERiscVObjMmuNode token fixer.

  CmObjectToken fixer function that updates the Tokens in the CmObjects.

  @param [in]  CmObject    Pointer to the Configuration Manager Object.
  @param [in]  Token       Token to be updated in the CmObject.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_UNSUPPORTED       Not supported.
**/
STATIC
EFI_STATUS
EFIAPI
TokenFixerMmuNode (
  IN  CM_OBJ_DESCRIPTOR  *CmObject,
  IN  CM_OBJECT_TOKEN    Token
  )
{
  ASSERT (CmObject != NULL);
  ((CM_RISCV_MMU_NODE *)CmObject->Data)->Token = Token;
  return EFI_SUCCESS;
}

/** TokenFixer functions table.

  A CmObj having a CM_OBJECT_TOKEN field might need to have its
  Token fixed. Each CmObj can have its Token in a specific way.
*/
CONST
CM_OBJECT_TOKEN_FIXER  ArmTokenFixer[EArmObjMax] = {
  NULL,                             ///<  0 - Reserved
  NULL,                             ///<  1 - Boot Architecture Info
  NULL,                             ///<  2 - GIC CPU Interface Info
  NULL,                             ///<  3 - GIC Distributor Info
  NULL,                             ///<  4 - GIC MSI Frame Info
  NULL,                             ///<  5 - GIC Redistributor Info
  NULL,                             ///<  6 - GIC ITS Info
  NULL,                             ///<  7 - Generic Timer Info
  NULL,                             ///<  8 - Platform GT Block Info
  NULL,                             ///<  9 - Generic Timer Block Frame Info
  NULL,                             ///< 10 - Platform Generic Watchdog
  TokenFixerItsGroup,               ///< 11 - ITS Group
  TokenFixerNamedComponentNode,     ///< 12 - Named Component
  TokenFixerRootComplexNode,        ///< 13 - Root Complex
  TokenFixerNotImplemented,         ///< 14 - SMMUv1 or SMMUv2
  TokenFixerSmmuV3Node,             ///< 15 - SMMUv3
  TokenFixerNotImplemented,         ///< 16 - PMCG
  NULL,                             ///< 17 - GIC ITS Identifier Array
  NULL,                             ///< 18 - ID Mapping Array
  NULL,                             ///< 19 - SMMU Interrupt Array
  NULL,                             ///< 20 - CMN-600 Info
  NULL,                             ///< 21 - Reserved Memory Range Node
  NULL,                             ///< 22 - Memory Range Descriptor
  NULL                              ///< 23 - Embedded Trace Extension/Module Info
};

/** TokenFixer functions table for RISC-V.

  A CmObj having a CM_OBJECT_TOKEN field might need to have its
  Token fixed. Each CmObj can have its Token in a specific way.
*/
CONST
CM_OBJECT_TOKEN_FIXER  RiscVTokenFixer[ERiscVObjMax] = {
  NULL,                             ///<  0 - Reserved
  NULL,                             ///<  1 - RINTC Info
  NULL,                             ///<  2 - IMSIC Info
  NULL,                             ///<  3 - APLIC Info
  NULL,                             ///<  4 - PLIC Info
  TokenFixerIsaStringNode,          ///<  5 - ISA String Info
  TokenFixerCmoNode,                ///<  6 - CMO Info
  TokenFixerMmuNode,                ///<  7 - MMU Info
  NULL,                             ///<  8 - Timer Type Info
};

/** CmObj token fixer.

  Some CmObj structures have a self-token, i.e. they are storing their own
  token value in the CmObj. Dynamically created CmObj need to have their
  self-token assigned at some point.

  @param [in]  CmObjDesc   Pointer to the Configuration Manager Object.
  @param [in]  Token       Token to update the CmObjDesc with.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_UNSUPPORTED       Not supported.
**/
EFI_STATUS
EFIAPI
FixupCmObjectSelfToken (
  IN  CM_OBJ_DESCRIPTOR  *CmObjDesc,
  IN  CM_OBJECT_TOKEN    Token
  )
{
  EFI_STATUS             Status;
  CM_OBJECT_TOKEN_FIXER  TokenFixerFunc;
  CM_OBJECT_ID           ArmNamespaceObjId;
  CM_OBJECT_ID           RiscVNamespaceObjId;

  if (CmObjDesc == NULL) {
    ASSERT (0);
    return EFI_INVALID_PARAMETER;
  }

  // Only support Arm and RISC-V objects for now.
  if (GET_CM_NAMESPACE_ID (CmObjDesc->ObjectId) == EObjNameSpaceArm) {
    ArmNamespaceObjId = GET_CM_OBJECT_ID (CmObjDesc->ObjectId);
    if (ArmNamespaceObjId >= EArmObjMax) {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }

    // Fixup self-token if necessary.
    TokenFixerFunc = ArmTokenFixer[ArmNamespaceObjId];
  } else if (GET_CM_NAMESPACE_ID (CmObjDesc->ObjectId) == EObjNameSpaceRiscV) {
    RiscVNamespaceObjId = GET_CM_OBJECT_ID (CmObjDesc->ObjectId);
    if (RiscVNamespaceObjId >= ERiscVObjMax) {
      ASSERT (0);
      return EFI_INVALID_PARAMETER;
    }

    // Fixup self-token if necessary.
    TokenFixerFunc = RiscVTokenFixer[RiscVNamespaceObjId];
  } else {
    ASSERT (
      (GET_CM_NAMESPACE_ID (CmObjDesc->ObjectId) == EObjNameSpaceArm) ||
      (GET_CM_NAMESPACE_ID (CmObjDesc->ObjectId) == EObjNameSpaceRiscV)
      );
    return EFI_UNSUPPORTED;
  }

  if (TokenFixerFunc != NULL) {
    Status = TokenFixerFunc (CmObjDesc, Token);
    if (EFI_ERROR (Status)) {
      ASSERT (0);
      return Status;
    }
  }

  return EFI_SUCCESS;
}
