/** @file

  Sample ACPI Platform Driver

  Copyright (c) 2008 - 2011, Intel Corporation. All rights reserved.<BR>
  Copyright (c) 2014 - 2016, AMD Inc. All rights reserved.<BR>

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

**/
/**
  Derived from:
   MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatform.c
**/

#include <Guid/ArmMpCoreInfo.h>

#include <Protocol/AcpiTable.h>
#include <Protocol/AmdMpCoreInfo.h>

#include <Library/ArmLib.h>
#include <Library/BaseLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/DebugLib.h>
#include <Library/DxeServicesLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/PcdLib.h>
#include <Library/UefiBootServicesTableLib.h>

#include <IndustryStandard/Acpi61.h>
#include <IndustryStandard/IoRemappingTable.h>

#include <SocVersion.h>

#include "AcpiPlatform.h"

STATIC EFI_ACPI_TABLE_PROTOCOL   *mAcpiTableProtocol;
STATIC AMD_MP_CORE_INFO_PROTOCOL *mAmdMpCoreInfoProtocol;


STATIC CONST UINT8 mDefaultMacPackageA[] = {
  0x12, 0xe, 0x6, 0xa, 0x2, 0xa, 0xa1, 0xa, 0xa2, 0xa, 0xa3, 0xa, 0xa4, 0xa, 0xa5
};

STATIC CONST UINT8 mDefaultMacPackageB[] = {
  0x12, 0xe, 0x6, 0xa, 0x2, 0xa, 0xb1, 0xa, 0xb2, 0xa, 0xb3, 0xa, 0xb4, 0xa, 0xb5
};

#define PACKAGE_MAC_OFFSET  4
#define PACKAGE_MAC_INCR    2

STATIC
VOID
SetPackageAddress (
  UINT8         *Package,
  UINT8         *MacAddress,
  UINTN         Size
  )
{
  UINTN   Index;

  for (Index = PACKAGE_MAC_OFFSET; Index < Size; Index += PACKAGE_MAC_INCR) {
    Package[Index] = *MacAddress++;
  }
}

STATIC
VOID
PatchAmlPackage (
  CONST UINT8   *Pattern,
  CONST UINT8   *Replacement,
  UINTN         PatternLength,
  UINT8         *SsdtTable,
  UINTN         TableSize
  )
{
  UINTN         Offset;

  for (Offset = 0; Offset < (TableSize - PatternLength); Offset++) {
    if (CompareMem (SsdtTable + Offset, Pattern, PatternLength) == 0) {
      CopyMem (SsdtTable + Offset, Replacement, PatternLength);
      break;
    }
  }
}

STATIC
VOID
EnableAvailableCores (
  EFI_ACPI_5_1_GIC_STRUCTURE            *GicC
  )
{
  ARM_CORE_INFO                         *ArmCoreInfoTable;
  UINTN                                 CoreCount;
  UINTN                                 Index;

  CoreCount = 0;
  ArmCoreInfoTable = mAmdMpCoreInfoProtocol->GetArmCoreInfoTable (&CoreCount);
  ASSERT (ArmCoreInfoTable != NULL);

  while (CoreCount--) {
    for (Index = 0; Index < MAX_CORES; Index++) {
      if (GicC[Index].MPIDR == ArmCoreInfoTable->Mpidr) {
        GicC[Index].Flags |= EFI_ACPI_5_1_GIC_ENABLED;
        break;
      }
    }
    ArmCoreInfoTable++;
  }
}

