/** @file
  X64 FADT Table Helpers

  Copyright (c) 2024 Advanced Micro Devices, Inc. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

  @par Reference(s):
  - ACPI 6.5 Specification, Aug 29, 2022

**/

#include <X64NameSpaceObjects.h>
#include <Library/BaseMemoryLib.h>
#include <Library/AcpiLib.h>
#include <Library/DebugLib.h>
#include <Protocol/AcpiTable.h>

// Module specific include files.
#include <AcpiTableGenerator.h>
#include <ConfigurationManagerObject.h>
#include <ConfigurationManagerHelper.h>
#include <Library/TableHelperLib.h>
#include <Protocol/ConfigurationManagerProtocol.h>
#include "FadtGenerator.h"

/** This macro expands to a function that retrieves the
    SCI interrupt information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceX64,
  EX64ObjFadtSciInterrupt,
  CM_X64_FADT_SCI_INTERRUPT
  );

/** This macro expands to a function that retrieves the
    SCI command information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceX64,
  EX64ObjFadtSciCmdInfo,
  CM_X64_FADT_SCI_CMD_INFO
  );

/** This macro expands to a function that retrieves the
    legacy power management information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceX64,
  EX64ObjFadtPmBlockInfo,
  CM_X64_FADT_PM_BLOCK_INFO
  );

/** This macro expands to a function that retrieves the
    legacy GPE block information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceX64,
  EX64ObjFadtGpeBlockInfo,
  CM_X64_FADT_GPE_BLOCK_INFO
  );

/** This macro expands to a function that retrieves the
    legacy level2 latency, level 3 latency, RTC information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceX64,
  EX64ObjFadtMiscInfo,
  CM_X64_FADT_MISC_INFO
  );

/** This macro expands to a function that retrieves the
    64-bit power management information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceX64,
  EX64ObjFadtXpmBlockInfo,
  CM_X64_FADT_X_PM_BLOCK_INFO
  );

/** This macro expands to a function that retrieves the
    64-bit GPE block information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceX64,
  EX64ObjFadtXgpeBlockInfo,
  CM_X64_FADT_X_GPE_BLOCK_INFO
  );

/** This macro expands to a function that retrieves the
    sleep block information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceX64,
  EX64ObjFadtSleepBlockInfo,
  CM_X64_FADT_SLEEP_BLOCK_INFO
  );

/** This macro expands to a function that retrieves the
    reset block information from the Configuration Manager.
*/
GET_OBJECT_LIST (
  EObjNameSpaceX64,
  EX64ObjFadtResetBlockInfo,
  CM_X64_FADT_RESET_BLOCK_INFO
  );

