/** @file
  Base ResetSystem library implementation.

  Copyright (c) 2024 Loongson Technology Corporation Limited. All rights reserved.<BR>

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

**/

#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/QemuFwCfgLib.h>
#include "ResetSystemAcpiGed.h"

/**
  Get configuration item data by the firmware configuration file name.

  @param[in]  Name - Name of file to look up.

  @return    VOID*       The Pointer of Value of Firmware Configuration item read.
**/
STATIC
VOID *
GetFwCfgData (
  CONST CHAR8  *Name
  )
{
  FIRMWARE_CONFIG_ITEM  FwCfgItem;
  EFI_STATUS            Status;
  UINTN                 FwCfgSize;
  VOID                  *Data;

  Status = QemuFwCfgFindFile (Name, &FwCfgItem, &FwCfgSize);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "%a %d read  %s error Status %d \n", __func__, __LINE__, Name, Status));
    return NULL;
  }

  Data = AllocatePool (FwCfgSize);
  if (Data == NULL) {
    return NULL;
  }

  QemuFwCfgSelectItem (FwCfgItem);
  QemuFwCfgReadBytes (FwCfgSize, Data);

  return Data;
}

/**
  Find the power manager related info from ACPI table

  @retval EFI_SUCCESS     Successfully find out all the required information.
  @retval EFI_NOT_FOUND   Failed to find the required info.
**/
STATIC
EFI_STATUS
GetPowerManagerByParseAcpiInfo (
  VOID
  )
{
  EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE     *Fadt       = NULL;
  EFI_ACPI_3_0_ROOT_SYSTEM_DESCRIPTION_POINTER  *Rsdp       = NULL;
  EFI_ACPI_DESCRIPTION_HEADER                   *Xsdt       = NULL;
  EFI_ACPI_DESCRIPTION_HEADER                   *Rsdt       = NULL;
  VOID                                          *AcpiTables = NULL;
  UINT32                                        *Entry32    = NULL;
  UINTN                                         Entry32Num;
  UINT32                                        *Signature = NULL;
  UINTN                                         Idx;

  Rsdp = GetFwCfgData ("etc/acpi/rsdp");
  if (Rsdp == NULL) {
    DEBUG ((DEBUG_ERROR, "%a %d read etc/acpi/rsdp error \n", __func__, __LINE__));
    return EFI_NOT_FOUND;
  }

  AcpiTables = GetFwCfgData ("etc/acpi/tables");
  if (AcpiTables == NULL) {
    DEBUG ((DEBUG_ERROR, "%a %d read etc/acpi/tables error \n", __func__, __LINE__));
    FreePool (Rsdp);
    return EFI_NOT_FOUND;
  }

  Rsdt       = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)AcpiTables +  Rsdp->RsdtAddress);
  Entry32    = (UINT32 *)(Rsdt + 1);
  Entry32Num = (Rsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
  for (Idx = 0; Idx < Entry32Num; Idx++) {
    Signature = (UINT32 *)((UINTN)Entry32[Idx] + (UINTN)AcpiTables);
    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
      DEBUG ((DEBUG_INFO, "Found Fadt in Rsdt\n"));
      goto Done;
    }
  }

  Xsdt       = (EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)AcpiTables +  Rsdp->XsdtAddress);
  Entry32    = (UINT32 *)(Xsdt + 1);
  Entry32Num = (Xsdt->Length - sizeof (EFI_ACPI_DESCRIPTION_HEADER)) >> 2;
  for (Idx = 0; Idx < Entry32Num; Idx++) {
    Signature = (UINT32 *)((UINTN)Entry32[Idx] + (UINTN)AcpiTables);
    if (*Signature == EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE_SIGNATURE) {
      Fadt = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)Signature;
      DEBUG ((DEBUG_INFO, "Found Fadt in Xsdt\n"));
      goto Done;
    }
  }

  FreePool (Rsdp);
  FreePool (AcpiTables);
  DEBUG ((DEBUG_ERROR, " Fadt Not Found\n"));
  return EFI_NOT_FOUND;

Done:
  mPowerManager.ResetRegAddr        = Fadt->ResetReg.Address;
  mPowerManager.ResetValue          = Fadt->ResetValue;
  mPowerManager.SleepControlRegAddr = Fadt->SleepControlReg.Address;
  mPowerManager.SleepStatusRegAddr  = Fadt->SleepStatusReg.Address;

  FreePool (Rsdp);
  FreePool (AcpiTables);

  return EFI_SUCCESS;
}

/**
  The constructor function to initialize mPowerManager.

  @retval EFI_SUCCESS     Initialize mPowerManager success.
  @retval EFI_NOT_FOUND   Failed to initialize mPowerManager.
**/
EFI_STATUS
EFI_API
ResetSystemLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  EFI_STATUS  Status;

  Status = GetPowerManagerByParseAcpiInfo ();
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_INFO, "%a:%d\n", __func__, __LINE__));
  }

  ASSERT (mPowerManager.SleepControlRegAddr);
  ASSERT (mPowerManager.SleepStatusRegAddr);
  ASSERT (mPowerManager.ResetRegAddr);

  return Status;
}