STATIC
VOID
InstallSystemDescriptionTables (
  VOID
  )
{
  EFI_ACPI_DESCRIPTION_HEADER                         *Table;
  EFI_STATUS                                          Status;
  UINT32                                              CpuId;
  UINTN                                               Index;
  UINTN                                               TableSize;
  UINTN                                               TableHandle;
  EFI_ACPI_5_1_GENERIC_TIMER_DESCRIPTION_TABLE        *Gtdt;
  EFI_ACPI_5_1_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *Madt;
  EFI_ACPI_5_1_GIC_STRUCTURE                          *GicC;
  UINT8                         MacPackage[sizeof(mDefaultMacPackageA)];

  CpuId = PcdGet32 (PcdSocCpuId);

  Status = EFI_SUCCESS;
  for (Index = 0; !EFI_ERROR (Status); Index++) {
    Status = GetSectionFromFv (&gEfiCallerIdGuid, EFI_SECTION_RAW, Index,
               (VOID **) &Table, &TableSize);
    if (EFI_ERROR (Status)) {
      break;
    }

    switch (Table->OemTableId) {
    case SIGNATURE_64 ('S', 't', 'y', 'x', 'B', '1', ' ', ' '):
      if ((CpuId & STYX_SOC_VERSION_MASK) < STYX_SOC_VERSION_B1) {
        continue;
      }
      break;

    case SIGNATURE_64 ('S', 't', 'y', 'x', 'X', 'g', 'b', 'e'):
      if (!FixedPcdGetBool (PcdXgbeEnable)) {
        continue;
      }

      //
      // Patch the SSDT binary with the correct MAC addresses
      //
      CopyMem (MacPackage, mDefaultMacPackageA, sizeof (MacPackage));

      SetPackageAddress (MacPackage, PcdGetPtr (PcdEthMacA), sizeof (MacPackage));
      PatchAmlPackage (mDefaultMacPackageA, MacPackage, sizeof (MacPackage),
        (UINT8 *)Table, TableSize);

      SetPackageAddress (MacPackage, PcdGetPtr (PcdEthMacB), sizeof (MacPackage));
      PatchAmlPackage (mDefaultMacPackageB, MacPackage, sizeof (MacPackage),
        (UINT8 *)Table, TableSize);

      break;

    case SIGNATURE_64 ('S', 't', 'y', 'x', 'K', 'c', 's', ' '):
      if (!FixedPcdGetBool (PcdEnableKcs) ||
          (CpuId & STYX_SOC_VERSION_MASK) < STYX_SOC_VERSION_B1) {
        continue;
      }

    default:
      switch (Table->Signature) {
      case EFI_ACPI_6_0_IO_REMAPPING_TABLE_SIGNATURE:
        if (!PcdGetBool (PcdEnableSmmus)) {
          continue;
        }
        break;

      case EFI_ACPI_5_1_GENERIC_TIMER_DESCRIPTION_TABLE_SIGNATURE:
        if ((CpuId & STYX_SOC_VERSION_MASK) < STYX_SOC_VERSION_B1) {
          Gtdt = (EFI_ACPI_5_1_GENERIC_TIMER_DESCRIPTION_TABLE *)Table;
          Gtdt->Header.Length = sizeof (*Gtdt);
          Gtdt->PlatformTimerCount = 0;
          Gtdt->PlatformTimerOffset = 0;
        }
        break;

      case EFI_ACPI_5_1_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE:
        Madt = (EFI_ACPI_5_1_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *)Table;
        GicC = (EFI_ACPI_5_1_GIC_STRUCTURE *)(Madt + 1);

        EnableAvailableCores (GicC);
        break;
      }
    }

    Status = mAcpiTableProtocol->InstallAcpiTable (mAcpiTableProtocol, Table,
                                   Table->Length, &TableHandle);

    DEBUG ((DEBUG_WARN,
      "Installing %c%c%c%c Table (Revision %d, Length %d) ... %r\n",
      ((UINT8 *)&Table->Signature)[0], ((UINT8 *)&Table->Signature)[1],
      ((UINT8 *)&Table->Signature)[2], ((UINT8 *)&Table->Signature)[3],
      Table->Revision, Table->Length, Status));

    FreePool (Table);
  }
}

/**
  Entrypoint of Acpi Platform driver.

  @param  ImageHandle
  @param  SystemTable

  @return EFI_SUCCESS
  @return EFI_LOAD_ERROR
  @return EFI_OUT_OF_RESOURCES

**/
EFI_STATUS
EFIAPI
AcpiPlatformEntryPoint (
  IN EFI_HANDLE         ImageHandle,
  IN EFI_SYSTEM_TABLE   *SystemTable
  )
{
  EFI_STATUS                  Status;

  //
  // Find the AcpiTable protocol
  //
  Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL,
                  (VOID**)&mAcpiTableProtocol);
  ASSERT_EFI_ERROR (Status);

  Status = gBS->LocateProtocol (&gAmdMpCoreInfoProtocolGuid, NULL,
                  (VOID**)&mAmdMpCoreInfoProtocol);
  ASSERT_EFI_ERROR (Status);

  InstallSystemDescriptionTables ();

  return EFI_REQUEST_UNLOAD_IMAGE;
}
