/** @file
  XSDT table parser

  Copyright (c) 2016 - 2018, ARM Limited. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

  @par Reference(s):
    - ACPI 6.2 Specification - Errata A, September 2017
**/

#include <IndustryStandard/Acpi.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include "AcpiParser.h"
#include "AcpiTableParser.h"

// Local variables
STATIC ACPI_DESCRIPTION_HEADER_INFO AcpiHdrInfo;

/** An ACPI_PARSER array describing the ACPI XSDT table.
*/
STATIC CONST ACPI_PARSER XsdtParser[] = {
  PARSE_ACPI_HEADER (&AcpiHdrInfo)
};

/**
  Get the ACPI XSDT header info.
**/
CONST ACPI_DESCRIPTION_HEADER_INFO *
EFIAPI
GetAcpiXsdtHeaderInfo (
  VOID
)
{
  return &AcpiHdrInfo;
}

/**
  This function parses the ACPI XSDT table and optionally traces the ACPI table fields.

  This function also performs validation of the XSDT table.

  @param [in] Trace              If TRUE, trace the ACPI fields.
  @param [in] Ptr                Pointer to the start of the buffer.
  @param [in] AcpiTableLength    Length of the ACPI table.
  @param [in] AcpiTableRevision  Revision of the ACPI table.
**/
VOID
EFIAPI
ParseAcpiXsdt (
  IN BOOLEAN Trace,
  IN UINT8*  Ptr,
  IN UINT32  AcpiTableLength,
  IN UINT8   AcpiTableRevision
  )
{
  UINT32        Offset;
  UINT32        TableOffset;
  UINT64*       TablePointer;
  UINTN         EntryIndex;
  CHAR16        Buffer[32];

  // Parse the ACPI header to get the length
  ParseAcpi (
    FALSE,
    0,
    "XSDT",
    Ptr,
    ACPI_DESCRIPTION_HEADER_LENGTH,
    PARSER_PARAMS (XsdtParser)
    );

  Offset = ParseAcpi (
             Trace,
             0,
             "XSDT",
             Ptr,
             *AcpiHdrInfo.Length,
             PARSER_PARAMS (XsdtParser)
             );

  TableOffset = Offset;

  if (Trace) {
    EntryIndex = 0;
    TablePointer = (UINT64*)(Ptr + TableOffset);
    while (Offset < (*AcpiHdrInfo.Length)) {
      CONST UINT32* Signature;
      CONST UINT32* Length;
      CONST UINT8*  Revision;

      if ((UINT64*)(UINTN)(*TablePointer) != NULL) {
        UINT8*      SignaturePtr;

        ParseAcpiHeader (
          (UINT8*)(UINTN)(*TablePointer),
          &Signature,
          &Length,
          &Revision
          );

        SignaturePtr = (UINT8*)Signature;

        UnicodeSPrint (
          Buffer,
          sizeof (Buffer),
          L"Entry[%d] - %c%c%c%c",
          EntryIndex++,
          SignaturePtr[0],
          SignaturePtr[1],
          SignaturePtr[2],
          SignaturePtr[3]
          );
      } else {
        UnicodeSPrint (
          Buffer,
          sizeof (Buffer),
          L"Entry[%d]",
          EntryIndex++
          );
      }

      PrintFieldName (2, Buffer);
      Print (L"0x%lx\n", *TablePointer);

      // Validate the table pointers are not NULL
      if ((UINT64*)(UINTN)(*TablePointer) == NULL) {
        IncrementErrorCount ();
        Print (
          L"ERROR: Invalid table entry at 0x%lx, table address is 0x%lx\n",
          TablePointer,
          *TablePointer
          );
      }
      Offset += sizeof (UINT64);
      TablePointer++;
    } // while
  }

  // Process the tables
  Offset = TableOffset;
  TablePointer = (UINT64*)(Ptr + TableOffset);
  while (Offset < (*AcpiHdrInfo.Length)) {
    if ((UINT64*)(UINTN)(*TablePointer) != NULL) {
      ProcessAcpiTable ((UINT8*)(UINTN)(*TablePointer));
    }
    Offset += sizeof (UINT64);
    TablePointer++;
  } // while
}
