/** @file
Update the _PRT and _PRW method for pci devices

Copyright (c) 2013-2016 Intel Corporation.

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


**/
#include "AcpiPlatform.h"

PCI_DEVICE_INFO *mQNCPciInfo = NULL;

/**
  Init Pci Device Structure
  @param mConfigData    - Pointer of Pci Device information Structure

**/
VOID
InitPciDeviceInfoStructure (
  PCI_DEVICE_SETTING            *mConfigData
  )
{
  //
  // Return 0 given that function unsupported.
  // Would need to parse ACPI tables and build mQNCPciInfo above
  // with found _PRT & _PRW methods for PCI devices.
  //
  mConfigData->PciDeviceInfoNumber = 0;
}

/**
  return Integer value.

  @param Data    - AML data buffer
  @param Integer - integer value.

  @return Data size processed.
**/
UINTN
SdtGetInteger (
  IN  UINT8  *Data,
  OUT UINT64 *Integer
  )
{
  *Integer = 0;
  switch (*Data) {
  case AML_ZERO_OP:
    return 1;
  case AML_ONE_OP:
    *Integer = 1;
    return 1;
  case AML_ONES_OP:
    *Integer = (UINTN)-1;
    return 1;
  case AML_BYTE_PREFIX:
    CopyMem (Integer, Data + 1, sizeof(UINT8));
    return 1 + sizeof(UINT8);
  case AML_WORD_PREFIX:
    CopyMem (Integer, Data + 1, sizeof(UINT16));
    return 1 + sizeof(UINT16);
  case AML_DWORD_PREFIX:
    CopyMem (Integer, Data + 1, sizeof(UINT32));
    return 1 + sizeof(UINT32);
  case AML_QWORD_PREFIX:
    CopyMem (Integer, Data + 1, sizeof(UINT64));
    return 1 + sizeof(UINT64);
  default:
    // Something wrong
    ASSERT (FALSE);
    return 1;
  }
}


