public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Ard Biesheuvel" <ardb@kernel.org>
To: devel@edk2.groups.io, quic_rcran@quicinc.com
Cc: quic_llindhol@quicinc.com, Sami Mujawar <sami.mujawar@arm.com>,
	 Jian J Wang <jian.j.wang@intel.com>,
	Liming Gao <gaoliming@byosoft.com.cn>,
	 Rebecca Cran <rebecca@quicinc.com>
Subject: Re: [edk2-devel] [PATCH v2 1/2] ArmPkg: implement EFI_MP_SERVICES_PROTOCOL based on PSCI calls
Date: Wed, 7 Sep 2022 10:13:20 +0200	[thread overview]
Message-ID: <CAMj1kXEMFa9nuR2593yDpzGiPccfi1t5nR_SXVSe=_4nsQym1A@mail.gmail.com> (raw)
In-Reply-To: <20220907040326.388003-2-rebecca@quicinc.com>

On Wed, 7 Sept 2022 at 06:22, Rebecca Cran <quic_rcran@quicinc.com> wrote:
>
> Add support for EFI_MP_SERVICES_PROTOCOL during the DXE phase under
> AArch64.
>
> PSCI_CPU_ON is called to power on the core, the supplied procedure is
> executed and PSCI_CPU_OFF is called to power off the core.
>
> Fixes contributed by Ard Biesheuvel.
>
> Signed-off-by: Rebecca Cran <rebecca@quicinc.com>
> Reviewed-by: Ard Biesheuvel <ardb@kernel.org>

I spotted one issue below when double checking the cache maintenance situation;

