/** @file
  This file contains the mpservices helper functions

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

**/

#include "HstiSiliconDxe.h"

typedef struct {
  UINT32  Index;
  UINT64  Value;
} AP_PRPCEDURE_ARGUMENT_READMSR;

typedef struct {
  UINT32  Index;
  UINT32  Eax;
  UINT32  Ebx;
  UINT32  Ecx;
  UINT32  Edx;
} AP_PRPCEDURE_ARGUMENT_CPUID;

EFI_MP_SERVICES_PROTOCOL  *mMpService;
UINTN                     mBspNumber;
UINTN                     mNumberOfProcessors;
UINTN                     mNumberOfEnabledProcessors;

/**
  Initialize MP Helper
**/
VOID
InitMp (
  VOID
  )
{
  EFI_STATUS  Status;

  DEBUG ((EFI_D_INFO, "InitMp\n"));

  Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID **) &mMpService);
  if (EFI_ERROR (Status)) {
    mMpService = NULL;
  }

  if (mMpService != NULL) {
    Status = mMpService->WhoAmI (mMpService, &mBspNumber);
    ASSERT_EFI_ERROR (Status);

    Status = mMpService->GetNumberOfProcessors (mMpService, &mNumberOfProcessors, &mNumberOfEnabledProcessors);
    ASSERT_EFI_ERROR (Status);
  } else {
    mBspNumber = 0;
    mNumberOfProcessors = 1;
    mNumberOfEnabledProcessors = 1;
  }

  DEBUG ((DEBUG_INFO, "BspNumber                 - 0x%x\n", mBspNumber));
  DEBUG ((DEBUG_INFO, "NumberOfProcessors        - 0x%x\n", mNumberOfProcessors));
  DEBUG ((DEBUG_INFO, "NumberOfEnabledProcessors - 0x%x\n", mNumberOfEnabledProcessors));
}

/**
  Concatenate error string.

  @retval UINTN - CpuNumber.
**/
UINTN
GetCpuNumber (
  VOID
  )
{
  return mNumberOfEnabledProcessors;
}

VOID
EFIAPI
ApReadMsr64 (
  IN OUT VOID  *Buffer
  )
{
  AP_PRPCEDURE_ARGUMENT_READMSR  *Argument;

  Argument = Buffer;
  Argument->Value = AsmReadMsr64 (Argument->Index);
}

/**
  Concatenate error string.

  @param[in, out] Buffer     - Pointer to Argument

**/
VOID
EFIAPI
ApCpuId (
  IN OUT VOID  *Buffer
  )
{
  AP_PRPCEDURE_ARGUMENT_CPUID  *Argument;

  Argument = Buffer;
  AsmCpuid (Argument->Index, &Argument->Eax, &Argument->Ebx, &Argument->Ecx, &Argument->Edx);
}

/**
  Concatenate error string.

  @param[in] ProcessorNumber     - Processor ID
  @param[in] Index               - Index

  @retval UINT64 - Msr Value.
**/
UINT64
ProcessorReadMsr64 (
  IN UINTN   ProcessorNumber,
  IN UINT32  Index
  )
{
  EFI_STATUS                      Status;
  AP_PRPCEDURE_ARGUMENT_READMSR   Argument;
  UINT8                           WakeApRetry;

  ASSERT (ProcessorNumber < mNumberOfEnabledProcessors);
  if (ProcessorNumber == mBspNumber) {
    return AsmReadMsr64 (Index);
  }

  ZeroMem (&Argument, sizeof (Argument));
  Argument.Index = Index;
  WakeApRetry = 0;
  do {
    Status = mMpService->StartupThisAP (
                           mMpService,
                           ApReadMsr64,
                           ProcessorNumber,
                           NULL,   // WaitEvent
                           1000,   // 1ms timeout
                           &Argument,
                           NULL    // Finished
                           );
    WakeApRetry++;
  } while ((Status == EFI_TIMEOUT) && (WakeApRetry < 3));
  ASSERT_EFI_ERROR (Status);

  return Argument.Value;
}

/**
  Concatenate error string.

  @param[in]  ProcessorNumber     - Processor ID
  @param[in]  Index               - Index
  @param[out] Eax                 - Eax
  @param[out] Ebx                 - Ebx
  @param[out] Ecx                 - Ecx
  @param[out] Edx                 - Edx
**/
VOID
ProcessorCpuid (
  IN  UINTN   ProcessorNumber,
  IN  UINT32  Index,
  OUT UINT32  *Eax,  OPTIONAL
  OUT UINT32  *Ebx,  OPTIONAL
  OUT UINT32  *Ecx,  OPTIONAL
  OUT UINT32  *Edx   OPTIONAL
  )
{
  EFI_STATUS                      Status;
  AP_PRPCEDURE_ARGUMENT_CPUID     Argument;
  UINT8                           WakeApRetry;

  ASSERT (ProcessorNumber < mNumberOfEnabledProcessors);
  if (ProcessorNumber == mBspNumber) {
    AsmCpuid (Index, Eax, Ebx, Ecx, Edx);
    return;
  }

  ZeroMem (&Argument, sizeof (Argument));
  Argument.Index = Index;
  WakeApRetry = 0;
  do {
    Status = mMpService->StartupThisAP (
                           mMpService,
                           ApCpuId,
                           ProcessorNumber,
                           NULL,   // WaitEvent
                           1000,   // 1ms timeout
                           &Argument,
                           NULL    // Finished
                           );
    WakeApRetry++;
  } while ((Status == EFI_TIMEOUT) && (WakeApRetry < 3));
  ASSERT_EFI_ERROR (Status);

  if (Eax != NULL) {
    *Eax = Argument.Eax;
  }
  if (Ebx != NULL) {
    *Ebx = Argument.Ebx;
  }
  if (Ecx != NULL) {
    *Ecx = Argument.Ecx;
  }
  if (Edx != NULL) {
    *Edx = Argument.Edx;
  }

  return;
}