/**
  Check if this handle has expected opcode.

  @param AcpiSdt    Pointer to Acpi SDT protocol
  @param Handle     ACPI handle
  @param OpCode     Expected OpCode
  @param SubOpCode  Expected SubOpCode

  @retval TRUE  This handle has expected opcode
  @retval FALSE This handle does not have expected opcode
**/
BOOLEAN
SdtIsThisTypeObject (
  IN EFI_ACPI_SDT_PROTOCOL *AcpiSdt,
  IN EFI_ACPI_HANDLE Handle,
  IN UINT8           OpCode,
  IN UINT8           SubOpCode
  )
{
  EFI_STATUS         Status;
  EFI_ACPI_DATA_TYPE DataType;
  UINT8              *Data;
  UINTN              DataSize;

  Status = AcpiSdt->GetOption (Handle, 0, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE);

  if (OpCode == AML_EXT_OP) {
    if (Data[1] == SubOpCode) {
      return TRUE;
    }
  } else {
    if (Data[0] == OpCode) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
  Check if this handle has expected name and name value.

  @param AcpiSdt    Pointer to Acpi SDT protocol
  @param Handle     ACPI handle
  @param Name       Expected name
  @param Value      Expected name value

  @retval TRUE  This handle has expected name and name value.
  @retval FALSE This handle does not have expected name and name value.
**/
BOOLEAN
SdtIsNameIntegerValueEqual (
  IN EFI_ACPI_SDT_PROTOCOL *AcpiSdt,
  IN EFI_ACPI_HANDLE Handle,
  IN CHAR8           *Name,
  IN UINT64          Value
  )
{
  EFI_STATUS         Status;
  EFI_ACPI_DATA_TYPE DataType;
  UINT8              *Data;
  UINTN              DataSize;
  UINT64             Integer;

  Status = AcpiSdt->GetOption (Handle, 1, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_NAME_STRING);

  if (CompareMem (Data, Name, 4) != 0) {
    return FALSE;
  }

  //
  // Name match check object
  //
  Status = AcpiSdt->GetOption (Handle, 2, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);

  Integer = 0;
  SdtGetInteger (Data, &Integer);
  if (Integer != Value) {
    return FALSE;
  }

  // All match
  return TRUE;
}

/**
  Check if this handle's children has expected name and name value.

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param ParentHandle     ACPI parent handle
  @param Name             Expected name
  @param Value            Expected name value

  @retval TRUE  This handle's children has expected name and name value.
  @retval FALSE This handle's children does not have expected name and name value.
**/
BOOLEAN
SdtCheckNameIntegerValue (
  IN EFI_ACPI_SDT_PROTOCOL *AcpiSdt,
  IN EFI_ACPI_HANDLE ParentHandle,
  IN CHAR8           *Name,
  IN UINT64          Value
  )
{
  EFI_ACPI_HANDLE PreviousHandle;
  EFI_ACPI_HANDLE Handle;
  EFI_STATUS      Status;

  Handle = NULL;
  while (TRUE) {
    PreviousHandle = Handle;
    Status = AcpiSdt->GetChild (ParentHandle, &Handle);
    ASSERT_EFI_ERROR (Status);

    if (PreviousHandle != NULL) {
      Status = AcpiSdt->Close (PreviousHandle);
      ASSERT_EFI_ERROR (Status);
    }

    //
    // Done
    //
    if (Handle == NULL) {
      return FALSE;
    }

    //
    // Check this name
    //
    if (SdtIsThisTypeObject (AcpiSdt, Handle, AML_NAME_OP, 0)) {
      if (SdtIsNameIntegerValueEqual (AcpiSdt, Handle, Name, Value)) {
        return TRUE;
      }
    }
  }

  //
  // Should not run here
  //
}

/**
  Convert the pci address from VPD (bus,dev,fun) into the address that acpi table
  can recognize.

  @param PciAddress    Pci address from VPD

  @retval return the address that acpi table can recognize
**/
UINT32
SdtConvertToAcpiPciAdress (
  IN UINT32 PciAddress
  )
{
  UINT32 ReturnAddress;

  ReturnAddress = ((PciAddress & 0x0000FF00) << 8) | (PciAddress & 0x000000FF);

  if ((PciAddress & 0x000000FF) == 0x000000FF)
    ReturnAddress |= 0x0000FFFF;

  return ReturnAddress;
}

/**
  return AML NameString size.

  @param Buffer - AML name string

  @return AML name string size
**/
UINTN
SdtGetNameStringSize (
  IN UINT8              *Buffer
  )
{
  UINTN                 SegCount;
  UINTN                 Length;

  Length = 0;

  //
  // Parse root or prefix
  //
  if (*Buffer == AML_ROOT_CHAR) {
    //
    // RootChar
    //
    Buffer ++;
    Length ++;
  } else if (*Buffer == AML_PARENT_PREFIX_CHAR) {
    //
    // ParentPrefixChar
    //
    Buffer ++;
    Length ++;
    while (*Buffer == AML_PARENT_PREFIX_CHAR) {
      Buffer ++;
      Length ++;
    }
  }

  //
  // Parse name segment
  //
  if (*Buffer == AML_DUAL_NAME_PREFIX) {
    //
    // DualName
    //
    Buffer ++;
    Length ++;
    SegCount = 2;
  } else if (*Buffer == AML_MULTI_NAME_PREFIX) {
    //
    // MultiName
    //
    Buffer ++;
    Length ++;
    SegCount = *Buffer;
    Buffer ++;
    Length ++;
  } else if (*Buffer == 0) {
    //
    // NULL Name
    //
    SegCount = 0;
    Length ++;
  } else {
    //
    // NameSeg
    //
    SegCount = 1;
  }

  Buffer += 4 * SegCount;
  Length += 4 * SegCount;

  return Length;
}

/**
  The routine to check if this device is PCI root bridge.

  @param AcpiSdt       Pointer to Acpi SDT protocol
  @param DeviceHandle  ACPI device handle
  @param Context       Context info - not used here

  @retval TRUE  This is PCI root bridge
  @retval FALSE This is not PCI root bridge
**/
BOOLEAN
SdtFindRootBridgeHandle (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        CheckHandle,
  IN VOID                   *Context
  )
{
  BOOLEAN            Result;
  EFI_ACPI_DATA_TYPE DataType;
  UINT8              *Data;
  UINTN              DataSize;
  EFI_STATUS         Status;

  if (!SdtIsThisTypeObject (AcpiSdt, CheckHandle, AML_EXT_OP, AML_EXT_DEVICE_OP))
    return FALSE;

  Result = SdtCheckNameIntegerValue (AcpiSdt,CheckHandle, "_HID", (UINT64)0x080AD041); // PNP0A08
  if (!Result) {
    Result = SdtCheckNameIntegerValue (AcpiSdt, CheckHandle, "_CID", (UINT64)0x030AD041); // PNP0A03
    if (!Result) {
      return Result;
    }
  }

  //
  // Found
  //
  Status = AcpiSdt->GetOption (CheckHandle, 1, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_NAME_STRING);

  return Result;
}


/**
  The routine to check if this device is wanted.

  @param AcpiSdt       Pointer to Acpi SDT protocol
  @param DeviceHandle  ACPI device handle
  @param Context       Context info - not used here

  @retval TRUE  This is PCI device wanted
  @retval FALSE This is not PCI device wanted
**/
BOOLEAN
SdtFindPciDeviceHandle (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        CheckHandle,
  IN VOID                   *Context
  )
{
  BOOLEAN            Result;
  EFI_ACPI_DATA_TYPE DataType;
  UINT8              *Data;
  UINTN              DataSize;
  EFI_STATUS         Status;

  if (!SdtIsThisTypeObject (AcpiSdt, CheckHandle, AML_EXT_OP, AML_EXT_DEVICE_OP))
    return FALSE;

  Result = SdtCheckNameIntegerValue (AcpiSdt,CheckHandle, "_ADR", (UINT64)*(UINT32 *)Context);
  if (!Result) {
    return Result;
  }

  //
  // Found
  //
  Status = AcpiSdt->GetOption (CheckHandle, 1, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_NAME_STRING);

  return Result;
}

/**
  Go through the parent handle and find the handle which pass CheckHandleInfo.

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param ParentHandle     ACPI parent handle
  @param CheckHandleInfo  The callback routine to check if this handle meet the requirement
  @param Context          The context of CheckHandleInfo

  @return the handle which is first one can pass CheckHandleInfo.
**/
EFI_ACPI_HANDLE
SdtGetHandleByScanAllChilds (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        ParentHandle,
  IN CHECK_HANDLE_INFO      CheckHandleInfo,
  IN VOID                   *Context
  )
{
  EFI_ACPI_HANDLE    PreviousHandle;
  EFI_ACPI_HANDLE    Handle;
  EFI_STATUS         Status;
  EFI_ACPI_HANDLE    ReturnHandle;

  //
  // Use deep first algo to enumerate all ACPI object
  //
  Handle = NULL;
  while (TRUE) {
    PreviousHandle = Handle;
    Status = AcpiSdt->GetChild (ParentHandle, &Handle);
    ASSERT_EFI_ERROR (Status);

    if (PreviousHandle != NULL) {
      Status = AcpiSdt->Close (PreviousHandle);
      ASSERT_EFI_ERROR (Status);
    }

    //
    // Done
    //
    if (Handle == NULL) {
      return NULL;
    }

    //
    // Check this handle
    //
    if (CheckHandleInfo (AcpiSdt, Handle, Context)) {
      return Handle;
    }

    //
    // Enumerate
    //
    ReturnHandle = SdtGetHandleByScanAllChilds (AcpiSdt, Handle, CheckHandleInfo, Context);
    if (ReturnHandle != NULL) {
      return ReturnHandle;
    }
  }

  //
  // Should not run here
  //
}


/**
  Check whether the INTx package is matched

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param INTxPkgHandle    ACPI INTx package handle
  @param PciAddress       Acpi pci address
  @param INTx             Index of INTx pin
  @param IsAPIC           Tell whether the returned INTx package is for APIC or not

  @retval       TRUE      the INTx package is matched
  @retval       FALSE     the INTx package is not matched

**/
BOOLEAN
SdtCheckINTxPkgIsMatch (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        INTxPkgHandle,
  IN UINT32                 PciAddress,
  IN UINT8                  INTx,
  IN BOOLEAN                *IsAPIC
  )
{
  EFI_ACPI_HANDLE    PreviousHandle;
  EFI_STATUS         Status;
  EFI_ACPI_HANDLE    MemberHandle;
  EFI_ACPI_DATA_TYPE DataType;
  UINT8              *Data;
  UINTN              DataSize;
  UINT64             CurrentPciAddress;
  UINT64             CurrentINTx;
  UINTN              ChildSize;


  //
  // Check the pci address
  //
  MemberHandle = NULL;
  Status = AcpiSdt->GetChild (INTxPkgHandle, &MemberHandle);
  ASSERT_EFI_ERROR (Status);
  ASSERT (MemberHandle != NULL);

  Status = AcpiSdt->GetOption (MemberHandle, 0, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE);

  CurrentPciAddress = 0;
  SdtGetInteger (Data, &CurrentPciAddress);

  if (CurrentPciAddress != PciAddress) {

    Status = AcpiSdt->Close (MemberHandle);
    ASSERT_EFI_ERROR (Status);
    return FALSE;
  }

  //
  // Check the pci interrupt pin
  //
  PreviousHandle = MemberHandle;
  Status = AcpiSdt->GetChild (INTxPkgHandle, &MemberHandle);
  ASSERT_EFI_ERROR (Status);
  ASSERT (MemberHandle != NULL);

  if (PreviousHandle != NULL) {
    Status = AcpiSdt->Close (PreviousHandle);
    ASSERT_EFI_ERROR (Status);
  }

  Status = AcpiSdt->GetOption (MemberHandle, 0, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE);

  CurrentINTx = 0;
  ChildSize = SdtGetInteger (Data, &CurrentINTx);

  Status = AcpiSdt->Close (MemberHandle);
  ASSERT_EFI_ERROR (Status);

  if (CurrentINTx != INTx)
    return FALSE;

  Data += ChildSize;

  if (*Data == AML_BYTE_PREFIX)
    Data += 1;

  //
  // Check the pci interrupt source
  //
  if (*Data  != 0)
    *IsAPIC = FALSE;
  else
    *IsAPIC = TRUE;

  return TRUE;
}




/**
  Get the wanted INTx package inside the parent package

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param ParentPkgHandle  ACPI parent package handle
  @param PciAddress       Acpi pci address
  @param INTx             Index of INTx pin
  @param INTxPkgHandle    ACPI INTx package handle
  @param IsAPIC           Tell whether the returned INTx package is for APIC or not

**/
VOID
SdtGetINTxPkgHandle (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        ParentPkgHandle,
  IN UINT32                 PciAddress,
  IN UINT8                  INTx,
  IN EFI_ACPI_HANDLE        *INTxPkgHandle,
  IN BOOLEAN                *IsAPIC
  )
{
  EFI_ACPI_HANDLE    PreviousHandle;
  EFI_STATUS         Status;
  EFI_ACPI_HANDLE    ChildPkgHandle;

  ChildPkgHandle = NULL;
  while (TRUE) {
    PreviousHandle = ChildPkgHandle;
    Status = AcpiSdt->GetChild (ParentPkgHandle, &ChildPkgHandle);
    ASSERT_EFI_ERROR (Status);

    if (PreviousHandle != NULL) {
      Status = AcpiSdt->Close (PreviousHandle);
      ASSERT_EFI_ERROR (Status);
    }

    if (ChildPkgHandle == NULL) {
      break;
    }

    if (SdtCheckINTxPkgIsMatch(AcpiSdt, ChildPkgHandle, PciAddress, INTx, IsAPIC)) {
      *INTxPkgHandle = ChildPkgHandle;
      return;
    }
  }

  return;
}

/**
  Update the INTx package with the correct pirq value

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param INTxPkgHandle    ACPI INTx package handle
  @param PirqValue        Correct pirq value
  @param IsAPIC           Tell whether the INTx package is for APIC or not

**/
VOID
SdtUpdateINTxPkg (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        INTxPkgHandle,
  IN UINT8                  PirqValue,
  IN BOOLEAN                IsAPIC
  )
{
  EFI_ACPI_HANDLE    PreviousHandle;
  EFI_STATUS         Status;
  EFI_ACPI_HANDLE    MemberHandle;
  EFI_ACPI_DATA_TYPE DataType;
  UINT8              *Data;
  UINTN              DataSize;
  UINT64             TempValue;
  UINTN              ChildSize;


  //
  // Check the pci address
  //
  MemberHandle = NULL;
  Status = AcpiSdt->GetChild (INTxPkgHandle, &MemberHandle);
  ASSERT_EFI_ERROR (Status);
  ASSERT (MemberHandle != NULL);

  //
  // Check the pci interrupt pin
  //
  PreviousHandle = MemberHandle;
  Status = AcpiSdt->GetChild (INTxPkgHandle, &MemberHandle);
  ASSERT_EFI_ERROR (Status);
  ASSERT (MemberHandle != NULL);

  if (PreviousHandle != NULL) {
    Status = AcpiSdt->Close (PreviousHandle);
    ASSERT_EFI_ERROR (Status);
  }

  Status = AcpiSdt->GetOption (MemberHandle, 0, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE);

  ChildSize = SdtGetInteger (Data, &TempValue);

  Status = AcpiSdt->Close (MemberHandle);
  ASSERT_EFI_ERROR (Status);

  Data += ChildSize;

  //
  // update the pci interrupt source or source index
  //
  if (!IsAPIC) {
    ChildSize = SdtGetNameStringSize (Data);
    Data += (ChildSize - 1);

    PirqValue += 0x40;   // change to ascii char
    if (*Data != PirqValue)
      *Data = PirqValue;
  } else {

    ChildSize = SdtGetInteger (Data, &TempValue);
    Data += ChildSize;

    Data += 1;

    if (*Data != PirqValue)
      *Data = PirqValue;
  }
}

/**
  Check every child package inside this interested parent package for update PRT

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param ParentPkgHandle  ACPI parent package handle
  @param PciDeviceInfo    Pointer to PCI_DEVICE_INFO

**/
VOID
SdtCheckParentPackage (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        ParentPkgHandle,
  IN PCI_DEVICE_INFO        *PciDeviceInfo
  )
{
  EFI_ACPI_HANDLE    INTAPkgHandle;
  EFI_ACPI_HANDLE    INTBPkgHandle;
  EFI_ACPI_HANDLE    INTCPkgHandle;
  EFI_ACPI_HANDLE    INTDPkgHandle;
  UINT32             PciAddress = 0;
  BOOLEAN            IsAllFunctions = FALSE;
  UINT8              IsAPIC = 0;
  EFI_STATUS         Status;

  INTAPkgHandle = INTBPkgHandle = INTCPkgHandle = INTDPkgHandle = NULL;

  PciAddress   = SdtConvertToAcpiPciAdress(PciDeviceInfo->DeviceAddress);

  if ((PciAddress & 0xFFFF) == 0xFFFF) {
    IsAllFunctions = TRUE;
  } else {
    IsAllFunctions = FALSE;
    PciAddress = (PciAddress | 0xFFFF);
  }

  SdtGetINTxPkgHandle (AcpiSdt, ParentPkgHandle, PciAddress, 0, &INTAPkgHandle, (BOOLEAN *)&IsAPIC);
  SdtGetINTxPkgHandle (AcpiSdt, ParentPkgHandle, PciAddress, 1, &INTBPkgHandle, (BOOLEAN *)&IsAPIC);
  SdtGetINTxPkgHandle (AcpiSdt, ParentPkgHandle, PciAddress, 2, &INTCPkgHandle, (BOOLEAN *)&IsAPIC);
  SdtGetINTxPkgHandle (AcpiSdt, ParentPkgHandle, PciAddress, 3, &INTDPkgHandle, (BOOLEAN *)&IsAPIC);

  //
  // Check INTA
  //
  if ((PciDeviceInfo->INTA[IsAPIC] != 0xFF) && (INTAPkgHandle != NULL)) {
    //
    // Find INTA package and there is valid INTA update item, update it
    //
    SdtUpdateINTxPkg (AcpiSdt, INTAPkgHandle, (PciDeviceInfo->INTA[IsAPIC]), IsAPIC);
  } else if ((PciDeviceInfo->INTA[IsAPIC] != 0xFF) && (INTAPkgHandle == NULL)) {
    //
    // There is valid INTA update item, but no INA package exist, should add it
    //
    DEBUG ((EFI_D_ERROR, "\n\nShould add INTA item for this device(0x%x)\n\n", PciAddress));

  } else if ((PciDeviceInfo->INTA[IsAPIC] == 0xFF) && (INTAPkgHandle != NULL) && IsAllFunctions) {
    //
    // For all functions senario, if there is invalid INTA update item, but INTA package does exist, should delete it
    //
    DEBUG ((EFI_D_ERROR, "\n\nShould remove INTA item for this device(0x%x)\n\n", PciAddress));

  }

  //
  // Check INTB
  //
  if ((PciDeviceInfo->INTB[IsAPIC] != 0xFF) && (INTBPkgHandle != NULL)) {
    //
    // Find INTB package and there is valid INTB update item, update it
    //
    SdtUpdateINTxPkg (AcpiSdt, INTBPkgHandle, (PciDeviceInfo->INTB[IsAPIC]), IsAPIC);
  } else if ((PciDeviceInfo->INTB[IsAPIC] != 0xFF) && (INTBPkgHandle == NULL)) {
    //
    // There is valid INTB update item, but no INTB package exist, should add it
    //
    DEBUG ((EFI_D_ERROR, "\n\nShould add INTB item for this device(0x%x)\n\n", PciAddress));

  } else if ((PciDeviceInfo->INTB[IsAPIC] == 0xFF) && (INTBPkgHandle != NULL) && IsAllFunctions) {
    //
    // For all functions senario, if there is invalid INTB update item, but INTB package does exist, should delete it
    //
    DEBUG ((EFI_D_ERROR, "\n\nShould remove INTB item for this device(0x%x)\n\n", PciAddress));

  }

  //
  // Check INTC
  //
  if ((PciDeviceInfo->INTC[IsAPIC] != 0xFF) && (INTCPkgHandle != NULL)) {
    //
    // Find INTC package and there is valid INTC update item, update it
    //
    SdtUpdateINTxPkg (AcpiSdt, INTCPkgHandle, (PciDeviceInfo->INTC[IsAPIC]), IsAPIC);
  } else if ((PciDeviceInfo->INTC[IsAPIC] != 0xFF) && (INTCPkgHandle == NULL)) {
    //
    // There is valid INTC update item, but no INTC package exist, should add it
    //
    DEBUG ((EFI_D_ERROR, "\n\nShould add INTC item for this device(0x%x)\n\n", PciAddress));

  } else if ((PciDeviceInfo->INTC[IsAPIC] == 0xFF) && (INTCPkgHandle != NULL) && IsAllFunctions) {
    //
    // For all functions senario, if there is invalid INTC update item, but INTC package does exist, should delete it
    //
    DEBUG ((EFI_D_ERROR, "\n\nShould remove INTC item for this device(0x%x)\n\n", PciAddress));
  }

  //
  // Check INTD
  //
  if ((PciDeviceInfo->INTD[IsAPIC] != 0xFF) && (INTDPkgHandle != NULL)) {
    //
    // Find INTD package and there is valid INTD update item, update it
    //
    SdtUpdateINTxPkg (AcpiSdt, INTDPkgHandle, (PciDeviceInfo->INTD[IsAPIC]), IsAPIC);
  } else if ((PciDeviceInfo->INTD[IsAPIC] != 0xFF) && (INTDPkgHandle == NULL)) {
    //
    // There is valid INTD update item, but no INTD package exist, should add it
    //
    DEBUG ((EFI_D_ERROR, "\n\nShould add INTD item for this device(0x%x)\n\n", PciAddress));

  }  else if ((PciDeviceInfo->INTD[IsAPIC] == 0xFF) && (INTDPkgHandle != NULL) && IsAllFunctions) {
    //
    // For all functions senario, if there is invalid INTD update item, but INTD package does exist, should delete it
    //
    DEBUG ((EFI_D_ERROR, "\n\nShould remove INTD item for this device(0x%x)\n\n", PciAddress));
  }


  if (INTAPkgHandle != NULL) {
    Status = AcpiSdt->Close (INTAPkgHandle);
    ASSERT_EFI_ERROR (Status);
  }

  if (INTBPkgHandle != NULL) {
    Status = AcpiSdt->Close (INTBPkgHandle);
    ASSERT_EFI_ERROR (Status);
  }

  if (INTCPkgHandle != NULL) {
    Status = AcpiSdt->Close (INTCPkgHandle);
    ASSERT_EFI_ERROR (Status);
  }

  if (INTDPkgHandle != NULL) {
    Status = AcpiSdt->Close (INTDPkgHandle);
    ASSERT_EFI_ERROR (Status);
  }

  return;
}

/**
  Check every return package for update PRT

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param ParentHandle     ACPI pci device handle
  @param PciDeviceInfo    Pointer to PCI_DEVICE_INFO

**/
VOID
SdtCheckReturnPackage (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        MethodHandle,
  IN PCI_DEVICE_INFO        *PciDeviceInfo
  )
{
  EFI_ACPI_HANDLE    PreviousHandle;
  EFI_ACPI_HANDLE    ReturnHandle;
  EFI_ACPI_HANDLE    PackageHandle;
  EFI_ACPI_HANDLE    NamePkgHandle;
  EFI_STATUS         Status;
  EFI_ACPI_DATA_TYPE DataType;
  UINT8              *Data;
  UINTN              DataSize;
  CHAR8              NameStr[128];

  ReturnHandle = NULL;
  while (TRUE) {
    PreviousHandle = ReturnHandle;
    Status = AcpiSdt->GetChild (MethodHandle, &ReturnHandle);
    ASSERT_EFI_ERROR (Status);

    if (PreviousHandle != NULL) {
      Status = AcpiSdt->Close (PreviousHandle);
      ASSERT_EFI_ERROR (Status);
    }

    if (ReturnHandle == NULL) {
      break;
    }

    Status = AcpiSdt->GetOption (ReturnHandle, 0, &DataType, (CONST VOID **)&Data, &DataSize);
    ASSERT_EFI_ERROR (Status);
    ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE);

    if (*Data == AML_RETURN_OP) {
      //
      // Find the return method handle, then look for the returned package data
      //
      Status = AcpiSdt->GetOption (ReturnHandle, 1, &DataType, (CONST VOID **)&Data, &DataSize);
      ASSERT_EFI_ERROR (Status);


      if (DataType == EFI_ACPI_DATA_TYPE_NAME_STRING) {
        ZeroMem (NameStr, 128);
        AsciiStrCpyS (NameStr, 128, "\\_SB.");
        DataSize = SdtGetNameStringSize (Data);
        AsciiStrnCatS (NameStr, 128, (CHAR8 *)Data, DataSize);

        NamePkgHandle = NULL;
        Status = AcpiSdt->FindPath (mDsdtHandle, NameStr, &NamePkgHandle);
        ASSERT_EFI_ERROR (Status);
        ASSERT (NamePkgHandle != NULL);

        Status = AcpiSdt->GetOption (NamePkgHandle, 0, &DataType, (CONST VOID **)&Data, &DataSize);
        ASSERT_EFI_ERROR (Status);
        ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE);
        ASSERT (*Data == AML_NAME_OP);

        Status = AcpiSdt->GetOption (NamePkgHandle, 2, &DataType, (CONST VOID **)&Data, &DataSize);
        ASSERT_EFI_ERROR (Status);
        ASSERT (DataType == EFI_ACPI_DATA_TYPE_CHILD);
      }

      ASSERT (DataType == EFI_ACPI_DATA_TYPE_CHILD);

      //
      // Get the parent package handle
      //
      PackageHandle = NULL;
      Status = AcpiSdt->Open (Data, &PackageHandle);
      ASSERT_EFI_ERROR (Status);

      //
      // Check the parent package for update pci routing
      //
      SdtCheckParentPackage (AcpiSdt, PackageHandle, PciDeviceInfo);

      Status = AcpiSdt->Close (PackageHandle);
      ASSERT_EFI_ERROR (Status);

      Status = AcpiSdt->Close (ReturnHandle);
      ASSERT_EFI_ERROR (Status);

      break;
    }

    //
    // Not ReturnOp, search it as parent
    //
    SdtCheckReturnPackage (AcpiSdt, ReturnHandle, PciDeviceInfo);
  }

  //
  // Done
  //
  return;

}