> ---
>  ArmPkg/ArmPkg.dsc                                            |    1 +
>  ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf |   55 +
>  ArmPkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h     |  351 ++++
>  ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c   | 1774 ++++++++++++++++++++
>  ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S                |   57 +
>  5 files changed, 2238 insertions(+)
>
...
> diff --git a/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c b/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c
> new file mode 100644
> index 000000000000..8cea203c7e34
> --- /dev/null
> +++ b/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c
...
> +/** Initialize multi-processor support.
> +
> +  @param ImageHandle  Image handle.
> +  @param SystemTable  System table.
> +
> +  @return EFI_SUCCESS on success, or an error code.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ArmPsciMpServicesDxeInitialize (
> +  IN EFI_HANDLE        ImageHandle,
> +  IN EFI_SYSTEM_TABLE  *SystemTable
> +  )
> +{
> +  EFI_STATUS                 Status;
> +  EFI_HANDLE                 Handle;
> +  UINTN                      MaxCpus;
> +  EFI_LOADED_IMAGE_PROTOCOL  *Image;
> +  EFI_HOB_GENERIC_HEADER     *Hob;
> +  VOID                       *HobData;
> +  UINTN                      HobDataSize;
> +  CONST ARM_CORE_INFO        *CoreInfo;
> +
> +  MaxCpus = 1;
> +
> +  DEBUG ((DEBUG_INFO, "Starting MP services\n"));
> +
> +  Status = gBS->HandleProtocol (
> +                  ImageHandle,
> +                  &gEfiLoadedImageProtocolGuid,
> +                  (VOID **)&Image
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  //
> +  // Parts of the code in this driver may be executed by other cores running
> +  // with the MMU off so we need to ensure that everything is clean to the
> +  // point of coherency (PoC)
> +  //
> +  WriteBackDataCacheRange (Image->ImageBase, Image->ImageSize);
> +
> +  Status = gBS->HandleProtocol (
> +                  ImageHandle,
> +                  &gEfiLoadedImageProtocolGuid,
> +                  (VOID **)&Image
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +
> +  //
> +  // Parts of the code in this driver may be executed by other cores running
> +  // with the MMU off so we need to ensure that everything is clean to the
> +  // point of coherency (PoC)
> +  //
> +  WriteBackDataCacheRange (Image->ImageBase, Image->ImageSize);
> +

I don't think doing this twice serves any purpose :-)

> +  Hob = GetFirstGuidHob (&gArmMpCoreInfoGuid);
> +  if (Hob != NULL) {
> +    HobData     = GET_GUID_HOB_DATA (Hob);
> +    HobDataSize = GET_GUID_HOB_DATA_SIZE (Hob);
> +    CoreInfo    = (ARM_CORE_INFO *)HobData;
> +    MaxCpus     = HobDataSize / sizeof (ARM_CORE_INFO);
> +  }
> +
> +  if (MaxCpus == 1) {
> +    DEBUG ((DEBUG_WARN, "Trying to use EFI_MP_SERVICES_PROTOCOL on a UP system"));
> +    // We are not MP so nothing to do
> +    return EFI_NOT_FOUND;
> +  }
> +
> +  Status = MpServicesInitialize (MaxCpus, CoreInfo);
> +  if (Status != EFI_SUCCESS) {
> +    ASSERT_EFI_ERROR (Status);
> +    return Status;
> +  }
> +
> +  //
> +  // Now install the MP services protocol.
> +  //
> +  Handle = NULL;
> +  Status = gBS->InstallMultipleProtocolInterfaces (
> +                  &Handle,
> +                  &gEfiMpServiceProtocolGuid,
> +                  &mMpServicesProtocol,
> +                  NULL
> +                  );
> +  ASSERT_EFI_ERROR (Status);
> +  return Status;
> +}
> +
> +/** C entry-point for the AP.
> +    This function gets called from the assembly function ApEntryPoint.
> +
> +**/
> +VOID
> +ApProcedure (
> +  VOID
> +  )
> +{
> +  ARM_SMC_ARGS      Args;
> +  EFI_AP_PROCEDURE  UserApProcedure;
> +  VOID              *UserApParameter;
> +  UINTN             ProcessorIndex;
> +
> +  WhoAmI (&mMpServicesProtocol, &ProcessorIndex);
> +
> +  /* Fetch the user-supplied procedure and parameter to execute */
> +  UserApProcedure = mCpuMpData.CpuData[ProcessorIndex].Procedure;
> +  UserApParameter = mCpuMpData.CpuData[ProcessorIndex].Parameter;
> +
> +  // Configure the MMU and caches
> +  ArmSetTCR (mCpuMpData.CpuData[ProcessorIndex].Tcr);
> +  ArmSetTTBR0 (mCpuMpData.CpuData[ProcessorIndex].Ttbr0);
> +  ArmSetMAIR (mCpuMpData.CpuData[ProcessorIndex].Mair);
> +  ArmDisableAlignmentCheck ();
> +  ArmEnableStackAlignmentCheck ();
> +  ArmEnableInstructionCache ();
> +  ArmEnableDataCache ();
> +  ArmEnableMmu ();
> +
> +  UserApProcedure (UserApParameter);
> +
> +  mCpuMpData.CpuData[ProcessorIndex].State = CpuStateFinished;
> +
> +  ArmDataMemoryBarrier ();
> +
> +  /* Since we're finished with this AP, turn it off */
> +  Args.Arg0 = ARM_SMC_ID_PSCI_CPU_OFF;
> +  ArmCallSmc (&Args);
> +
> +  /* Should never be reached */
> +  ASSERT (FALSE);
> +  CpuDeadLoop ();
> +}
> +
> +/** Returns whether the processor executing this function is the BSP.
> +
> +    @return Whether the current processor is the BSP.
> +**/
> +STATIC
> +BOOLEAN
> +IsCurrentProcessorBSP (
> +  VOID
> +  )
> +{
> +  EFI_STATUS  Status;
> +  UINTN       ProcessorIndex;
> +
> +  Status = WhoAmI (&mMpServicesProtocol, &ProcessorIndex);
> +  if (EFI_ERROR (Status)) {
> +    ASSERT_EFI_ERROR (Status);
> +    return FALSE;
> +  }
> +
> +  return IsProcessorBSP (ProcessorIndex);
> +}
> +
> +/** 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
> +  )
> +{
> +  EFI_PROCESSOR_INFORMATION  *CpuInfo;
> +
> +  CpuInfo = &mCpuMpData.CpuData[ProcessorIndex].Info;
> +
> +  return (CpuInfo->StatusFlag & PROCESSOR_ENABLED_BIT) != 0;
> +}
> +
> +/** Returns whether all processors are in the idle state.
> +
> +   @return Whether all the processors are idle.
> +
> +**/
> +STATIC
> +BOOLEAN
> +CheckAllCpusReady (
> +  VOID
> +  )
> +{
> +  UINTN        Index;
> +  CPU_AP_DATA  *CpuData;
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    CpuData = &mCpuMpData.CpuData[Index];
> +    if (IsProcessorBSP (Index)) {
> +      // Skip BSP
> +      continue;
> +    }
> +
> +    if (!IsProcessorEnabled (Index)) {
> +      // Skip Disabled processors
> +      continue;
> +    }
> +
> +    if (GetApState (CpuData) != CpuStateIdle) {
> +      return FALSE;
> +    }
> +  }
> +
> +  return TRUE;
> +}
> +
> +/** Sets up the state for the StartupAllAPs function.
> +
> +   @param SingleThread Whether the APs will execute sequentially.
> +
> +**/
> +STATIC
> +VOID
> +StartupAllAPsPrepareState (
> +  IN BOOLEAN  SingleThread
> +  )
> +{
> +  UINTN        Index;
> +  CPU_STATE    APInitialState;
> +  CPU_AP_DATA  *CpuData;
> +
> +  mCpuMpData.FinishCount  = 0;
> +  mCpuMpData.StartCount   = 0;
> +  mCpuMpData.SingleThread = SingleThread;
> +
> +  APInitialState = CpuStateReady;
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    CpuData = &mCpuMpData.CpuData[Index];
> +
> +    //
> +    // Get APs prepared, and put failing APs into FailedCpuList.
> +    // If "SingleThread", only 1 AP will put into ready state, other AP will be
> +    // put into ready state 1 by 1, until the previous 1 finished its task.
> +    // If not "SingleThread", all APs are put into ready state from the
> +    // beginning
> +    //
> +
> +    if (IsProcessorBSP (Index)) {
> +      // Skip BSP
> +      continue;
> +    }
> +
> +    if (!IsProcessorEnabled (Index)) {
> +      // Skip Disabled processors
> +      if (mCpuMpData.FailedList != NULL) {
> +        mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] = Index;
> +      }
> +
> +      continue;
> +    }
> +
> +    ASSERT (GetApState (CpuData) == CpuStateIdle);
> +    CpuData->State = APInitialState;
> +
> +    mCpuMpData.StartCount++;
> +    if (SingleThread) {
> +      APInitialState = CpuStateBlocked;
> +    }
> +  }
> +}
> +
> +/** 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.
> +
> +   @return EFI_SUCCESS on success.
> +**/
> +STATIC
> +EFI_STATUS
> +StartupAllAPsWithWaitEvent (
> +  IN EFI_AP_PROCEDURE  Procedure,
> +  IN VOID              *ProcedureArgument,
> +  IN EFI_EVENT         WaitEvent,
> +  IN UINTN             TimeoutInMicroseconds
> +  )
> +{
> +  EFI_STATUS   Status;
> +  UINTN        Index;
> +  CPU_AP_DATA  *CpuData;
> +
> +  for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +    CpuData = &mCpuMpData.CpuData[Index];
> +    if (IsProcessorBSP (Index)) {
> +      // Skip BSP
> +      continue;
> +    }
> +
> +    if (!IsProcessorEnabled (Index)) {
> +      // Skip Disabled processors
> +      continue;
> +    }
> +
> +    if (GetApState (CpuData) == CpuStateReady) {
> +      SetApProcedure (CpuData, Procedure, ProcedureArgument);
> +    }
> +  }
> +
> +  //
> +  // Save data into private data structure, and create timer to poll AP state
> +  // before exiting
> +  //
> +  mCpuMpData.Procedure         = Procedure;
> +  mCpuMpData.ProcedureArgument = ProcedureArgument;
> +  mCpuMpData.WaitEvent         = WaitEvent;
> +  mCpuMpData.Timeout           = TimeoutInMicroseconds;
> +  mCpuMpData.TimeoutActive     = (BOOLEAN)(TimeoutInMicroseconds != 0);
> +  Status                       = gBS->SetTimer (
> +                                        mCpuMpData.CheckAllAPsEvent,
> +                                        TimerPeriodic,
> +                                        POLL_INTERVAL_US
> +                                        );
> +  return Status;
> +}
> +
> +/** 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
> +  )
> +{
> +  EFI_STATUS   Status;
> +  UINTN        Index;
> +  UINTN        NextIndex;
> +  UINTN        Timeout;
> +  CPU_AP_DATA  *CpuData;
> +
> +  Timeout = TimeoutInMicroseconds;
> +
> +  while (TRUE) {
> +    for (Index = 0; Index < mCpuMpData.NumberOfProcessors; Index++) {
> +      CpuData = &mCpuMpData.CpuData[Index];
> +      if (IsProcessorBSP (Index)) {
> +        // Skip BSP
> +        continue;
> +      }
> +
> +      if (!IsProcessorEnabled (Index)) {
> +        // Skip Disabled processors
> +        continue;
> +      }
> +
> +      switch (GetApState (CpuData)) {
> +        case CpuStateReady:
> +          SetApProcedure (CpuData, Procedure, ProcedureArgument);
> +          Status = DispatchCpu (Index);
> +          if (EFI_ERROR (Status)) {
> +            CpuData->State = CpuStateIdle;
> +            Status         = EFI_NOT_READY;
> +            goto Done;
> +          }
> +
> +          break;
> +
> +        case CpuStateFinished:
> +          mCpuMpData.FinishCount++;
> +          if (SingleThread) {
> +            Status = GetNextBlockedNumber (&NextIndex);
> +            if (!EFI_ERROR (Status)) {
> +              mCpuMpData.CpuData[NextIndex].State = CpuStateReady;
> +            }
> +          }
> +
> +          CpuData->State = CpuStateIdle;
> +          break;
> +
> +        default:
> +          break;
> +      }
> +    }
> +
> +    if (mCpuMpData.FinishCount == mCpuMpData.StartCount) {
> +      Status = EFI_SUCCESS;
> +      goto Done;
> +    }
> +
> +    if ((TimeoutInMicroseconds != 0) && (Timeout == 0)) {
> +      Status = EFI_TIMEOUT;
> +      goto Done;
> +    }
> +
> +    Timeout -= CalculateAndStallInterval (Timeout);
> +  }
> +
> +Done:
> +  if (FailedCpuList != NULL) {
> +    if (mCpuMpData.FailedListIndex == 0) {
> +      FreePool (*FailedCpuList);
> +      *FailedCpuList = NULL;
> +    }
> +  }
> +
> +  return Status;
> +}
> diff --git a/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S b/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S
> new file mode 100644
> index 000000000000..a90fa8a0075f
> --- /dev/null
> +++ b/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpFuncs.S
> @@ -0,0 +1,57 @@
> +#===============================================================================
> +#  Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
> +#
> +#  SPDX-License-Identifier: BSD-2-Clause-Patent
> +#===============================================================================
> +
> +.text
> +.align 3
> +
> +#include <AsmMacroIoLibV8.h>
> +#include <IndustryStandard/ArmStdSmc.h>
> +
> +#include "MpServicesInternal.h"
> +
> +GCC_ASM_IMPORT (gApStacksBase)
> +GCC_ASM_IMPORT (gProcessorIDs)
> +GCC_ASM_IMPORT (ApProcedure)
> +GCC_ASM_IMPORT (gApStackSize)
> +
> +GCC_ASM_EXPORT (ApEntryPoint)
> +
> +// Entry-point for the AP
> +// VOID
> +// ApEntryPoint (
> +//   VOID
> +//   );
> +ASM_PFX(ApEntryPoint):
> +  mrs x0, mpidr_el1
> +  // Mask the non-affinity bits
> +  bic x0, x0, 0x00ff000000
> +  and x0, x0, 0xffffffffff
> +  ldr x1, gProcessorIDs
> +  mov x2, 0                   // x2 = processor index
> +
> +// Find index in gProcessorIDs for current processor
> +1:
> +  ldr x3, [x1, x2, lsl #3]    // x4 = gProcessorIDs + x2 * 8
> +  cmp x3, #-1                 // check if we've reached the end of gProcessorIDs
> +  beq ProcessorNotFound
> +  add x2, x2, 1               // x2++
> +  cmp x0, x3                  // if mpidr_el1 != gProcessorIDs[x] then loop
> +  bne 1b
> +
> +// Calculate stack address
> +  // x2 contains the index for the current processor plus 1
> +  ldr x0, gApStacksBase
> +  ldr x1, gApStackSize
> +  mul x3, x2, x1              // x3 = (ProcessorIndex + 1) * gApStackSize
> +  add sp, x0, x3              // sp = gApStacksBase + x3
> +  mov x29, xzr
> +  bl ApProcedure              // doesn't return
> +
> +ProcessorNotFound:
> +// Turn off the processor
> +  MOV32 (w0, ARM_SMC_ID_PSCI_CPU_OFF)
> +  smc #0
> +  b .
> --
> 2.30.2
>
>
>
> 
>
>

  parent reply	other threads:[~2022-09-07  8:13 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-07  4:03 [PATCH v2 0/2] Add support EFI_MP_SERVICES_PROTOCOL on AARCH64 Rebecca Cran
2022-09-07  4:03 ` [PATCH v2 1/2] ArmPkg: implement EFI_MP_SERVICES_PROTOCOL based on PSCI calls Rebecca Cran
2022-09-07  4:25   ` Rebecca Cran
     [not found]   ` <1712796FB27A1CB6.17907@groups.io>
2022-09-07  4:35     ` [edk2-devel] " Rebecca Cran
2022-09-07  4:38       ` Michael Kubacki
2022-09-07  7:35       ` Ard Biesheuvel
2022-09-07 15:11         ` Michael Kubacki
2022-09-07 15:21           ` Ard Biesheuvel
2022-09-07 15:32             ` Michael Kubacki
2022-09-07  8:13   ` Ard Biesheuvel [this message]
2022-09-21 13:08     ` Rebecca Cran
2022-09-07  4:03 ` [PATCH v2 2/2] MdeModulePkg: Add new Application/MpServicesTest application Rebecca Cran

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAMj1kXEMFa9nuR2593yDpzGiPccfi1t5nR_SXVSe=_4nsQym1A@mail.gmail.com' \
    --to=devel@edk2.groups.io \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox