/** @file

Copyright (c) 2017, Intel Corporation. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Uefi.h>
#include <PiDxe.h>
#include <Library/TestPointCheckLib.h>
#include <Library/TestPointLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include <Library/BaseMemoryLib.h>
#include <IndustryStandard/Acpi.h>

VOID
DumpCharArray (
  IN CHAR8  *Ch,
  IN UINTN  Size
  );

VOID
DumpAcpiTableHeader (
  IN EFI_ACPI_DESCRIPTION_HEADER  *Table
  );

BOOLEAN
IsMmioExit (
  IN EFI_PHYSICAL_ADDRESS  BaseAddress,
  IN UINT64                Length,
  IN BOOLEAN               CheckAllocated
  );

typedef struct {
  UINT8                 Type;
  UINT8                 Length;
} APIC_STRUCT_HEADER;

CHAR8 *mMadtTypeString[] = {
  "APIC      ",
  "IO_APIC   ",
  "INT_SRC_OR",
  "NNI_SRC   ",
  "APIC_NMI  ",
  "APIC_OR   ",
  "IO_SAPIC  ",
  "SAPIC     ",
  "PL_INT_SRC",
  "X2APIC    ",
  "X2APIC_NMI",
};

STATIC CHAR8 mUnknownStr[11];

CHAR8 *
ShortNameOfMadtType(
  IN UINT8 Type
  )
{
  if (Type < sizeof(mMadtTypeString) / sizeof(mMadtTypeString[0])) {
    return mMadtTypeString[Type];
  } else {
    AsciiSPrint(mUnknownStr, sizeof(mUnknownStr), "[%02x]      ", Type);
    return mUnknownStr;
  }
}

VOID
DumpAcpiMadt (
  IN EFI_ACPI_4_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER  *Madt
  )
{
  APIC_STRUCT_HEADER                                    *ApicStructHeader;
  INTN                                                  MadtLen;
  EFI_ACPI_4_0_PROCESSOR_LOCAL_APIC_STRUCTURE           *ProcessorLocalApic;
  EFI_ACPI_4_0_IO_APIC_STRUCTURE                        *IOApic;
  EFI_ACPI_4_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE      *InterruptSourceOverride;
  EFI_ACPI_4_0_NON_MASKABLE_INTERRUPT_SOURCE_STRUCTURE  *NonMaskableInterruptSource;
  EFI_ACPI_4_0_LOCAL_APIC_NMI_STRUCTURE                 *LocalApicNMI;
  EFI_ACPI_4_0_LOCAL_APIC_ADDRESS_OVERRIDE_STRUCTURE    *LocalApicAddressOverride;
  EFI_ACPI_4_0_IO_SAPIC_STRUCTURE                       *IOSapic;
  EFI_ACPI_4_0_PROCESSOR_LOCAL_SAPIC_STRUCTURE          *ProcessorLocalSapic;
  EFI_ACPI_4_0_PLATFORM_INTERRUPT_SOURCES_STRUCTURE     *PlatformInterruptSource;
  EFI_ACPI_4_0_PROCESSOR_LOCAL_X2APIC_STRUCTURE         *ProcessorLocalX2Apic;
  EFI_ACPI_4_0_LOCAL_X2APIC_NMI_STRUCTURE               *LocalX2ApicNmi;

  DumpAcpiTableHeader (&Madt->Header);
  DEBUG ((DEBUG_INFO, "         "));
  DEBUG ((DEBUG_INFO, " LocalApicAddress=0x%08x\n", Madt->LocalApicAddress));

  //
  // Sub table
  //
  MadtLen  = Madt->Header.Length - sizeof(EFI_ACPI_4_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER);
  ApicStructHeader = (APIC_STRUCT_HEADER *)(Madt + 1);
  while (MadtLen > 0) {
    switch (ApicStructHeader->Type) {
    case EFI_ACPI_4_0_PROCESSOR_LOCAL_APIC:
      ProcessorLocalApic = (EFI_ACPI_4_0_PROCESSOR_LOCAL_APIC_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " ID=0x%02x", ProcessorLocalApic->AcpiProcessorId));
      DEBUG ((DEBUG_INFO, " ApicId=0x%02x", ProcessorLocalApic->ApicId));
      if ((ProcessorLocalApic->Flags & EFI_ACPI_4_0_LOCAL_APIC_ENABLED) != 0) {
        DEBUG ((DEBUG_INFO, " (Enabled)"));
      }
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_IO_APIC:
      IOApic = (EFI_ACPI_4_0_IO_APIC_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " IoApicId=0x%02x", IOApic->IoApicId));
      DEBUG ((DEBUG_INFO, " Address=0x%08x", IOApic->IoApicAddress));
      DEBUG ((DEBUG_INFO, " InterruptBase=0x%08x", IOApic->GlobalSystemInterruptBase));
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_INTERRUPT_SOURCE_OVERRIDE:
      InterruptSourceOverride = (EFI_ACPI_4_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " Bus=0x%02x", InterruptSourceOverride->Bus));
      DEBUG ((DEBUG_INFO, " Source=0x%02x", InterruptSourceOverride->Source));
      DEBUG ((DEBUG_INFO, " Interrupt=0x%08x", InterruptSourceOverride->GlobalSystemInterrupt));
      DEBUG ((DEBUG_INFO, " Flags=0x%04x", InterruptSourceOverride->Flags));
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_NON_MASKABLE_INTERRUPT_SOURCE:
      NonMaskableInterruptSource = (EFI_ACPI_4_0_NON_MASKABLE_INTERRUPT_SOURCE_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " Interrupt=0x%08x", NonMaskableInterruptSource->GlobalSystemInterrupt));
      DEBUG ((DEBUG_INFO, " Flags=0x%04x", NonMaskableInterruptSource->Flags));
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_LOCAL_APIC_NMI:
      LocalApicNMI = (EFI_ACPI_4_0_LOCAL_APIC_NMI_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " ID=0x%02x", LocalApicNMI->AcpiProcessorId));
      DEBUG ((DEBUG_INFO, " Lint=0x%02x", LocalApicNMI->LocalApicLint));
      DEBUG ((DEBUG_INFO, " Flags=0x%04x", LocalApicNMI->Flags));
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_LOCAL_APIC_ADDRESS_OVERRIDE:
      LocalApicAddressOverride = (EFI_ACPI_4_0_LOCAL_APIC_ADDRESS_OVERRIDE_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " LocalApicAddress=0x%016lx", LocalApicAddressOverride->LocalApicAddress));
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_IO_SAPIC:
      IOSapic = (EFI_ACPI_4_0_IO_SAPIC_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " IoApicId=0x%02x", IOSapic->IoApicId));
      DEBUG ((DEBUG_INFO, " InterruptBase=0x%08x", IOSapic->GlobalSystemInterruptBase));
      DEBUG ((DEBUG_INFO, " IoSapicAddress=0x%016lx", IOSapic->IoSapicAddress));
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_LOCAL_SAPIC:
      ProcessorLocalSapic = (EFI_ACPI_4_0_PROCESSOR_LOCAL_SAPIC_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " ID=0x%02x", ProcessorLocalSapic->AcpiProcessorId));
      DEBUG ((DEBUG_INFO, " LocalSapicId=0x%02x", ProcessorLocalSapic->LocalSapicId));
      DEBUG ((DEBUG_INFO, " LocalSapicEid=0x%02x", ProcessorLocalSapic->LocalSapicEid));
      DEBUG ((DEBUG_INFO, " UID=0x%08x", ProcessorLocalSapic->ACPIProcessorUIDValue));
      if ((ProcessorLocalSapic->Flags & EFI_ACPI_5_0_LOCAL_APIC_ENABLED) != 0) {
        DEBUG ((DEBUG_INFO, " (Enabled)"));
      }
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_PLATFORM_INTERRUPT_SOURCES:
      PlatformInterruptSource = (EFI_ACPI_4_0_PLATFORM_INTERRUPT_SOURCES_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " Type=0x%02x", PlatformInterruptSource->InterruptType));
      DEBUG ((DEBUG_INFO, " ID=0x%02x", PlatformInterruptSource->ProcessorId));
      DEBUG ((DEBUG_INFO, " EID=0x%02x", PlatformInterruptSource->ProcessorEid));
      DEBUG ((DEBUG_INFO, " IoSapicVector=0x%02x", PlatformInterruptSource->IoSapicVector));
      DEBUG ((DEBUG_INFO, " Interrupt=0x%08x", PlatformInterruptSource->GlobalSystemInterrupt));
      DEBUG ((DEBUG_INFO, " SourceFlags=0x%08x", PlatformInterruptSource->PlatformInterruptSourceFlags));
      DEBUG ((DEBUG_INFO, " Flags=0x%04x", PlatformInterruptSource->Flags));
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_PROCESSOR_LOCAL_X2APIC:
      ProcessorLocalX2Apic = (EFI_ACPI_4_0_PROCESSOR_LOCAL_X2APIC_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " X2ApicId=0x%08x", ProcessorLocalX2Apic->X2ApicId));
      DEBUG ((DEBUG_INFO, " UID=0x%08x", ProcessorLocalX2Apic->AcpiProcessorUid));
      if ((ProcessorLocalX2Apic->Flags & EFI_ACPI_5_0_LOCAL_APIC_ENABLED) != 0) {
        DEBUG ((DEBUG_INFO, " (Enabled)"));
      }
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    case EFI_ACPI_4_0_LOCAL_X2APIC_NMI:
      LocalX2ApicNmi = (EFI_ACPI_4_0_LOCAL_X2APIC_NMI_STRUCTURE *)ApicStructHeader;
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, ": [0x%02x]", ApicStructHeader->Type));
      DEBUG ((DEBUG_INFO, " UID=0x%08x", LocalX2ApicNmi->AcpiProcessorUid));
      DEBUG ((DEBUG_INFO, " Lint=0x%02x", LocalX2ApicNmi->LocalX2ApicLint));
      DEBUG ((DEBUG_INFO, " Flags=0x%04x", LocalX2ApicNmi->Flags));
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    default:
      DEBUG ((DEBUG_INFO, "         "));
      DEBUG ((DEBUG_INFO, " "));
      DEBUG ((DEBUG_INFO, ShortNameOfMadtType(ApicStructHeader->Type)));
      DEBUG ((DEBUG_INFO, "\n"));
      break;
    }
    ApicStructHeader = (APIC_STRUCT_HEADER *)((UINT8 *)ApicStructHeader + ApicStructHeader->Length);
    MadtLen         -= ApicStructHeader->Length;
  }
}

EFI_STATUS
CheckAcpiMadt (
  IN EFI_ACPI_4_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER  *Madt
  )
{

  APIC_STRUCT_HEADER                                    *ApicStructHeader;
  INTN                                                  MadtLen;
  EFI_ACPI_4_0_IO_APIC_STRUCTURE                        *IOApic;
  EFI_ACPI_4_0_LOCAL_APIC_ADDRESS_OVERRIDE_STRUCTURE    *LocalApicAddressOverride;
  EFI_ACPI_4_0_IO_SAPIC_STRUCTURE                       *IOSapic;
#if 0
  EFI_ACPI_4_0_PROCESSOR_LOCAL_APIC_STRUCTURE           *ProcessorLocalApic;
  EFI_ACPI_4_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE      *InterruptSourceOverride;
  EFI_ACPI_4_0_NON_MASKABLE_INTERRUPT_SOURCE_STRUCTURE  *NonMaskableInterruptSource;
  EFI_ACPI_4_0_LOCAL_APIC_NMI_STRUCTURE                 *LocalApicNMI;
  EFI_ACPI_4_0_PROCESSOR_LOCAL_SAPIC_STRUCTURE          *ProcessorLocalSapic;
  EFI_ACPI_4_0_PLATFORM_INTERRUPT_SOURCES_STRUCTURE     *PlatformInterruptSource;
  EFI_ACPI_4_0_PROCESSOR_LOCAL_X2APIC_STRUCTURE         *ProcessorLocalX2Apic;
  EFI_ACPI_4_0_LOCAL_X2APIC_NMI_STRUCTURE               *LocalX2ApicNmi;
#endif

  if (!IsMmioExit (Madt->LocalApicAddress, SIZE_4KB, TRUE)) {
    DEBUG ((DEBUG_ERROR, "MADT resource (0x%x) is not reported correctly.\n", Madt->LocalApicAddress));
    return EFI_NOT_STARTED;
  }

  //
  // Sub table
  //
  MadtLen  = Madt->Header.Length - sizeof(EFI_ACPI_4_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER);
  ApicStructHeader = (APIC_STRUCT_HEADER *)(Madt + 1);
  while (MadtLen > 0) {
    switch (ApicStructHeader->Type) {
    case EFI_ACPI_4_0_IO_APIC:
      IOApic = (EFI_ACPI_4_0_IO_APIC_STRUCTURE *)ApicStructHeader;
      if (!IsMmioExit (IOApic->IoApicAddress, SIZE_4KB, TRUE)) {
        DEBUG ((DEBUG_ERROR, "MADT.IOAPIC resource (0x%x) is not reported correctly.\n", IOApic->IoApicAddress));
        return EFI_NOT_STARTED;
      }
      break;
    case EFI_ACPI_4_0_LOCAL_APIC_ADDRESS_OVERRIDE:
      LocalApicAddressOverride = (EFI_ACPI_4_0_LOCAL_APIC_ADDRESS_OVERRIDE_STRUCTURE *)ApicStructHeader;
      if (!IsMmioExit (LocalApicAddressOverride->LocalApicAddress, SIZE_4KB, TRUE)) {
        DEBUG ((DEBUG_ERROR, "MADT.LocalApicOverride resource (0x%x) is not reported correctly.\n", LocalApicAddressOverride->LocalApicAddress));
        return EFI_NOT_STARTED;
      }
      break;
    case EFI_ACPI_4_0_IO_SAPIC:
      IOSapic = (EFI_ACPI_4_0_IO_SAPIC_STRUCTURE *)ApicStructHeader;
      if (!IsMmioExit (IOSapic->IoSapicAddress, SIZE_4KB, TRUE)) {
        DEBUG ((DEBUG_ERROR, "MADT.IOSAPIC resource (0x%x) is not reported correctly.\n", IOSapic->IoSapicAddress));
        return EFI_NOT_STARTED;
      }
      break;
#if 0
    case EFI_ACPI_4_0_PROCESSOR_LOCAL_APIC:
      ProcessorLocalApic = (EFI_ACPI_4_0_PROCESSOR_LOCAL_APIC_STRUCTURE *)ApicStructHeader;
      break;
    case EFI_ACPI_4_0_INTERRUPT_SOURCE_OVERRIDE:
      InterruptSourceOverride = (EFI_ACPI_4_0_INTERRUPT_SOURCE_OVERRIDE_STRUCTURE *)ApicStructHeader;
      break;
    case EFI_ACPI_4_0_NON_MASKABLE_INTERRUPT_SOURCE:
      NonMaskableInterruptSource = (EFI_ACPI_4_0_NON_MASKABLE_INTERRUPT_SOURCE_STRUCTURE *)ApicStructHeader;
      break;
    case EFI_ACPI_4_0_LOCAL_APIC_NMI:
      LocalApicNMI = (EFI_ACPI_4_0_LOCAL_APIC_NMI_STRUCTURE *)ApicStructHeader;
      break;
    case EFI_ACPI_4_0_LOCAL_SAPIC:
      ProcessorLocalSapic = (EFI_ACPI_4_0_PROCESSOR_LOCAL_SAPIC_STRUCTURE *)ApicStructHeader;
      break;
    case EFI_ACPI_4_0_PLATFORM_INTERRUPT_SOURCES:
      PlatformInterruptSource = (EFI_ACPI_4_0_PLATFORM_INTERRUPT_SOURCES_STRUCTURE *)ApicStructHeader;
      break;
    case EFI_ACPI_4_0_PROCESSOR_LOCAL_X2APIC:
      ProcessorLocalX2Apic = (EFI_ACPI_4_0_PROCESSOR_LOCAL_X2APIC_STRUCTURE *)ApicStructHeader;
      break;
    case EFI_ACPI_4_0_LOCAL_X2APIC_NMI:
      LocalX2ApicNmi = (EFI_ACPI_4_0_LOCAL_X2APIC_NMI_STRUCTURE *)ApicStructHeader;
      break;
#endif
    default:
      break;
    }
    ApicStructHeader = (APIC_STRUCT_HEADER *)((UINT8 *)ApicStructHeader + ApicStructHeader->Length);
    MadtLen         -= ApicStructHeader->Length;
  }
  return EFI_SUCCESS;
}