/**
  update interrupt info inside the PRT method for the given pci device handle

  @param AcpiSdt       Pointer to Acpi SDT protocol
  @param PciHandle     ACPI pci device handle
  @param PciDeviceInfo Pointer to PCI_DEVICE_INFO

**/
EFI_STATUS
SdtUpdatePrtMethod (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        PciHandle,
  IN PCI_DEVICE_INFO        *PciDeviceInfo
  )
{
  EFI_STATUS         Status;
  EFI_ACPI_HANDLE    PrtMethodHandle;

  //
  // Find the PRT method under this pci device
  //
  PrtMethodHandle = NULL;
  Status = AcpiSdt->FindPath (PciHandle, "_PRT", &PrtMethodHandle);

  if (EFI_ERROR (Status)) {
    return EFI_INVALID_PARAMETER;
  }

  if (PrtMethodHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  SdtCheckReturnPackage(AcpiSdt, PrtMethodHandle, PciDeviceInfo);

  Status = AcpiSdt->Close (PrtMethodHandle);
  ASSERT_EFI_ERROR (Status);

  return Status;
}


/**
  Update the package inside name op with correct wakeup resources

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param InPkgHandle      ACPI inside package handle
  @param GPEPin           Correct gpe pin
  @param SxNum            Correct system state the device can wake up from

**/
VOID
SdtUpdatePackageInName (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        INTxPkgHandle,
  IN UINT8                  GPEPin,
  IN UINT8                  SxNum
  )
{
  EFI_ACPI_HANDLE    PreviousHandle;
  EFI_STATUS         Status;
  EFI_ACPI_HANDLE    MemberHandle;
  EFI_ACPI_DATA_TYPE DataType;
  UINT8              *Data;
  UINTN              DataSize;

  //
  // Check the gpe pin
  //
  MemberHandle = NULL;
  Status = AcpiSdt->GetChild (INTxPkgHandle, &MemberHandle);
  ASSERT_EFI_ERROR (Status);
  ASSERT (MemberHandle != NULL);

  Status = AcpiSdt->GetOption (MemberHandle, 0, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE);

  //
  // Skip byte prefix
  //
  Data += 1;

  if (*Data != GPEPin) {

    *Data = GPEPin;
  }

  //
  // Check the sx number
  //
  PreviousHandle = MemberHandle;
  Status = AcpiSdt->GetChild (INTxPkgHandle, &MemberHandle);
  ASSERT_EFI_ERROR (Status);
  ASSERT (MemberHandle != NULL);

  if (PreviousHandle != NULL) {
    Status = AcpiSdt->Close (PreviousHandle);
    ASSERT_EFI_ERROR (Status);
  }

  Status = AcpiSdt->GetOption (MemberHandle, 0, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE);

  //
  // Skip byte prefix
  //
  Data += 1;

  if (*Data != SxNum) {

    *Data = SxNum;
  }

  Status = AcpiSdt->Close (MemberHandle);
  ASSERT_EFI_ERROR (Status);

}

/**
  Check the name package belonged to PRW

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param PrwPkgHandle     ACPI PRW package handle
  @param PciDeviceInfo    Pointer to PCI_DEVICE_INFO

**/
VOID
SdtCheckNamePackage (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        PrwPkgHandle,
  IN PCI_DEVICE_INFO        *PciDeviceInfo
  )
{
  EFI_ACPI_HANDLE    InPkgHandle;
  EFI_STATUS         Status;
  EFI_ACPI_DATA_TYPE DataType;
  UINT8              *Data;
  UINTN              DataSize;

  Status = AcpiSdt->GetOption (PrwPkgHandle, 0, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_OPCODE);
  ASSERT (*Data == AML_NAME_OP);

  Status = AcpiSdt->GetOption (PrwPkgHandle, 2, &DataType, (CONST VOID **)&Data, &DataSize);
  ASSERT_EFI_ERROR (Status);
  ASSERT (DataType == EFI_ACPI_DATA_TYPE_CHILD);

  //
  // Get the inside package handle
  //
  InPkgHandle = NULL;
  Status = AcpiSdt->Open (Data, &InPkgHandle);
  ASSERT_EFI_ERROR (Status);

  //
  // update the package in name op for wakeup info
  //
  if ((PciDeviceInfo->GPEPin != 0xFF) && (PciDeviceInfo->SxNum != 0xFF))
    SdtUpdatePackageInName (AcpiSdt, InPkgHandle, PciDeviceInfo->GPEPin, PciDeviceInfo->SxNum);

  Status = AcpiSdt->Close (InPkgHandle);
  ASSERT_EFI_ERROR (Status);

  return;

}

/**
  update wakeup info inside the PRW method for the given pci device handle

  @param AcpiSdt       Pointer to Acpi SDT protocol
  @param PciHandle     ACPI pci device handle
  @param PciDeviceInfo Pointer to PCI_DEVICE_INFO

**/
EFI_STATUS
SdtUpdatePrwPackage (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        PciHandle,
  IN PCI_DEVICE_INFO        *PciDeviceInfo
  )
{
  EFI_STATUS         Status;
  EFI_ACPI_HANDLE    PrwPkgHandle;

  //
  // Find the PRT method under this pci device
  //
  PrwPkgHandle = NULL;
  Status = AcpiSdt->FindPath (PciHandle, "_PRW", &PrwPkgHandle);

  if (EFI_ERROR (Status)) {
    return EFI_INVALID_PARAMETER;
  }

  if (PrwPkgHandle == NULL) {
    return EFI_INVALID_PARAMETER;
  }

  SdtCheckNamePackage(AcpiSdt, PrwPkgHandle, PciDeviceInfo);

  Status = AcpiSdt->Close (PrwPkgHandle);
  ASSERT_EFI_ERROR (Status);

  return Status;
}

/**
  update pci routing information in acpi table based on pcd settings

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param PciRootHandle       ACPI root bridge handle
  @param PciDeviceInfo    Pointer to PCI_DEVICE_INFO

**/
EFI_STATUS
SdtUpdatePciRouting (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        PciRootHandle,
  IN PCI_DEVICE_INFO        *PciDeviceInfo
  )
{
  EFI_STATUS         Status;
  EFI_ACPI_HANDLE    PciBridgeHandle;
  UINT32             PciAddress;


  PciBridgeHandle = NULL;
  if (PciDeviceInfo->BridgeAddress == 0x00000000) {
    //
    // Its bridge is the host root bridge
    //
    PciBridgeHandle = PciRootHandle;

  } else {

    //
    // Its bridge is just a pci device under the host bridge
    //

    //
    // Conver the bridge address into one that acpi table can recognize
    //
    PciAddress = SdtConvertToAcpiPciAdress (PciDeviceInfo->BridgeAddress);

    //
    // Scan the whole table to find the pci device
    //
    PciBridgeHandle = SdtGetHandleByScanAllChilds(AcpiSdt, PciRootHandle, SdtFindPciDeviceHandle, &PciAddress);
    if (PciBridgeHandle == NULL) {

      return EFI_INVALID_PARAMETER;
    }
  }

  Status = SdtUpdatePrtMethod(AcpiSdt, PciBridgeHandle, PciDeviceInfo);

  if (PciDeviceInfo->BridgeAddress != 0x00000000) {
    Status = AcpiSdt->Close (PciBridgeHandle);
    ASSERT_EFI_ERROR (Status);
  }

  return Status;
}


/**
  update power resource wake up information in acpi table based on pcd settings

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param PciRootHandle    ACPI root bridge handle
  @param PciDeviceInfo    Pointer to PCI_DEVICE_INFO

**/
EFI_STATUS
SdtUpdatePowerWake (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        PciRootHandle,
  IN PCI_DEVICE_INFO        *PciDeviceInfo
  )
{
  EFI_STATUS         Status;
  EFI_ACPI_HANDLE    PciBridgeHandle;
  EFI_ACPI_HANDLE    PciDeviceHandle;
  UINT32             PciAddress;

  PciBridgeHandle = NULL;
  if (PciDeviceInfo->BridgeAddress == 0x00000000) {
    //
    // Its bridge is the host root bridge
    //
    PciBridgeHandle = PciRootHandle;

  } else {

    //
    // Its bridge is just a pci device under the host bridge
    //

    //
    // Conver the bridge address into one that acpi table can recognize
    //
    PciAddress = SdtConvertToAcpiPciAdress (PciDeviceInfo->BridgeAddress);

    //
    // Scan the whole table to find the pci device
    //
    PciBridgeHandle = SdtGetHandleByScanAllChilds(AcpiSdt, PciRootHandle, SdtFindPciDeviceHandle, &PciAddress);

    if (PciBridgeHandle == NULL) {

      Status = AcpiSdt->Close (PciRootHandle);
      ASSERT_EFI_ERROR (Status);

      return EFI_INVALID_PARAMETER;
    }
  }

  PciDeviceHandle = NULL;

  //
  // Conver the device address into one that acpi table can recognize
  //
  PciAddress = SdtConvertToAcpiPciAdress (PciDeviceInfo->DeviceAddress);

  //
  // Scan the whole table to find the pci device
  //
  PciDeviceHandle = SdtGetHandleByScanAllChilds(AcpiSdt, PciBridgeHandle, SdtFindPciDeviceHandle, &PciAddress);

  if (PciDeviceHandle == NULL) {
    if (PciDeviceInfo->BridgeAddress != 0x00000000) {
      Status = AcpiSdt->Close (PciBridgeHandle);
      ASSERT_EFI_ERROR (Status);
    }

    return EFI_INVALID_PARAMETER;
  }

  Status = SdtUpdatePrwPackage(AcpiSdt, PciDeviceHandle, PciDeviceInfo);

  Status = AcpiSdt->Close (PciDeviceHandle);
  ASSERT_EFI_ERROR (Status);

  if (PciDeviceInfo->BridgeAddress != 0x00000000) {
    Status = AcpiSdt->Close (PciBridgeHandle);
    ASSERT_EFI_ERROR (Status);
  }

  return Status;
}


/**
  Get the root bridge handle by scanning the acpi table

  @param AcpiSdt          Pointer to Acpi SDT protocol
  @param DsdtHandle       ACPI root handle

  @retval EFI_ACPI_HANDLE the handle of the root bridge
**/
EFI_ACPI_HANDLE
SdtGetRootBridgeHandle (
  IN EFI_ACPI_SDT_PROTOCOL  *AcpiSdt,
  IN EFI_ACPI_HANDLE        DsdtHandle
  )
{
  EFI_ACPI_HANDLE    PciRootHandle;

  //
  // Scan the whole table to find the root bridge
  //
  PciRootHandle = NULL;
  PciRootHandle = SdtGetHandleByScanAllChilds(AcpiSdt, DsdtHandle, SdtFindRootBridgeHandle, NULL);
  ASSERT (PciRootHandle != NULL);

  return PciRootHandle;
}


/**
  Check input Pci device info is changed from the default values
  @param PciDeviceInfo    Pointer to PCI_DEVICE_INFO
  @param UpdatePRT        Pointer to BOOLEAN
  @param UpdatePRW        Pointer to BOOLEAN

**/
VOID
SdtCheckPciDeviceInfoChanged (
  IN PCI_DEVICE_INFO        *PciDeviceInfo,
  IN BOOLEAN                *UpdatePRT,
  IN BOOLEAN                *UpdatePRW
  )
{
  UINTN Index = 0;

  if (mQNCPciInfo == NULL) {
    *UpdatePRT = FALSE;
    *UpdatePRW = FALSE;
    return;
  }

  *UpdatePRT = TRUE;
  *UpdatePRW = TRUE;

  for (Index = 0;Index < CURRENT_PCI_DEVICE_NUM; Index++) {
    if ((mQNCPciInfo[Index].BridgeAddress == PciDeviceInfo->BridgeAddress)
      && (mQNCPciInfo[Index].DeviceAddress == PciDeviceInfo->DeviceAddress)) {
      //
      // Find one matched entry
      //
      if (CompareMem (&(mQNCPciInfo[Index].INTA[0]), &PciDeviceInfo->INTA[0], 10) == 0) {
        *UpdatePRT = FALSE;
        *UpdatePRW = FALSE;
        //DEBUG ((EFI_D_ERROR, "Find one matched entry[%d] and no change\n", Index));
      } else {
        if (CompareMem (&(mQNCPciInfo[Index].INTA[0]), &PciDeviceInfo->INTA[0], 8) == 0)
          *UpdatePRT = FALSE;

        if (CompareMem (&(mQNCPciInfo[Index].GPEPin), &PciDeviceInfo->GPEPin, 2) == 0)
          *UpdatePRW = FALSE;

        if (*(UINT64 *)(&PciDeviceInfo->INTA[0]) == 0xFFFFFFFFFFFFFFFFULL)
          *UpdatePRT = FALSE;

        if (*(UINT16 *)(&PciDeviceInfo->GPEPin) == 0xFFFF)
          *UpdatePRW = FALSE;

        //DEBUG ((EFI_D_ERROR, "Find one matched entry[%d] and but need update PRT:0x%x PRW:0x%x\n", Index, *UpdatePRT, *UpdatePRW));
      }
      break;
    }
  }

  //if (Index == 42) {
  //  DEBUG ((EFI_D_ERROR, "Find No matched entry\n"));
  //}

  return;
}
