/** @file
*
*  Copyright (c) 2018, Hisilicon Limited. All rights reserved.
*  Copyright (c) 2018, Linaro Limited. All rights reserved.
*
*  SPDX-License-Identifier: BSD-2-Clause-Patent
*
**/
#include "Einj.h"
#include <IndustryStandard/Acpi62.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include "OemApeiHi1620.h"
#include <PlatformArch.h>


EINJ_TABLE mEinj = {
  {
    ARM_ACPI_HEADER (
      EFI_ACPI_6_0_ERROR_INJECTION_TABLE_SIGNATURE,
      EFI_ACPI_6_0_ERROR_INJECTION_TABLE_HEADER,
      EFI_ACPI_6_0_ERROR_INJECTION_TABLE_REVISION
    ),
    sizeof (EFI_ACPI_6_0_ERROR_INJECTION_TABLE_HEADER),
    0x0,
    {
      0x0,
      0x0,
      0x0
    },
    EINJ_ACTION_NO
  },
  {
    {
      // 0 EFI_ACPI_6_0_EINJ_BEGIN_INJECTION_OPERATION
      EFI_ACPI_6_0_EINJ_BEGIN_INJECTION_OPERATION,
      EFI_ACPI_6_0_EINJ_WRITE_REGISTER_VALUE,
      EFI_ACPI_6_0_EINJ_PRESERVE_REGISTER,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_QWORD,
        0
      },
      EINJ_BEGIN_OPERATION_VALUE,
      EINJ_WRITE_MASK
    },
    {
      // 1 EFI_ACPI_6_0_EINJ_GET_TRIGGER_ERROR_ACTION_TABLE
      EFI_ACPI_6_0_EINJ_GET_TRIGGER_ERROR_ACTION_TABLE,
      EFI_ACPI_6_0_EINJ_READ_REGISTER,
      0,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_QWORD,
        0
      },
      0,
      EINJ_READ_MASK
    },
    {
      // 2 EFI_ACPI_6_0_EINJ_SET_ERROR_TYPE
      EFI_ACPI_6_0_EINJ_SET_ERROR_TYPE,
      EFI_ACPI_6_0_EINJ_WRITE_REGISTER,
      EFI_ACPI_6_0_EINJ_PRESERVE_REGISTER,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_QWORD,
        0
      },
      0,
      EINJ_WRITE_MASK
    },
    {
      // 3 EFI_ACPI_6_0_EINJ_GET_ERROR_TYPE
      EFI_ACPI_6_0_EINJ_GET_ERROR_TYPE,
      EFI_ACPI_6_0_EINJ_READ_REGISTER,
      0,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_QWORD,
        0
      },
      0,
      0xFFFFFFFF
    },
    {
      // 4 EFI_ACPI_6_0_EINJ_END_OPERATION
      EFI_ACPI_6_0_EINJ_END_OPERATION,
      EFI_ACPI_6_0_EINJ_WRITE_REGISTER_VALUE,
      EFI_ACPI_6_0_EINJ_PRESERVE_REGISTER,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_QWORD,
        0
      },
      EINJ_END_OPERATION_VALUE,
      0xFFFFFFFF
    },
    {
      // 5 EFI_ACPI_6_0_EINJ_EXECUTE_OPERATION
      EFI_ACPI_6_0_EINJ_EXECUTE_OPERATION,
      EFI_ACPI_6_0_EINJ_WRITE_REGISTER_VALUE,
      EFI_ACPI_6_0_EINJ_PRESERVE_REGISTER,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        32,
        0,
        EFI_ACPI_6_0_DWORD,
        GPIO1_BASE + GPIO_INT_MASK //0x40070008//0x4d000F00//GPIO0_BASE + GPIO0_SWPORT_DR_OFFSET
      },
      0,
      0xFFFFFFFF //BIT0
    },
    {
      // 6 EFI_ACPI_6_0_EINJ_CHECK_BUSY_STATUS
      EFI_ACPI_6_0_EINJ_CHECK_BUSY_STATUS,
      EFI_ACPI_6_0_EINJ_READ_REGISTER_VALUE,
      0,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_QWORD,
        0
      },
      0x01,
      0x01
    },
    {
      // 7 EFI_ACPI_6_0_EINJ_GET_COMMAND_STATUS
      EFI_ACPI_6_0_EINJ_GET_COMMAND_STATUS,
      EFI_ACPI_6_0_EINJ_READ_REGISTER,
      0,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_QWORD,
        0
      },
      0,
      0x3
    },
    {
      // 8 EFI_ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS
      EFI_ACPI_EINJ_SET_ERROR_TYPE_WITH_ADDRESS,
      EFI_ACPI_6_0_EINJ_WRITE_REGISTER,
      EFI_ACPI_6_0_EINJ_PRESERVE_REGISTER,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_QWORD,
        0
      },
      0,
      0xFFFFFF
    },
    {
      // 9 EFI_ACPI_EINJ_GET_EXCUTE_OPERATION_TIMINGS
      EFI_ACPI_EINJ_GET_EXCUTE_OPERATION_TIMINGS,
      EFI_ACPI_6_0_EINJ_WRITE_REGISTER,
      EFI_ACPI_6_0_EINJ_PRESERVE_REGISTER,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_QWORD,
        0
      },
      0,
      0xFFFFFF
    }
  }
};

EINJ_TRIGGER_ERROR_ACTION mEinjTriggerErrorAction = {
  {
    sizeof (EFI_ACPI_6_0_EINJ_TRIGGER_ACTION_TABLE),
    0,
    sizeof (EINJ_TRIGGER_ERROR_ACTION),
    EINJ_TRIGGER_ERROR_ACTION_NO
  },
  {
    {
      EFI_ACPI_6_0_EINJ_TRIGGER_ERROR,
      EFI_ACPI_6_0_EINJ_NOOP,
      0,
      0,
      {
        EFI_ACPI_6_0_SYSTEM_MEMORY,
        64,
        0,
        EFI_ACPI_6_0_DWORD,
        0
      },
      0,
      0
    }
  }
};


