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
>
>
>
>
>
>
next prev 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