public inbox for devel@edk2.groups.io
 help / color / mirror / Atom feed
From: "Rebecca Cran" <quic_rcran@quicinc.com>
To: Ard Biesheuvel <ardb@kernel.org>, <devel@edk2.groups.io>
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>
Subject: Re: [edk2-devel] [PATCH v2 1/2] ArmPkg: implement EFI_MP_SERVICES_PROTOCOL based on PSCI calls
Date: Wed, 21 Sep 2022 07:08:46 -0600	[thread overview]
Message-ID: <1b6947de-549b-f532-245f-05b39e8da663@quicinc.com> (raw)
In-Reply-To: <CAMj1kXEMFa9nuR2593yDpzGiPccfi1t5nR_SXVSe=_4nsQym1A@mail.gmail.com>

Thanks.
I've also noticed a problem with cache maintenance on the N2 FVP: it 
only works if I add manual cache flushes after enabling the MMU and 
caches (and also flush ApFunction to memory).
I'm working on debugging it.

-- 
Rebecca Cran

On 9/7/22 02:13, Ard Biesheuvel wrote:
> 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
>>
>>
>>
>> 
>>
>>


  reply	other threads:[~2022-09-21 13:09 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
2022-09-21 13:08     ` Rebecca Cran [this message]
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=1b6947de-549b-f532-245f-05b39e8da663@quicinc.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