VOID
EinjSetAcpiTable (
  EINJ_CONTEXT *Context
)
{
  UINTN                   AcpiTableHandle;
  EFI_STATUS              Status;
  UINT8                   Checksum;
  EFI_ACPI_SDT_HEADER     *Table;
  EFI_ACPI_TABLE_VERSION  TableVersion;
  UINTN                   TableKey;
  UINTN                   i;

  Context->EINJ->EinjTableHeader.Header.Length = sizeof (EINJ_TABLE);
  Checksum = CalculateCheckSum8 (
               (UINT8*)(Context->EINJ),
               Context->EINJ->EinjTableHeader.Header.Length);
  Context->EINJ->EinjTableHeader.Header.Checksum = Checksum;
  AcpiTableHandle = 0;
  Status = mAcpiTableProtocol->InstallAcpiTable (
             mAcpiTableProtocol,
             Context->EINJ,
             Context->EINJ->EinjTableHeader.Header.Length,
             &AcpiTableHandle
           );
  for (i = 0; i < EFI_ACPI_MAX_NUM_TABLES; i++) {
    Status = mAcpiSdtProtocol->GetAcpiTable (i, &Table, &TableVersion, &TableKey);
    if (EFI_ERROR (Status)) {
      break;
    }
    if (Table->Signature != EFI_ACPI_6_0_ERROR_INJECTION_TABLE_SIGNATURE) {
      continue;
    }
    mApeiTrustedfirmwareData->EinjTableAddress = (EFI_PHYSICAL_ADDRESS)Table;
    mApeiTrustedfirmwareData->EinjDataStruct = (EFI_PHYSICAL_ADDRESS)Context->EinjData;
  }
  ASSERT_EFI_ERROR (Status) ;
}
//V2
EFI_STATUS
EinjHeaderCreator (
  EINJ_CONTEXT *Context
)
{
  EFI_STATUS           Status;
  EINJ_DATA_STRUCTURE  *EinjData = NULL;
  Status = gBS->AllocatePool (
             EfiReservedMemoryType,
             sizeof (EINJ_DATA_STRUCTURE),
             (VOID**)&EinjData
           );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  gBS->SetMem (
    EinjData,
    sizeof (EINJ_DATA_STRUCTURE),
    0
  );

  DEBUG ((DEBUG_INFO, "EINJ EinjData is at 0x%X,size =0x%x\n",
          EinjData, sizeof (EINJ_DATA_STRUCTURE)));
  EinjData->TriggerErrorActionTablePtr =
    (EINJ_TRIGGER_ERROR_ACTION*)(&(EinjData->TriggerErrorActionTable));
  gBS->CopyMem (
         EinjData->TriggerErrorActionTablePtr,
         &mEinjTriggerErrorAction,
         sizeof (EINJ_TRIGGER_ERROR_ACTION));
  EinjData->OperationBegin = 0;
  EinjData->ErrorType = 0;
  EinjData->ErrorCapabilities = 0xFFF;
  EinjData->BusyStatus = 0;
  EinjData->CommandStatus = 0;
  mEinj.EinjInstructionEntry[0].RegisterRegion.Address =
    (UINT64)(&(EinjData->OperationBegin));
  mEinj.EinjInstructionEntry[1].RegisterRegion.Address =
    (UINT64)(&(EinjData->TriggerErrorActionTablePtr));
  mEinj.EinjInstructionEntry[2].RegisterRegion.Address =
    (UINT64)(&(EinjData->ErrorType));
  mEinj.EinjInstructionEntry[3].RegisterRegion.Address =
    (UINT64)(&(EinjData->ErrorCapabilities));
  mEinj.EinjInstructionEntry[4].RegisterRegion.Address =
    (UINT64)(&(EinjData->OperationBegin));
  mEinj.EinjInstructionEntry[6].RegisterRegion.Address =
    (UINT64)(&(EinjData->BusyStatus));
  mEinj.EinjInstructionEntry[7].RegisterRegion.Address =
    (UINT64)(&(EinjData->CommandStatus));
  mEinj.EinjInstructionEntry[8].RegisterRegion.Address =
    (UINT64)(&(EinjData->ErrorTypeWithAddress));
  mEinj.EinjInstructionEntry[9].RegisterRegion.Address =
    (UINT64)(&(EinjData->Timing));
  EinjData->ErrorTypeWithAddress.VendorErrorTypeOffset =
    (UINT32)((UINTN)&(EinjData->VendorErrorTypeExtension) -
    (UINTN)&(EinjData->ErrorTypeWithAddress));
  Context->EinjData = EinjData;
  Context->EINJ = &mEinj;
  Context->ExecuteOperationEntry = &mEinj.EinjInstructionEntry[5];
  Context->GetErrorTypeEntry = &mEinj.EinjInstructionEntry[3];
  return EFI_SUCCESS;
}


EFI_STATUS
EinjConfigErrorInjectCapability (
  EINJ_CONTEXT* Context,
  UINT32        BitsSupportedErrorType
)
{
  EFI_ACPI_6_0_EINJ_INJECTION_INSTRUCTION_ENTRY* KeyEntry;
  UINT32*                                        EinjCapablity;

  KeyEntry = Context->GetErrorTypeEntry;
  EinjCapablity = (UINT32*)KeyEntry->RegisterRegion.Address;
  *EinjCapablity = BitsSupportedErrorType;
  KeyEntry->Value = BitsSupportedErrorType;
  return EFI_SUCCESS;
}

