/** @file

Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<BR>
Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
Portions copyright (c) 2011, Apple Inc. All rights reserved.

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

**/

#ifndef MP_SERVICES_INTERNAL_H_
#define MP_SERVICES_INTERNAL_H_

#include <Protocol/Cpu.h>
#include <Protocol/MpService.h>

#include <Library/BaseLib.h>
#include <Library/UefiLib.h>

#define AP_STACK_SIZE  0x1000

//
// Internal Data Structures
//

//
// AP state
//
// The state transitions for an AP when it processes a procedure are:
//  Idle ----> Ready ----> Busy ----> Finished ----> Idle
//       [BSP]       [BSP]      [AP]           [BSP]
//
typedef enum {
  CpuStateIdle,
  CpuStateReady,
  CpuStateBlocked,
  CpuStateBusy,
  CpuStateFinished,
  CpuStateDisabled
} CPU_STATE;

//
// Define Individual Processor Data block.
//
typedef struct {
  EFI_PROCESSOR_INFORMATION    Info;
  EFI_AP_PROCEDURE             Procedure;
  VOID                         *Parameter;
  CPU_STATE                    State;
  EFI_EVENT                    CheckThisAPEvent;
  EFI_EVENT                    WaitEvent;
  UINTN                        Timeout;
  UINTN                        TimeTaken;
  BOOLEAN                      TimeoutActive;
  BOOLEAN                      *SingleApFinished;
} CPU_AP_DATA;

//
// Define MP data block which consumes individual processor block.
//
typedef struct {
  UINTN               NumberOfProcessors;
  UINTN               NumberOfEnabledProcessors;
  EFI_EVENT           CheckAllAPsEvent;
  EFI_EVENT           AllWaitEvent;
  UINTN               FinishCount;
  UINTN               StartCount;
  EFI_AP_PROCEDURE    Procedure;
  VOID                *ProcedureArgument;
  BOOLEAN             SingleThread;
  UINTN               StartedNumber;
  CPU_AP_DATA         *CpuData;
  UINTN               *FailedList;
  UINTN               FailedListIndex;
  UINTN               AllTimeout;
  UINTN               AllTimeTaken;
  BOOLEAN             AllTimeoutActive;
} CPU_MP_DATA;

/** Secondary core entry point.

**/
VOID
ApEntryPoint (
  VOID
  );

/** C entry-point for the AP.
    This function gets called from the assembly function ApEntryPoint.
**/
VOID
ApProcedure (
  VOID
  );

/** Turns on the specified core using PSCI and executes the user-supplied
    function that's been configured via a previous call to SetApProcedure.

   @param ProcessorIndex The index of the core to turn on.

   @retval EFI_SUCCESS       The processor was successfully turned on.
   @retval EFI_DEVICE_ERROR  An error occurred turning the processor on.

**/
STATIC
EFI_STATUS
EFIAPI
DispatchCpu (
  IN UINTN  ProcessorIndex
  );

/** Returns whether the specified processor is the BSP.

   @param[in] ProcessorIndex The index the processor to check.

   @return TRUE if the processor is the BSP, FALSE otherwise.
**/
STATIC
BOOLEAN
IsProcessorBSP (
  UINTN  ProcessorIndex
  );

/** Returns whether the processor executing this function is the BSP.

   @return Whether the current processor is the BSP.
**/
STATIC
BOOLEAN
IsCurrentProcessorBSP (
  VOID
  );

/** Returns whether the specified processor is enabled.

   @param[in] ProcessorIndex The index of the processor to check.

   @return TRUE if the processor is enabled, FALSE otherwise.
**/
STATIC
BOOLEAN
IsProcessorEnabled (
  UINTN  ProcessorIndex
  );

/** Configures the processor context with the user-supplied procedure and
    argument.

   @param CpuData           The processor context.
   @param Procedure         The user-supplied procedure.
   @param ProcedureArgument The user-supplied procedure argument.

**/
STATIC
VOID
SetApProcedure (
  IN   CPU_AP_DATA       *CpuData,
  IN   EFI_AP_PROCEDURE  Procedure,
  IN   VOID              *ProcedureArgument
  );

/**
  Get the Application Processors state.

  @param[in]  CpuData    The pointer to CPU_AP_DATA of specified AP

  @return  The AP status
**/
CPU_STATE
GetApState (
  IN  CPU_AP_DATA  *CpuData
  );

/** Returns the index of the next processor that is blocked.

   @param[out] NextNumber The index of the next blocked processor.

   @retval EFI_SUCCESS   Successfully found the next blocked processor.
   @retval EFI_NOT_FOUND There are no blocked processors.

**/
STATIC
EFI_STATUS
GetNextBlockedNumber (
  OUT UINTN  *NextNumber
  );

