/** @file

  Copyright (c) 2008, Intel Corporation. All rights reserved.<BR>
  (C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>

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

**/

#include <Library/DebugLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/PcdLib.h>
#include <Library/CpuLib.h>
#include <Library/SynchronizationLib.h>
#include <Uefi/UefiBaseType.h>
#include <IndustryStandard/IntelTdx.h>
#include <IndustryStandard/Tdx.h>
#include <Library/TdxMailboxLib.h>

/**
  This function will be called by BSP to get the CPU number.

  @retval   CPU number
**/
UINT32
EFIAPI
GetCpusNum (
  VOID
  )
{
  UINT64          Status;
  TD_RETURN_DATA  TdReturnData;

  Status = TdCall (TDCALL_TDINFO, 0, 0, 0, &TdReturnData);
  if (Status == TDX_EXIT_REASON_SUCCESS) {
    return TdReturnData.TdInfo.NumVcpus;
  } else {
    DEBUG ((DEBUG_ERROR, "Failed call TDCALL_TDINFO. %llx\n", Status));
  }

  return 0;
}

/**
  Get the address of Td mailbox.
**/
volatile VOID *
EFIAPI
GetTdxMailBox (
  VOID
  )
{
  return (VOID *)(UINTN)PcdGet32 (PcdOvmfSecGhcbBackupBase);
}

/**
  This function will be called by BSP to wakeup APs the are spinning on mailbox
  in protected mode

  @param[in] Command          Command to send APs
  @param[in] WakeupVector     If used, address for APs to start executing
  @param[in] WakeArgsX        Args to pass to APs for excuting commands
**/
VOID
EFIAPI
MpSendWakeupCommand (
  IN UINT16  Command,
  IN UINT64  WakeupVector,
  IN UINT64  WakeupArgs1,
  IN UINT64  WakeupArgs2,
  IN UINT64  WakeupArgs3,
  IN UINT64  WakeupArgs4
  )
{
  volatile MP_WAKEUP_MAILBOX  *MailBox;

  MailBox               = (volatile MP_WAKEUP_MAILBOX *)GetTdxMailBox ();
  MailBox->ApicId       = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_INVALID;
  MailBox->WakeUpVector = 0;
  MailBox->Command      = MpProtectedModeWakeupCommandNoop;
  MailBox->ApicId       = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_BROADCAST;
  MailBox->WakeUpVector = WakeupVector;
  MailBox->WakeUpArgs1  = WakeupArgs1;
  MailBox->WakeUpArgs2  = WakeupArgs2;
  MailBox->WakeUpArgs3  = WakeupArgs3;
  MailBox->WakeUpArgs4  = WakeupArgs4;
  AsmCpuid (0x01, NULL, NULL, NULL, NULL);
  MailBox->Command = Command;
  AsmCpuid (0x01, NULL, NULL, NULL, NULL);
  return;
}

/**
  BSP wait until all the APs arriving. It means the task triggered by BSP is started.
**/
VOID
EFIAPI
MpSerializeStart (
  VOID
  )
{
  volatile MP_WAKEUP_MAILBOX  *MailBox;
  UINT32                      NumOfCpus;

  NumOfCpus = GetCpusNum ();
  MailBox   = (volatile MP_WAKEUP_MAILBOX *)GetTdxMailBox ();

  DEBUG ((DEBUG_VERBOSE, "Waiting for APs to arriving. NumOfCpus=%d, MailBox=%p\n", NumOfCpus, MailBox));
  while (MailBox->NumCpusArriving != (NumOfCpus -1)) {
    CpuPause ();
  }

  DEBUG ((DEBUG_VERBOSE, "Releasing APs\n"));
  MailBox->NumCpusExiting = NumOfCpus;
  InterlockedIncrement ((UINT32 *)&MailBox->NumCpusArriving);
}

/**
  BSP wait until all the APs arriving. It means the task triggered by BSP is ended.
**/
VOID
EFIAPI
MpSerializeEnd (
  VOID
  )
{
  volatile MP_WAKEUP_MAILBOX  *MailBox;

  MailBox = (volatile MP_WAKEUP_MAILBOX *)GetTdxMailBox ();
  DEBUG ((DEBUG_VERBOSE, "Waiting for APs to finish\n"));
  while (MailBox->NumCpusExiting != 1 ) {
    CpuPause ();
  }

  DEBUG ((DEBUG_VERBOSE, "Restarting APs\n"));
  MailBox->Command         = MpProtectedModeWakeupCommandNoop;
  MailBox->NumCpusArriving = 0;
  InterlockedDecrement ((UINT32 *)&MailBox->NumCpusExiting);
}