/** Updates the Architecture specific information in the FADT Table.

  @param [in]  CfgMgrProtocol Pointer to the Configuration Manager
                              Protocol Interface.
  @param [in, out] Fadt       Pointer to the constructed ACPI Table.

  @retval EFI_SUCCESS           Success.
  @retval EFI_INVALID_PARAMETER A parameter is invalid.
  @retval EFI_NOT_FOUND         The required object was not found.
  @retval EFI_BAD_BUFFER_SIZE   The size returned by the Configuration
                                Manager is less than the Object size for the
                                requested object.
**/
EFI_STATUS
EFIAPI
FadtArchUpdate (
  IN  CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL  *CONST  CfgMgrProtocol,
  IN   OUT EFI_ACPI_6_5_FIXED_ACPI_DESCRIPTION_TABLE      *Fadt
  )
{
  EFI_STATUS                    Status;
  CM_X64_FADT_SCI_INTERRUPT     *SciInterrupt;
  CM_X64_FADT_SCI_CMD_INFO      *SciCmdinfo;
  CM_X64_FADT_PM_BLOCK_INFO     *PmBlockInfo;
  CM_X64_FADT_GPE_BLOCK_INFO    *GpeBlockInfo;
  CM_X64_FADT_X_PM_BLOCK_INFO   *XpmBlockInfo;
  CM_X64_FADT_X_GPE_BLOCK_INFO  *XgpeBlockInfo;
  CM_X64_FADT_SLEEP_BLOCK_INFO  *SleepBlockInfo;
  CM_X64_FADT_RESET_BLOCK_INFO  *ResetBlockInfo;
  CM_X64_FADT_MISC_INFO         *FadtMiscInfo;

  ASSERT (CfgMgrProtocol != NULL);
  ASSERT (Fadt != NULL);

  // Get the SCI interrupt from the Platform Configuration Manager
  Status = GetEX64ObjFadtSciInterrupt (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &SciInterrupt,
             NULL
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: FADT: Failed to get SCI Interrupt information." \
      " Status = %r\n",
      Status
      ));
  } else {
    Fadt->SciInt = SciInterrupt->SciInterrupt;
  }

  // Get the SCI CMD information from the Platform Configuration Manager
  Status = GetEX64ObjFadtSciCmdInfo (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &SciCmdinfo,
             NULL
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: FADT: Failed to get SCI CMD information." \
      " Status = %r\n",
      Status
      ));
  } else {
    Fadt->SmiCmd      = SciCmdinfo->SciCmd;
    Fadt->AcpiEnable  = SciCmdinfo->AcpiEnable;
    Fadt->AcpiDisable = SciCmdinfo->AcpiDisable;
    Fadt->S4BiosReq   = SciCmdinfo->S4BiosReq;
    Fadt->PstateCnt   = SciCmdinfo->PstateCnt;
    Fadt->CstCnt      = SciCmdinfo->CstCnt;
  }

  // Get the SCI PM Block information from the Platform Configuration Manager
  Status = GetEX64ObjFadtPmBlockInfo (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &PmBlockInfo,
             NULL
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: FADT: Failed to get PM Block information." \
      " Status = %r\n",
      Status
      ));
  } else {
    Fadt->Pm1aEvtBlk = PmBlockInfo->Pm1aEvtBlk;
    Fadt->Pm1bEvtBlk = PmBlockInfo->Pm1bEvtBlk;
    Fadt->Pm1aCntBlk = PmBlockInfo->Pm1aCntBlk;
    Fadt->Pm1bCntBlk = PmBlockInfo->Pm1bCntBlk;
    Fadt->Pm2CntBlk  = PmBlockInfo->Pm2CntBlk;
    Fadt->PmTmrBlk   = PmBlockInfo->PmTmrBlk;
    Fadt->Pm1EvtLen  = PmBlockInfo->Pm1EvtLen;
    Fadt->Pm1CntLen  = PmBlockInfo->Pm1CntLen;
    Fadt->Pm2CntLen  = PmBlockInfo->Pm2CntLen;
    Fadt->PmTmrLen   = PmBlockInfo->PmTmrLen;
  }

  // Get the SCI PM Block information from the Platform Configuration Manager
  Status = GetEX64ObjFadtGpeBlockInfo (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &GpeBlockInfo,
             NULL
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: FADT: Failed to get PM Block information." \
      " Status = %r\n",
      Status
      ));
  } else {
    Fadt->Gpe0Blk    = GpeBlockInfo->Gpe0Blk;
    Fadt->Gpe1Blk    = GpeBlockInfo->Gpe1Blk;
    Fadt->Gpe0BlkLen = GpeBlockInfo->Gpe0BlkLen;
    Fadt->Gpe1BlkLen = GpeBlockInfo->Gpe1BlkLen;
    Fadt->Gpe1Base   = GpeBlockInfo->Gpe1Base;
  }

  // Get the 64-bit PM Block information from the Platform Configuration Manager
  Status = GetEX64ObjFadtXpmBlockInfo (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &XpmBlockInfo,
             NULL
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: FADT: Failed to get 64-bit PM Block information." \
      " Status = %r\n",
      Status
      ));
  } else {
    CopyMem (
      &Fadt->XPm1aEvtBlk,
      &XpmBlockInfo->XPm1aEvtBlk,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
    CopyMem (
      &Fadt->XPm1bEvtBlk,
      &XpmBlockInfo->XPm1bEvtBlk,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
    CopyMem (
      &Fadt->XPm1aCntBlk,
      &XpmBlockInfo->XPm1aCntBlk,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
    CopyMem (
      &Fadt->XPm1bCntBlk,
      &XpmBlockInfo->XPm1bCntBlk,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
    CopyMem (
      &Fadt->XPm2CntBlk,
      &XpmBlockInfo->XPm2CntBlk,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
    CopyMem (
      &Fadt->XPmTmrBlk,
      &XpmBlockInfo->XPmTmrBlk,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
  }

  // Get the various platform information from the Platform Configuration manager
  Status = GetEX64ObjFadtMiscInfo (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &FadtMiscInfo,
             NULL
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: FADT: Failed to get various platform information." \
      " Status = %r\n",
      Status
      ));
  } else {
    Fadt->PLvl2Lat    = FadtMiscInfo->PLvl2Lat;
    Fadt->PLvl3Lat    = FadtMiscInfo->PLvl3Lat;
    Fadt->FlushSize   = FadtMiscInfo->FlushSize;
    Fadt->FlushStride = FadtMiscInfo->FlushStride;
    Fadt->DutyOffset  = FadtMiscInfo->DutyOffset;
    Fadt->DutyWidth   = FadtMiscInfo->DutyWidth;
    Fadt->DayAlrm     = FadtMiscInfo->DayAlrm;
    Fadt->MonAlrm     = FadtMiscInfo->MonAlrm;
    Fadt->Century     = FadtMiscInfo->Century;
  }

  // Get the 64-bit GPE Block information from the Platform Configuration Manager
  Status = GetEX64ObjFadtXgpeBlockInfo (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &XgpeBlockInfo,
             NULL
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: FADT: Failed to get 64-bit GPE Block information." \
      " Status = %r\n",
      Status
      ));
  } else {
    CopyMem (
      &Fadt->XGpe0Blk,
      &XgpeBlockInfo->XGpe0Blk,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
    CopyMem (
      &Fadt->XGpe1Blk,
      &XgpeBlockInfo->XGpe1Blk,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
  }

  // Get the sleep Block information from the Platform Configuration Manager
  Status = GetEX64ObjFadtSleepBlockInfo (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &SleepBlockInfo,
             NULL
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: FADT: Failed to get Sleep Block information." \
      " Status = %r\n",
      Status
      ));
  } else {
    CopyMem (
      &Fadt->SleepControlReg,
      &SleepBlockInfo->SleepControlReg,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
    CopyMem (
      &Fadt->SleepStatusReg,
      &SleepBlockInfo->SleepStatusReg,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
  }

  // Get the sleep Block information from the Platform Configuration Manager
  Status = GetEX64ObjFadtResetBlockInfo (
             CfgMgrProtocol,
             CM_NULL_TOKEN,
             &ResetBlockInfo,
             NULL
             );
  if (EFI_ERROR (Status)) {
    DEBUG ((
      DEBUG_ERROR,
      "ERROR: FADT: Failed to get Reset Block information." \
      " Status = %r\n",
      Status
      ));
  } else {
    CopyMem (
      &Fadt->ResetReg,
      &ResetBlockInfo->ResetReg,
      sizeof (EFI_ACPI_6_5_GENERIC_ADDRESS_STRUCTURE)
      );
    Fadt->ResetValue = ResetBlockInfo->ResetValue;
  }

  return Status;
}