/** Stalls the BSP for the minimum of gPollInterval and Timeout.

   @param[in]  Timeout    The time limit in microseconds remaining for
                          APs to return from Procedure.

   @retval     StallTime  Time of execution stall.
**/
STATIC
UINTN
CalculateAndStallInterval (
  IN UINTN  Timeout
  );

/** Sets up the state for the StartupAllAPs function.

   @param SingleThread Whether the APs will execute sequentially.

**/
STATIC
VOID
StartupAllAPsPrepareState (
  IN BOOLEAN  SingleThread
  );

/** Handles execution of StartupAllAPs when a WaitEvent has been specified.

  @param Procedure         The user-supplied procedure.
  @param ProcedureArgument The user-supplied procedure argument.
  @param WaitEvent         The wait event to be signaled when the work is
                           complete or a timeout has occurred.
  @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
                               indicates an infinite timeout.
  @param SingleThread          Whether the APs will execute sequentially.
  @param FailedCpuList         User-supplied pointer for list of failed CPUs.

   @return EFI_SUCCESS on success.
**/
STATIC
EFI_STATUS
StartupAllAPsWithWaitEvent (
  IN EFI_AP_PROCEDURE  Procedure,
  IN VOID              *ProcedureArgument,
  IN EFI_EVENT         WaitEvent,
  IN UINTN             TimeoutInMicroseconds,
  IN BOOLEAN           SingleThread,
  IN UINTN             **FailedCpuList
  );

/** Handles execution of StartupAllAPs when no wait event has been specified.

   @param Procedure             The user-supplied procedure.
   @param ProcedureArgument     The user-supplied procedure argument.
   @param TimeoutInMicroseconds The timeout for the work to be completed. Zero
                                indicates an infinite timeout.
   @param SingleThread          Whether the APs will execute sequentially.
   @param FailedCpuList         User-supplied pointer for list of failed CPUs.

   @return EFI_SUCCESS on success.
**/
STATIC
EFI_STATUS
StartupAllAPsNoWaitEvent (
  IN EFI_AP_PROCEDURE  Procedure,
  IN VOID              *ProcedureArgument,
  IN UINTN             TimeoutInMicroseconds,
  IN BOOLEAN           SingleThread,
  IN UINTN             **FailedCpuList
  );

/** Adds the specified processor the list of failed processors.

   @param ProcessorIndex The processor index to add.
   @param ApState         Processor state.

**/
STATIC
VOID
AddProcessorToFailedList (
  UINTN      ProcessorIndex,
  CPU_STATE  ApState
  );

/** Handles the StartupAllAPs case where the timeout has occurred.

**/
STATIC
VOID
ProcessStartupAllAPsTimeout (
  VOID
  );

/**
  If a timeout is specified in StartupAllAps(), a timer is set, which invokes
  this procedure periodically to check whether all APs have finished.

  @param[in] Event   The WaitEvent the user supplied.
  @param[in] Context The event context.
**/
STATIC
VOID
EFIAPI
CheckAllAPsStatus (
  IN  EFI_EVENT  Event,
  IN  VOID       *Context
  );

/** Invoked periodically via a timer to check the state of the processor.

   @param Event   The event supplied by the timer expiration.
   @param Context The processor context.

**/
STATIC
VOID
EFIAPI
CheckThisAPStatus (
  IN  EFI_EVENT  Event,
  IN  VOID       *Context
  );

/**
  This function is called by all processors (both BSP and AP) once and collects
  MP related data.

  @param BSP            TRUE if the processor is the BSP.
  @param Mpidr          The MPIDR for the specified processor. This should be
                        the full MPIDR and not only the affinity bits.
  @param ProcessorIndex The index of the processor.

  @return EFI_SUCCESS if the data for the processor collected and filled in.

**/
STATIC
EFI_STATUS
FillInProcessorInformation (
  IN BOOLEAN  BSP,
  IN UINTN    Mpidr,
  IN UINTN    ProcessorIndex
  );

/**
  Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOOT is
  signaled. After this point, non-blocking mode is no longer allowed.

  @param  Event     Event whose notification function is being invoked.
  @param  Context   The pointer to the notification function's context,
                    which is implementation-dependent.

**/
STATIC
VOID
EFIAPI
ReadyToBootSignaled (
  IN  EFI_EVENT  Event,
  IN  VOID       *Context
  );

#endif /* MP_SERVICES_INTERNAL_H_ */
