From nobody Tue May 14 06:52:18 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+93329+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+93329+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=quicinc.com ARC-Seal: i=1; a=rsa-sha256; t=1662524525; cv=none; d=zohomail.com; s=zohoarc; b=Kwwq4/kENSWVL3t2268S7UNmW2lfR2bgANX2g7fGS8mgaQ+vIRmGm5USxPuQucfLk/CaQAIpTxSADZvgzArDmAkU/ybP40+wJcbBOgvbafg+aSugg4TvKx6D3XYyFN4eDaShNWv1m1RxYCyYnkmJzmbT+HMTRz0JgbB8l3Qz4uY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1662524525; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=eXvJrA2mPP/cpvmsEXvmwgcaLn1+xY3YOLV/CZp5C2s=; b=PeC0r1QnbCWGzU+hgen55Zv8bOgOTcihoUJ38dyZoVw0QpA22glm90DVG3FP0nrOiGQU1Ic6nnMrQH/RgXlJSU3i+vE06orPGFxFptUE+2d3laeFSL4ly4ExxwYy1W8dSMJJx6Pl61BcArth3v1dRubssTVImPdIzYZmEc2FX2c= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+93329+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 1662524525644106.7641827302416; Tue, 6 Sep 2022 21:22:05 -0700 (PDT) Return-Path: X-Received: by 127.0.0.2 with SMTP id w9GXYY1788612xSLS4aAHq0R; Tue, 06 Sep 2022 21:22:05 -0700 X-Received: from mx0b-0031df01.pphosted.com (mx0b-0031df01.pphosted.com [205.220.180.131]) by mx.groups.io with SMTP id smtpd.web09.4046.1662524523961291226 for ; Tue, 06 Sep 2022 21:22:04 -0700 X-Received: from pps.filterd (m0279868.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.5/8.17.1.5) with ESMTP id 287470F6006565; Wed, 7 Sep 2022 04:21:52 GMT X-Received: from nalasppmta01.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3jefarrj4w-3 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 07 Sep 2022 04:21:52 +0000 X-Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA01.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 28743mSr024548 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 7 Sep 2022 04:03:48 GMT X-Received: from linbox.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.29; Tue, 6 Sep 2022 21:03:47 -0700 From: "Rebecca Cran" To: , , , Sami Mujawar , Jian J Wang , Liming Gao CC: Rebecca Cran Subject: [edk2-devel] [PATCH v2 1/2] ArmPkg: implement EFI_MP_SERVICES_PROTOCOL based on PSCI calls Date: Tue, 6 Sep 2022 22:03:25 -0600 Message-ID: <20220907040326.388003-2-rebecca@quicinc.com> In-Reply-To: <20220907040326.388003-1-rebecca@quicinc.com> References: <20220907040326.388003-1-rebecca@quicinc.com> MIME-Version: 1.0 X-Originating-IP: [10.80.80.8] X-ClientProxiedBy: nasanex01a.na.qualcomm.com (10.52.223.231) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-GUID: JVbP5X0ywKAsPtVixGb8GYHG-8I2JPKe X-Proofpoint-ORIG-GUID: JVbP5X0ywKAsPtVixGb8GYHG-8I2JPKe Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,quic_rcran@quicinc.com X-Gm-Message-State: 5giY0xNLH7ks6q940UA2MEUtx1787277AA= Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1662524525; bh=zl2gVKJf97SH2Yv20loODDkQlzmtn9cxdUazXIYrjuc=; h=CC:Content-Type:Date:From:Reply-To:Subject:To; b=ddk2oVQK9kl6KNYsdDHLlHRccdA+n2r5SC7LK5ODHDxburLwQ7sqQnMyxgQhhXC6W78 GW95OflLzqUvyfGSqvs4FEHJkq3YpyXrFIJGECUpL8fDF0PYrrjQMmwdiQWXSCkeFZoxi ZmO9wvdJXp0RBcHOaPViDFxIEiti2wL3gSE= X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1662524526595100001 Content-Type: text/plain; charset="utf-8" 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 Reviewed-by: Ard Biesheuvel --- 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/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc index 59fd8f295d4f..4716789402fc 100644 --- a/ArmPkg/ArmPkg.dsc +++ b/ArmPkg/ArmPkg.dsc @@ -125,6 +125,7 @@ [Components.common] ArmPkg/Drivers/CpuPei/CpuPei.inf ArmPkg/Drivers/ArmGic/ArmGicDxe.inf ArmPkg/Drivers/ArmGic/ArmGicLib.inf + ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf ArmPkg/Drivers/TimerDxe/TimerDxe.inf =20 diff --git a/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf b= /ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf new file mode 100644 index 000000000000..2b109c72e0a0 --- /dev/null +++ b/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.inf @@ -0,0 +1,55 @@ +## @file +# ARM MP services protocol driver +# +# Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserve= d.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 1.27 + BASE_NAME =3D ArmPsciMpServicesDxe + FILE_GUID =3D 007ab472-dc4a-4df8-a5c2-abb4a327278c + MODULE_TYPE =3D DXE_DRIVER + VERSION_STRING =3D 1.0 + + ENTRY_POINT =3D ArmPsciMpServicesDxeInitialize + +[Sources.Common] + ArmPsciMpServicesDxe.c + MpFuncs.S + MpServicesInternal.h + +[Packages] + ArmPkg/ArmPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + ArmLib + ArmMmuLib + ArmSmcLib + BaseMemoryLib + CacheMaintenanceLib + DebugLib + HobLib + MemoryAllocationLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEfiMpServiceProtocolGuid ## PRODUCES + gEfiLoadedImageProtocolGuid ## CONSUMES + +[Guids] + gArmMpCoreInfoGuid + +[Depex] + TRUE + +[BuildOptions] + GCC:*_*_*_CC_FLAGS =3D -mstrict-align diff --git a/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h b/Arm= Pkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h new file mode 100644 index 000000000000..ee13816ef64c --- /dev/null +++ b/ArmPkg/Drivers/ArmPsciMpServicesDxe/MpServicesInternal.h @@ -0,0 +1,351 @@ +/** @file + +Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.<= BR> +Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.
+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 +#include + +#include +#include + +#define AP_STACK_SIZE 0x1000 + +// +// Internal Data Structures +// + +// +// AP state +// +// The state transitions for an AP when it process a procedure are: +// Idle ----> Ready ----> Busy ----> Idle +// [BSP] [AP] [AP] +// +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; + UINTN Timeout; + UINTN TimeTaken; + VOID *Ttbr0; + UINTN Tcr; + UINTN Mair; +} CPU_AP_DATA; + +// +// Define MP data block which consumes individual processor block. +// +typedef struct { + UINTN NumberOfProcessors; + UINTN NumberOfEnabledProcessors; + EFI_EVENT CheckAllAPsEvent; + EFI_EVENT WaitEvent; + UINTN FinishCount; + UINTN StartCount; + EFI_AP_PROCEDURE Procedure; + VOID *ProcedureArgument; + BOOLEAN SingleThread; + UINTN StartedNumber; + CPU_AP_DATA *CpuData; + UINTN Timeout; + UINTN *FailedList; + UINTN FailedListIndex; + BOOLEAN TimeoutActive; +} 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 + ); + +/** Returns whether all processors are in the idle state. + + @return Whether all the processors are idle. + +**/ +STATIC +BOOLEAN +CheckAllCpusReady ( + VOID + ); + +/** 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. + + @return EFI_SUCCESS on success. +**/ +STATIC +EFI_STATUS +StartupAllAPsWithWaitEvent ( + IN EFI_AP_PROCEDURE Procedure, + IN VOID *ProcedureArgument, + IN EFI_EVENT WaitEvent, + IN UINTN TimeoutInMicroseconds + ); + +/** Handles execution of StartupAllAPs when no wait event has been specifi= ed. + + @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 C= PUs. + + @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 invo= kes + 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 col= lects + 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 i= n. + +**/ +STATIC +EFI_STATUS +FillInProcessorInformation ( + IN BOOLEAN BSP, + IN UINTN Mpidr, + IN UINTN ProcessorIndex + ); + +/** + Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOO= T 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_ */ diff --git a/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c b/A= rmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c new file mode 100644 index 000000000000..8cea203c7e34 --- /dev/null +++ b/ArmPkg/Drivers/ArmPsciMpServicesDxe/ArmPsciMpServicesDxe.c @@ -0,0 +1,1774 @@ +/** @file + Construct MP Services Protocol. + + The MP Services Protocol provides a generalized way of performing follow= ing tasks: + - Retrieving information of multi-processor environment and MP-related= status of + specific processors. + - Dispatching user-provided function to APs. + - Maintain MP-related processor status. + + The MP Services Protocol must be produced on any system with more than o= ne logical + processor. + + The Protocol is available only during boot time. + + MP Services Protocol is hardware-independent. Most of the logic of this = protocol + is architecturally neutral. It abstracts the multi-processor environment= and + status of processors, and provides interfaces to retrieve information, m= aintain, + and dispatch. + + MP Services Protocol may be consumed by ACPI module. The ACPI module may= use this + protocol to retrieve data that are needed for an MP platform and report = them to OS. + MP Services Protocol may also be used to program and configure processor= s, such + as MTRR synchronization for memory space attributes setting in DXE Servi= ces. + MP Services Protocol may be used by non-CPU DXE drivers to speed up plat= form boot + by taking advantage of the processing capabilities of the APs, for examp= le, using + APs to help test system memory in parallel with other device initializat= ion. + Diagnostics applications may also use this protocol for multi-processor. + + Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved= .
+ SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "MpServicesInternal.h" + +#define POLL_INTERVAL_US 50000 + +#define GET_MPIDR_AFFINITY_BITS(x) ((x) & 0xFF00FFFFFF) + +#define MPIDR_MT_BIT BIT24 + +STATIC CPU_MP_DATA mCpuMpData; +STATIC BOOLEAN mNonBlockingModeAllowed; +UINT64 *gApStacksBase; +UINT64 *gProcessorIDs; +CONST UINT64 gApStackSize =3D AP_STACK_SIZE; + +STATIC +BOOLEAN +IsCurrentProcessorBSP ( + 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 Success. + @retval EFI_DEVICE_ERROR The processor could not be turned on. + +**/ +STATIC +EFI_STATUS +EFIAPI +DispatchCpu ( + IN UINTN ProcessorIndex + ) +{ + ARM_SMC_ARGS Args; + EFI_STATUS Status; + + Status =3D EFI_SUCCESS; + + mCpuMpData.CpuData[ProcessorIndex].State =3D CpuStateBusy; + + /* Turn the AP on */ + if (sizeof (Args.Arg0) =3D=3D sizeof (UINT32)) { + Args.Arg0 =3D ARM_SMC_ID_PSCI_CPU_ON_AARCH32; + } else { + Args.Arg0 =3D ARM_SMC_ID_PSCI_CPU_ON_AARCH64; + } + + Args.Arg1 =3D gProcessorIDs[ProcessorIndex]; + Args.Arg2 =3D (UINTN)ApEntryPoint; + + mCpuMpData.CpuData[ProcessorIndex].Tcr =3D ArmGetTCR (); + mCpuMpData.CpuData[ProcessorIndex].Mair =3D ArmGetMAIR (); + mCpuMpData.CpuData[ProcessorIndex].Ttbr0 =3D ArmGetTTBR0BaseAddress (); + WriteBackDataCacheRange (&mCpuMpData.CpuData[ProcessorIndex], sizeof (CP= U_AP_DATA)); + + ArmCallSmc (&Args); + + if (Args.Arg0 !=3D ARM_SMC_PSCI_RET_SUCCESS) { + DEBUG ((DEBUG_ERROR, "PSCI_CPU_ON call failed: %d\n", Args.Arg0)); + Status =3D EFI_DEVICE_ERROR; + } + + return Status; +} + +/** 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 + ) +{ + EFI_PROCESSOR_INFORMATION *CpuInfo; + + CpuInfo =3D &mCpuMpData.CpuData[ProcessorIndex].Info; + + return (CpuInfo->StatusFlag & PROCESSOR_AS_BSP_BIT) !=3D 0; +} + +/** 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 + ) +{ + return CpuData->State; +} + +/** 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 + ) +{ + ASSERT (CpuData !=3D NULL); + ASSERT (Procedure !=3D NULL); + + CpuData->Parameter =3D ProcedureArgument; + CpuData->Procedure =3D Procedure; +} + +/** 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 + ) +{ + UINTN Index; + CPU_STATE State; + CPU_AP_DATA *CpuData; + + for (Index =3D 0; Index < mCpuMpData.NumberOfProcessors; Index++) { + CpuData =3D &mCpuMpData.CpuData[Index]; + if (IsProcessorBSP (Index)) { + // Skip BSP + continue; + } + + State =3D CpuData->State; + + if (State =3D=3D CpuStateBlocked) { + *NextNumber =3D Index; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** Stalls the BSP for the minimum of POLL_INTERVAL_US 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 + ) +{ + UINTN StallTime; + + if ((Timeout < POLL_INTERVAL_US) && (Timeout !=3D 0)) { + StallTime =3D Timeout; + } else { + StallTime =3D POLL_INTERVAL_US; + } + + gBS->Stall (StallTime); + + return StallTime; +} + +/** + This service retrieves the number of logical processor in the platform + and the number of those logical processors that are enabled on this boot. + This service may only be called from the BSP. + + This function is used to retrieve the following information: + - The number of logical processors that are present in the system. + - The number of enabled logical processors in the system at the instant + this call is made. + + Because MP Service Protocol provides services to enable and disable proc= essors + dynamically, the number of enabled logical processors may vary during the + course of a boot session. + + If this service is called from an AP, then EFI_DEVICE_ERROR is returned. + If NumberOfProcessors or NumberOfEnabledProcessors is NULL, then + EFI_INVALID_PARAMETER is returned. Otherwise, the total number of proces= sors + is returned in NumberOfProcessors, the number of currently enabled proce= ssor + is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the + EFI_MP_SERVICES_PROTOCOL instanc= e. + @param[out] NumberOfProcessors Pointer to the total number of l= ogical + processors in the system, includ= ing + the BSP and disabled APs. + @param[out] NumberOfEnabledProcessors Pointer to the number of enabled + logical processors that exist in= the + system, including the BSP. + + @retval EFI_SUCCESS The number of logical processors and ena= bled + logical processors was retrieved. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER NumberOfProcessors is NULL. + @retval EFI_INVALID_PARAMETER NumberOfEnabledProcessors is NULL. + +**/ +STATIC +EFI_STATUS +EFIAPI +GetNumberOfProcessors ( + IN EFI_MP_SERVICES_PROTOCOL *This, + OUT UINTN *NumberOfProcessors, + OUT UINTN *NumberOfEnabledProcessors + ) +{ + if ((NumberOfProcessors =3D=3D NULL) || (NumberOfEnabledProcessors =3D= =3D NULL)) { + return EFI_INVALID_PARAMETER; + } + + if (!IsCurrentProcessorBSP ()) { + return EFI_DEVICE_ERROR; + } + + *NumberOfProcessors =3D mCpuMpData.NumberOfProcessors; + *NumberOfEnabledProcessors =3D mCpuMpData.NumberOfEnabledProcessors; + return EFI_SUCCESS; +} + +/** + Gets detailed MP-related information on the requested processor at the + instant this call is made. This service may only be called from the BSP. + + This service retrieves detailed MP-related information about any process= or + on the platform. Note the following: + - The processor information may change during the course of a boot ses= sion. + - The information presented here is entirely MP related. + + Information regarding the number of caches and their sizes, frequency of + operation, slot numbers is all considered platform-related information a= nd is + not provided by this service. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTO= COL + instance. + @param[in] ProcessorIndex The index of the processor. + @param[out] ProcessorInfoBuffer A pointer to the buffer where informat= ion + for the requested processor is deposit= ed. + + @retval EFI_SUCCESS Processor information was returned. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_INVALID_PARAMETER ProcessorInfoBuffer is NULL. + @retval EFI_NOT_FOUND The processor with the handle specified = by + ProcessorNumber does not exist in the pl= atform. + +**/ +STATIC +EFI_STATUS +EFIAPI +GetProcessorInfo ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorIndex, + OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer + ) +{ + if (ProcessorInfoBuffer =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if (!IsCurrentProcessorBSP ()) { + return EFI_DEVICE_ERROR; + } + + ProcessorIndex &=3D ~CPU_V2_EXTENDED_TOPOLOGY; + + if (ProcessorIndex >=3D mCpuMpData.NumberOfProcessors) { + return EFI_NOT_FOUND; + } + + CopyMem ( + ProcessorInfoBuffer, + &mCpuMpData.CpuData[ProcessorIndex], + sizeof (EFI_PROCESSOR_INFORMATION) + ); + return EFI_SUCCESS; +} + +/** + This service executes a caller provided function on all enabled APs. APs= can + run either simultaneously or one at a time in sequence. This service sup= ports + both blocking and non-blocking requests. The non-blocking requests use E= FI + events so the BSP can detect when the APs have finished. This service ma= y only + be called from the BSP. + + This function is used to dispatch all the enabled APs to the function + specified by Procedure. If any enabled AP is busy, then EFI_NOT_READY is + returned immediately and Procedure is not started on any AP. + + If SingleThread is TRUE, all the enabled APs execute the function specif= ied by + Procedure one by one, in ascending order of processor handle number. + Otherwise, all the enabled APs execute the function specified by Procedu= re + simultaneously. + + If WaitEvent is NULL, execution is in blocking mode. The BSP waits until= all + APs finish or TimeoutInMicroseconds expires. Otherwise, execution is in + non-blocking mode, and the BSP returns from this service without waiting= for + APs. If a non-blocking mode is requested after the UEFI Event + EFI_EVENT_GROUP_READY_TO_BOOT is signaled, then EFI_UNSUPPORTED must be + returned. + + If the timeout specified by TimeoutInMicroseconds expires before all APs + return from Procedure, then Procedure on the failed APs is terminated. + All enabled APs are always available for further calls to + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() and + EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). If FailedCpuList is not NULL, = its + content points to the list of processor handle numbers in which Procedur= e was + terminated. + + Note: It is the responsibility of the consumer of the + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() to make sure that the nature of= the + code that is executed on the BSP and the dispatched APs is well controll= ed. + The MP Services Protocol does not guarantee that the Procedure function = is + MP-safe. Hence, the tasks that can be run in parallel are limited to cer= tain + independent tasks and well-controlled exclusive code. EFI services and + protocols may not be called by APs unless otherwise specified. + + In blocking execution mode, BSP waits until all APs finish or + TimeoutInMicroseconds expires. + + In non-blocking execution mode, BSP is freed to return to the caller and= then + proceed to the next task without having to wait for APs. The following + sequence needs to occur in a non-blocking execution mode: + + -# The caller that intends to use this MP Services Protocol in non-blo= cking + mode creates WaitEvent by calling the EFI CreateEvent() service. T= he + caller invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the par= ameter + WaitEvent is not NULL, then StartupAllAPs() executes in non-blocking + mode. It requests the function specified by Procedure to be started= on + all the enabled APs, and releases the BSP to continue with other ta= sks. + -# The caller can use the CheckEvent() and WaitForEvent() services to = check + the state of the WaitEvent created in step 1. + -# When the APs complete their task or TimeoutInMicroSecondss expires,= the + MP Service signals WaitEvent by calling the EFI SignalEvent() funct= ion. + If FailedCpuList is not NULL, its content is available when WaitEve= nt is + signaled. If all APs returned from Procedure prior to the timeout, = then + FailedCpuList is set to NULL. If not all APs return from Procedure = before + the timeout, then FailedCpuList is filled in with the list of the f= ailed + APs. The buffer is allocated by MP Service Protocol using AllocateP= ool(). + It is the caller's responsibility to free the buffer with FreePool() + service. + -# This invocation of SignalEvent() function informs the caller that i= nvoked + EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() that either all the APs + completed the specified task or a timeout occurred. The contents of + FailedCpuList can be examined to determine which APs did not comple= te the + specified task prior to the timeout. + + @param[in] This A pointer to the EFI_MP_SERVICES_PRO= TOCOL + instance. + @param[in] Procedure A pointer to the function to be run = on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] SingleThread If TRUE, then all the enabled APs ex= ecute + the function specified by Procedure = one by + one, in ascending order of processor + handle number. If FALSE, then all t= he + enabled APs execute the function spe= cified + by Procedure simultaneously. + @param[in] WaitEvent The event created by the caller with + CreateEvent() service. If it is NUL= L, + then execute in blocking mode. BSP w= aits + until all APs finish or + TimeoutInMicroseconds expires. If i= t's + not NULL, then execute in non-blocki= ng + mode. BSP requests the function spec= ified + by Procedure to be started on all the + enabled APs, and go on executing + immediately. If all return from Proc= edure, + or TimeoutInMicroseconds expires, th= is + event is signaled. The BSP can use t= he + CheckEvent() or WaitForEvent() + services to check the state of event= . Type + EFI_EVENT is defined in CreateEvent(= ) in + the Unified Extensible Firmware Inte= rface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microsec= onds + for APs to return from Procedure, ei= ther + for blocking or non-blocking mode. Z= ero + means infinity. If the timeout expi= res + before all APs return from Procedure= , then + Procedure on the failed APs is termi= nated. + All enabled APs are available for ne= xt + function assigned by + EFI_MP_SERVICES_PROTOCOL.StartupAllA= Ps() + or EFI_MP_SERVICES_PROTOCOL.StartupT= hisAP(). + If the timeout expires in blocking m= ode, + BSP returns EFI_TIMEOUT. If the tim= eout + expires in non-blocking mode, WaitEv= ent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure = for + all APs. + @param[out] FailedCpuList If NULL, this parameter is ignored. + Otherwise, if all APs finish success= fully, + then its content is set to NULL. If = not + all APs finish before timeout expire= s, + then its content is set to address o= f the + buffer holding handle numbers of the + failed APs. + The buffer is allocated by MP Service + Protocol, and it's the caller's + responsibility to free the buffer wi= th + FreePool() service. + In blocking mode, it is ready for + consumption when the call returns. In + non-blocking mode, it is ready when + WaitEvent is signaled. The list of f= ailed + CPU is terminated by END_OF_CPU_LIS= T. + + @retval EFI_SUCCESS In blocking mode, all APs have finished = before + the timeout expired. + @retval EFI_SUCCESS In non-blocking mode, function has been + dispatched to all enabled APs. + @retval EFI_UNSUPPORTED A non-blocking mode request was made aft= er the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT= was + signaled. + @retval EFI_DEVICE_ERROR Caller processor is AP. + @retval EFI_NOT_STARTED No enabled APs exist in the system. + @retval EFI_NOT_READY Any enabled APs are busy. + @retval EFI_TIMEOUT In blocking mode, the timeout expired be= fore + all enabled APs have finished. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +STATIC +EFI_STATUS +EFIAPI +StartupAllAPs ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN BOOLEAN SingleThread, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT UINTN **FailedCpuList OPTIONAL + ) +{ + EFI_STATUS Status; + + if (!IsCurrentProcessorBSP ()) { + return EFI_DEVICE_ERROR; + } + + if (mCpuMpData.NumberOfProcessors =3D=3D 1) { + return EFI_NOT_STARTED; + } + + if (Procedure =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if ((WaitEvent !=3D NULL) && !mNonBlockingModeAllowed) { + return EFI_UNSUPPORTED; + } + + if (!CheckAllCpusReady ()) { + return EFI_NOT_READY; + } + + if (FailedCpuList !=3D NULL) { + mCpuMpData.FailedList =3D AllocatePool ( + (mCpuMpData.NumberOfProcessors + 1) * + sizeof (UINTN) + ); + if (mCpuMpData.FailedList =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + SetMemN ( + mCpuMpData.FailedList, + (mCpuMpData.NumberOfProcessors + 1) * + sizeof (UINTN), + END_OF_CPU_LIST + ); + mCpuMpData.FailedListIndex =3D 0; + *FailedCpuList =3D mCpuMpData.FailedList; + } + + StartupAllAPsPrepareState (SingleThread); + + if (WaitEvent !=3D NULL) { + Status =3D StartupAllAPsWithWaitEvent ( + Procedure, + ProcedureArgument, + WaitEvent, + TimeoutInMicroseconds + ); + } else { + Status =3D StartupAllAPsNoWaitEvent ( + Procedure, + ProcedureArgument, + TimeoutInMicroseconds, + SingleThread, + FailedCpuList + ); + } + + return Status; +} + +/** + This service lets the caller get one enabled AP to execute a caller-prov= ided + function. The caller can request the BSP to either wait for the completi= on + of the AP or just proceed with the next task by using the EFI event mech= anism. + See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blo= cking + execution support. This service may only be called from the BSP. + + This function is used to dispatch one enabled AP to the function specifi= ed by + Procedure passing in the argument specified by ProcedureArgument. If Wa= itEvent + is NULL, execution is in blocking mode. The BSP waits until the AP finis= hes or + TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking = mode. + BSP proceeds to the next task without waiting for the AP. If a non-block= ing mode + is requested after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signa= led, + then EFI_UNSUPPORTED must be returned. + + If the timeout specified by TimeoutInMicroseconds expires before the AP = returns + from Procedure, then execution of Procedure by the AP is terminated. The= AP is + available for subsequent calls to EFI_MP_SERVICES_PROTOCOL.StartupAllAPs= () and + EFI_MP_SERVICES_PROTOCOL.StartupThisAP(). + + @param[in] This A pointer to the EFI_MP_SERVICES_PRO= TOCOL + instance. + @param[in] Procedure A pointer to the function to be run = on + enabled APs of the system. See type + EFI_AP_PROCEDURE. + @param[in] ProcessorNumber The handle number of the AP. The ran= ge is + from 0 to the total number of logical + processors minus 1. The total number= of + logical processors can be retrieved = by + EFI_MP_SERVICES_PROTOCOL.GetNumberOf= Processors(). + @param[in] WaitEvent The event created by the caller with= CreateEvent() + service. If it is NULL, then execut= e in + blocking mode. BSP waits until all A= Ps finish + or TimeoutInMicroseconds expires. I= f it's + not NULL, then execute in non-blocki= ng mode. + BSP requests the function specified = by + Procedure to be started on all the e= nabled + APs, and go on executing immediately= . If + all return from Procedure or Timeout= InMicroseconds + expires, this event is signaled. The= BSP + can use the CheckEvent() or WaitForE= vent() + services to check the state of event= . Type + EFI_EVENT is defined in CreateEvent(= ) in + the Unified Extensible Firmware Inte= rface + Specification. + @param[in] TimeoutInMicroseconds Indicates the time limit in microsec= onds for + APs to return from Procedure, either= for + blocking or non-blocking mode. Zero = means + infinity. If the timeout expires be= fore + all APs return from Procedure, then = Procedure + on the failed APs is terminated. All= enabled + APs are available for next function = assigned + by EFI_MP_SERVICES_PROTOCOL.StartupA= llAPs() + or EFI_MP_SERVICES_PROTOCOL.StartupT= hisAP(). + If the timeout expires in blocking m= ode, + BSP returns EFI_TIMEOUT. If the tim= eout + expires in non-blocking mode, WaitEv= ent + is signaled with SignalEvent(). + @param[in] ProcedureArgument The parameter passed into Procedure = for + all APs. + @param[out] Finished If NULL, this parameter is ignored. = In + blocking mode, this parameter is ign= ored. + In non-blocking mode, if AP returns = from + Procedure before the timeout expires= , its + content is set to TRUE. Otherwise, t= he + value is set to FALSE. The caller can + determine if the AP returned from Pr= ocedure + by evaluating this value. + + @retval EFI_SUCCESS In blocking mode, specified AP finished = before + the timeout expires. + @retval EFI_SUCCESS In non-blocking mode, the function has b= een + dispatched to specified AP. + @retval EFI_UNSUPPORTED A non-blocking mode request was made aft= er the + UEFI event EFI_EVENT_GROUP_READY_TO_BOOT= was + signaled. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_TIMEOUT In blocking mode, the timeout expired be= fore + the specified AP has finished. + @retval EFI_NOT_READY The specified AP is busy. + @retval EFI_NOT_FOUND The processor with the handle specified = by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP or dis= abled AP. + @retval EFI_INVALID_PARAMETER Procedure is NULL. + +**/ +STATIC +EFI_STATUS +EFIAPI +StartupThisAP ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN EFI_AP_PROCEDURE Procedure, + IN UINTN ProcessorNumber, + IN EFI_EVENT WaitEvent OPTIONAL, + IN UINTN TimeoutInMicroseconds, + IN VOID *ProcedureArgument OPTIONAL, + OUT BOOLEAN *Finished OPTIONAL + ) +{ + EFI_STATUS Status; + UINTN Timeout; + CPU_AP_DATA *CpuData; + + if (!IsCurrentProcessorBSP ()) { + return EFI_DEVICE_ERROR; + } + + if (Procedure =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + if (ProcessorNumber >=3D mCpuMpData.NumberOfProcessors) { + return EFI_NOT_FOUND; + } + + CpuData =3D &mCpuMpData.CpuData[ProcessorNumber]; + + if (IsProcessorBSP (ProcessorNumber)) { + return EFI_INVALID_PARAMETER; + } + + if (!IsProcessorEnabled (ProcessorNumber)) { + return EFI_INVALID_PARAMETER; + } + + if (GetApState (CpuData) !=3D CpuStateIdle) { + return EFI_NOT_READY; + } + + if ((WaitEvent !=3D NULL) && !mNonBlockingModeAllowed) { + return EFI_UNSUPPORTED; + } + + Timeout =3D TimeoutInMicroseconds; + + mCpuMpData.StartCount =3D 1; + mCpuMpData.FinishCount =3D 0; + + SetApProcedure ( + CpuData, + Procedure, + ProcedureArgument + ); + + Status =3D DispatchCpu (ProcessorNumber); + if (EFI_ERROR (Status)) { + CpuData->State =3D CpuStateIdle; + return EFI_NOT_READY; + } + + if (WaitEvent !=3D NULL) { + // Non Blocking + mCpuMpData.WaitEvent =3D WaitEvent; + gBS->SetTimer ( + CpuData->CheckThisAPEvent, + TimerPeriodic, + POLL_INTERVAL_US + ); + return EFI_SUCCESS; + } + + // Blocking + while (TRUE) { + if (GetApState (CpuData) =3D=3D CpuStateFinished) { + CpuData->State =3D CpuStateIdle; + break; + } + + if ((TimeoutInMicroseconds !=3D 0) && (Timeout =3D=3D 0)) { + return EFI_TIMEOUT; + } + + Timeout -=3D CalculateAndStallInterval (Timeout); + } + + return EFI_SUCCESS; +} + +/** + This service switches the requested AP to be the BSP from that point onw= ard. + This service changes the BSP for all purposes. This call can only be + performed by the current BSP. + + This service switches the requested AP to be the BSP from that point onw= ard. + This service changes the BSP for all purposes. The new BSP can take over= the + execution of the old BSP and continue seamlessly from where the old one = left + off. This service may not be supported after the UEFI Event EFI_EVENT_GR= OUP_READY_TO_BOOT + is signaled. + + If the BSP cannot be switched prior to the return from this service, then + EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL i= nstance. + @param[in] ProcessorNumber The handle number of AP that is to become t= he new + BSP. The range is from 0 to the total numbe= r of + logical processors minus 1. The total numbe= r of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcess= ors(). + @param[in] EnableOldBSP If TRUE, then the old BSP will be listed as= an + enabled AP. Otherwise, it will be disabled. + + @retval EFI_SUCCESS BSP successfully switched. + @retval EFI_UNSUPPORTED Switching the BSP cannot be completed pr= ior to + this service returning. + @retval EFI_UNSUPPORTED Switching the BSP is not supported. + @retval EFI_SUCCESS The calling processor is an AP. + @retval EFI_NOT_FOUND The processor with the handle specified = by + ProcessorNumber does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the current BS= P or + a disabled AP. + @retval EFI_NOT_READY The specified AP is busy. + +**/ +STATIC +EFI_STATUS +EFIAPI +SwitchBSP ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableOldBSP + ) +{ + return EFI_UNSUPPORTED; +} + +/** + This service lets the caller enable or disable an AP from this point onw= ard. + This service may only be called from the BSP. + + This service allows the caller enable or disable an AP from this point o= nward. + The caller can optionally specify the health status of the AP by Health.= If + an AP is being disabled, then the state of the disabled AP is implementa= tion + dependent. If an AP is enabled, then the implementation must guarantee t= hat a + complete initialization sequence is performed on the AP, so the AP is in= a state + that is compatible with an MP operating system. This service may not be = supported + after the UEFI Event EFI_EVENT_GROUP_READY_TO_BOOT is signaled. + + If the enable or disable AP operation cannot be completed prior to the r= eturn + from this service, then EFI_UNSUPPORTED must be returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL i= nstance. + @param[in] ProcessorNumber The handle number of AP that is to become t= he new + BSP. The range is from 0 to the total numbe= r of + logical processors minus 1. The total numbe= r of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcess= ors(). + @param[in] EnableAP Specifies the new state for the processor f= or + enabled, FALSE for disabled. + @param[in] HealthFlag If not NULL, a pointer to a value that spec= ifies + the new health status of the AP. This flag + corresponds to StatusFlag defined in + EFI_MP_SERVICES_PROTOCOL.GetProcessorInfo()= . Only + the PROCESSOR_HEALTH_STATUS_BIT is used. Al= l other + bits are ignored. If it is NULL, this para= meter + is ignored. + + @retval EFI_SUCCESS The specified AP was enabled or disabled= successfully. + @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be co= mpleted + prior to this service returning. + @retval EFI_UNSUPPORTED Enabling or disabling an AP is not suppo= rted. + @retval EFI_DEVICE_ERROR The calling processor is an AP. + @retval EFI_NOT_FOUND Processor with the handle specified by P= rocessorNumber + does not exist. + @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP. + +**/ +STATIC +EFI_STATUS +EFIAPI +EnableDisableAP ( + IN EFI_MP_SERVICES_PROTOCOL *This, + IN UINTN ProcessorNumber, + IN BOOLEAN EnableAP, + IN UINT32 *HealthFlag OPTIONAL + ) +{ + UINTN StatusFlag; + CPU_AP_DATA *CpuData; + + StatusFlag =3D mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag; + CpuData =3D &mCpuMpData.CpuData[ProcessorNumber]; + + if (!IsCurrentProcessorBSP ()) { + return EFI_DEVICE_ERROR; + } + + if (ProcessorNumber >=3D mCpuMpData.NumberOfProcessors) { + return EFI_NOT_FOUND; + } + + if (IsProcessorBSP (ProcessorNumber)) { + return EFI_INVALID_PARAMETER; + } + + if (GetApState (CpuData) !=3D CpuStateIdle) { + return EFI_UNSUPPORTED; + } + + if (EnableAP) { + if (!IsProcessorEnabled (ProcessorNumber)) { + mCpuMpData.NumberOfEnabledProcessors++; + } + + StatusFlag |=3D PROCESSOR_ENABLED_BIT; + } else { + if (IsProcessorEnabled (ProcessorNumber)) { + mCpuMpData.NumberOfEnabledProcessors--; + } + + StatusFlag &=3D ~PROCESSOR_ENABLED_BIT; + } + + if (HealthFlag !=3D NULL) { + StatusFlag &=3D ~PROCESSOR_HEALTH_STATUS_BIT; + StatusFlag |=3D (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT); + } + + mCpuMpData.CpuData[ProcessorNumber].Info.StatusFlag =3D StatusFlag; + return EFI_SUCCESS; +} + +/** + This return the handle number for the calling processor. This service m= ay be + called from the BSP and APs. + + This service returns the processor handle number for the calling process= or. + The returned value is in the range from 0 to the total number of logical + processors minus 1. The total number of logical processors can be retrie= ved + with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may = be + called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALI= D_PARAMETER + is returned. Otherwise, the current processors handle number is returned= in + ProcessorNumber, and EFI_SUCCESS is returned. + + @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL i= nstance. + @param[out] ProcessorNumber The handle number of AP that is to become t= he new + BSP. The range is from 0 to the total numbe= r of + logical processors minus 1. The total numbe= r of + logical processors can be retrieved by + EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcess= ors(). + + @retval EFI_SUCCESS The current processor handle number was = returned + in ProcessorNumber. + @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL. + +**/ +STATIC +EFI_STATUS +EFIAPI +WhoAmI ( + IN EFI_MP_SERVICES_PROTOCOL *This, + OUT UINTN *ProcessorNumber + ) +{ + UINTN Index; + UINT64 ProcessorId; + + if (ProcessorNumber =3D=3D NULL) { + return EFI_INVALID_PARAMETER; + } + + ProcessorId =3D GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ()); + for (Index =3D 0; Index < mCpuMpData.NumberOfProcessors; Index++) { + if (ProcessorId =3D=3D gProcessorIDs[Index]) { + *ProcessorNumber =3D Index; + break; + } + } + + return EFI_SUCCESS; +} + +STATIC EFI_MP_SERVICES_PROTOCOL mMpServicesProtocol =3D { + GetNumberOfProcessors, + GetProcessorInfo, + StartupAllAPs, + StartupThisAP, + SwitchBSP, + EnableDisableAP, + WhoAmI +}; + +/** 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 + ) +{ + UINTN Index; + BOOLEAN Found; + + Found =3D FALSE; + + if (ApState =3D=3D CpuStateIdle) { + return; + } + + // If we are retrying make sure we don't double count + for (Index =3D 0; Index < mCpuMpData.FailedListIndex; Index++) { + if (mCpuMpData.FailedList[Index] =3D=3D ProcessorIndex) { + Found =3D TRUE; + break; + } + } + + /* If the CPU isn't already in the FailedList, add it */ + if (!Found) { + mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] =3D Index; + } +} + +/** Handles the StartupAllAPs case where the timeout has occurred. + +**/ +STATIC +VOID +ProcessStartupAllAPsTimeout ( + VOID + ) +{ + CPU_AP_DATA *CpuData; + UINTN Index; + + if (mCpuMpData.FailedList =3D=3D NULL) { + return; + } + + for (Index =3D 0; Index < mCpuMpData.NumberOfProcessors; Index++) { + CpuData =3D &mCpuMpData.CpuData[Index]; + if (IsProcessorBSP (Index)) { + // Skip BSP + continue; + } + + if (!IsProcessorEnabled (Index)) { + // Skip Disabled processors + continue; + } + + CpuData =3D &mCpuMpData.CpuData[Index]; + AddProcessorToFailedList (Index, GetApState (CpuData)); + } +} + +/** Updates the status of the APs. + + @param[in] ProcessorIndex The index of the AP to update. +**/ +STATIC +VOID +UpdateApStatus ( + IN UINTN ProcessorIndex + ) +{ + EFI_STATUS Status; + CPU_AP_DATA *CpuData; + CPU_AP_DATA *NextCpuData; + CPU_STATE State; + UINTN NextNumber; + + CpuData =3D &mCpuMpData.CpuData[ProcessorIndex]; + + if (IsProcessorBSP (ProcessorIndex)) { + // Skip BSP + return; + } + + if (!IsProcessorEnabled (ProcessorIndex)) { + // Skip Disabled processors + return; + } + + State =3D GetApState (CpuData); + + switch (State) { + case CpuStateFinished: + if (mCpuMpData.SingleThread) { + Status =3D GetNextBlockedNumber (&NextNumber); + if (!EFI_ERROR (Status)) { + NextCpuData =3D &mCpuMpData.CpuData[NextNumber]; + + NextCpuData->State =3D CpuStateReady; + + SetApProcedure ( + NextCpuData, + mCpuMpData.Procedure, + mCpuMpData.ProcedureArgument + ); + } + } + + CpuData->State =3D CpuStateIdle; + mCpuMpData.FinishCount++; + break; + + default: + break; + } +} + +/** + If a timeout is specified in StartupAllAps(), a timer is set, which invo= kes + 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 + ) +{ + UINTN Index; + + if (mCpuMpData.TimeoutActive) { + mCpuMpData.Timeout -=3D CalculateAndStallInterval (mCpuMpData.Timeout); + } + + for (Index =3D 0; Index < mCpuMpData.NumberOfProcessors; Index++) { + UpdateApStatus (Index); + } + + if (mCpuMpData.TimeoutActive && (mCpuMpData.Timeout =3D=3D 0)) { + ProcessStartupAllAPsTimeout (); + + // Force terminal exit + mCpuMpData.FinishCount =3D mCpuMpData.StartCount; + } + + if (mCpuMpData.FinishCount !=3D mCpuMpData.StartCount) { + return; + } + + gBS->SetTimer ( + mCpuMpData.CheckAllAPsEvent, + TimerCancel, + 0 + ); + + if (mCpuMpData.FailedListIndex =3D=3D 0) { + if (mCpuMpData.FailedList !=3D NULL) { + FreePool (mCpuMpData.FailedList); + mCpuMpData.FailedList =3D NULL; + } + } + + gBS->SignalEvent (mCpuMpData.WaitEvent); +} + +/** 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 + ) +{ + EFI_STATUS Status; + CPU_AP_DATA *CpuData; + CPU_STATE State; + + CpuData =3D Context; + CpuData->TimeTaken +=3D POLL_INTERVAL_US; + + State =3D GetApState (CpuData); + + if (State =3D=3D CpuStateFinished) { + Status =3D gBS->SetTimer (CpuData->CheckThisAPEvent, TimerCancel, 0); + ASSERT_EFI_ERROR (Status); + + if (mCpuMpData.WaitEvent !=3D NULL) { + Status =3D gBS->SignalEvent (mCpuMpData.WaitEvent); + ASSERT_EFI_ERROR (Status); + } + + CpuData->State =3D CpuStateIdle; + } + + if (CpuData->TimeTaken > CpuData->Timeout) { + if (mCpuMpData.WaitEvent !=3D NULL) { + Status =3D gBS->SignalEvent (mCpuMpData.WaitEvent); + ASSERT_EFI_ERROR (Status); + } + } +} + +/** + This function is called by all processors (both BSP and AP) once and col= lects + 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 i= n. + +**/ +STATIC +EFI_STATUS +FillInProcessorInformation ( + IN BOOLEAN BSP, + IN UINTN Mpidr, + IN UINTN ProcessorIndex + ) +{ + EFI_PROCESSOR_INFORMATION *CpuInfo; + + CpuInfo =3D &mCpuMpData.CpuData[ProcessorIndex].Info; + + CpuInfo->ProcessorId =3D GET_MPIDR_AFFINITY_BITS (Mpidr); + CpuInfo->StatusFlag =3D PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS= _BIT; + + if (BSP) { + CpuInfo->StatusFlag |=3D PROCESSOR_AS_BSP_BIT; + } + + if ((Mpidr & MPIDR_MT_BIT) > 0) { + CpuInfo->Location.Package =3D GET_MPIDR_AFF2 (Mpidr); + CpuInfo->Location.Core =3D GET_MPIDR_AFF1 (Mpidr); + CpuInfo->Location.Thread =3D GET_MPIDR_AFF0 (Mpidr); + + CpuInfo->ExtendedInformation.Location2.Package =3D GET_MPIDR_AFF3 (Mpi= dr); + CpuInfo->ExtendedInformation.Location2.Die =3D GET_MPIDR_AFF2 (Mpi= dr); + CpuInfo->ExtendedInformation.Location2.Core =3D GET_MPIDR_AFF1 (Mpi= dr); + CpuInfo->ExtendedInformation.Location2.Thread =3D GET_MPIDR_AFF0 (Mpi= dr); + } else { + CpuInfo->Location.Package =3D GET_MPIDR_AFF1 (Mpidr); + CpuInfo->Location.Core =3D GET_MPIDR_AFF0 (Mpidr); + CpuInfo->Location.Thread =3D 0; + + CpuInfo->ExtendedInformation.Location2.Package =3D GET_MPIDR_AFF2 (Mpi= dr); + CpuInfo->ExtendedInformation.Location2.Die =3D GET_MPIDR_AFF1 (Mpi= dr); + CpuInfo->ExtendedInformation.Location2.Core =3D GET_MPIDR_AFF0 (Mpi= dr); + CpuInfo->ExtendedInformation.Location2.Thread =3D 0; + } + + mCpuMpData.CpuData[ProcessorIndex].State =3D BSP ? CpuStateBusy : CpuSta= teIdle; + + mCpuMpData.CpuData[ProcessorIndex].Procedure =3D NULL; + mCpuMpData.CpuData[ProcessorIndex].Parameter =3D NULL; + + return EFI_SUCCESS; +} + +/** Initializes the MP Services system data + + @param NumberOfProcessors The number of processors, both BSP and AP. + @param CoreInfo CPU information gathered earlier during boot. + +**/ +STATIC +EFI_STATUS +MpServicesInitialize ( + IN UINTN NumberOfProcessors, + IN CONST ARM_CORE_INFO *CoreInfo + ) +{ + EFI_STATUS Status; + UINTN Index; + EFI_EVENT ReadyToBootEvent; + BOOLEAN IsBsp; + + // + // Clear the data structure area first. + // + ZeroMem (&mCpuMpData, sizeof (CPU_MP_DATA)); + // + // First BSP fills and inits all known values, including its own records. + // + mCpuMpData.NumberOfProcessors =3D NumberOfProcessors; + mCpuMpData.NumberOfEnabledProcessors =3D NumberOfProcessors; + + mCpuMpData.CpuData =3D AllocateZeroPool ( + mCpuMpData.NumberOfProcessors * sizeof (CPU_AP_DA= TA) + ); + + if (mCpuMpData.CpuData =3D=3D NULL) { + return EFI_OUT_OF_RESOURCES; + } + + /* Allocate one extra for the sentinel entry at the end */ + gProcessorIDs =3D AllocatePool ((mCpuMpData.NumberOfProcessors + 1) * si= zeof (UINT64)); + ASSERT (gProcessorIDs !=3D NULL); + + Status =3D gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + CheckAllAPsStatus, + NULL, + &mCpuMpData.CheckAllAPsEvent + ); + ASSERT_EFI_ERROR (Status); + + gApStacksBase =3D AllocatePages ( + EFI_SIZE_TO_PAGES ( + mCpuMpData.NumberOfProcessors * + gApStackSize + ) + ); + ASSERT (gApStacksBase !=3D NULL); + + for (Index =3D 0; Index < mCpuMpData.NumberOfProcessors; Index++) { + if (GET_MPIDR_AFFINITY_BITS (ArmReadMpidr ()) =3D=3D CoreInfo[Index].M= pidr) { + IsBsp =3D TRUE; + } else { + IsBsp =3D FALSE; + } + + FillInProcessorInformation (IsBsp, CoreInfo[Index].Mpidr, Index); + + gProcessorIDs[Index] =3D mCpuMpData.CpuData[Index].Info.ProcessorId; + + Status =3D gBS->CreateEvent ( + EVT_TIMER | EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + CheckThisAPStatus, + (VOID *)&mCpuMpData.CpuData[Index], + &mCpuMpData.CpuData[Index].CheckThisAPEvent + ); + ASSERT_EFI_ERROR (Status); + } + + gProcessorIDs[Index] =3D MAX_UINT64; + + // + // The global pointer variables as well as the gProcessorIDs array conte= nts + // are accessed by the other cores so we must clean them to the PoC + // + WriteBackDataCacheRange (&gProcessorIDs, sizeof (UINT64 *)); + WriteBackDataCacheRange (&gApStacksBase, sizeof (UINT64 *)); + + WriteBackDataCacheRange ( + gProcessorIDs, + (mCpuMpData.NumberOfProcessors + 1) * sizeof (UINT64) + ); + + Status =3D EfiCreateEventReadyToBootEx ( + TPL_CALLBACK, + ReadyToBootSignaled, + NULL, + &ReadyToBootEvent + ); + ASSERT_EFI_ERROR (Status); + + return EFI_SUCCESS; +} + +/** + Event notification function called when the EFI_EVENT_GROUP_READY_TO_BOO= T 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 + ) +{ + mNonBlockingModeAllowed =3D FALSE; +} + +/** 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 =3D 1; + + DEBUG ((DEBUG_INFO, "Starting MP services\n")); + + Status =3D gBS->HandleProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&Image + ); + ASSERT_EFI_ERROR (Status); + + // + // Parts of the code in this driver may be executed by other cores runni= ng + // 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 =3D gBS->HandleProtocol ( + ImageHandle, + &gEfiLoadedImageProtocolGuid, + (VOID **)&Image + ); + ASSERT_EFI_ERROR (Status); + + // + // Parts of the code in this driver may be executed by other cores runni= ng + // with the MMU off so we need to ensure that everything is clean to the + // point of coherency (PoC) + // + WriteBackDataCacheRange (Image->ImageBase, Image->ImageSize); + + Hob =3D GetFirstGuidHob (&gArmMpCoreInfoGuid); + if (Hob !=3D NULL) { + HobData =3D GET_GUID_HOB_DATA (Hob); + HobDataSize =3D GET_GUID_HOB_DATA_SIZE (Hob); + CoreInfo =3D (ARM_CORE_INFO *)HobData; + MaxCpus =3D HobDataSize / sizeof (ARM_CORE_INFO); + } + + if (MaxCpus =3D=3D 1) { + DEBUG ((DEBUG_WARN, "Trying to use EFI_MP_SERVICES_PROTOCOL on a UP sy= stem")); + // We are not MP so nothing to do + return EFI_NOT_FOUND; + } + + Status =3D MpServicesInitialize (MaxCpus, CoreInfo); + if (Status !=3D EFI_SUCCESS) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + // + // Now install the MP services protocol. + // + Handle =3D NULL; + Status =3D 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 =3D mCpuMpData.CpuData[ProcessorIndex].Procedure; + UserApParameter =3D 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 =3D CpuStateFinished; + + ArmDataMemoryBarrier (); + + /* Since we're finished with this AP, turn it off */ + Args.Arg0 =3D 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 =3D 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 =3D &mCpuMpData.CpuData[ProcessorIndex].Info; + + return (CpuInfo->StatusFlag & PROCESSOR_ENABLED_BIT) !=3D 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 =3D 0; Index < mCpuMpData.NumberOfProcessors; Index++) { + CpuData =3D &mCpuMpData.CpuData[Index]; + if (IsProcessorBSP (Index)) { + // Skip BSP + continue; + } + + if (!IsProcessorEnabled (Index)) { + // Skip Disabled processors + continue; + } + + if (GetApState (CpuData) !=3D 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 =3D 0; + mCpuMpData.StartCount =3D 0; + mCpuMpData.SingleThread =3D SingleThread; + + APInitialState =3D CpuStateReady; + + for (Index =3D 0; Index < mCpuMpData.NumberOfProcessors; Index++) { + CpuData =3D &mCpuMpData.CpuData[Index]; + + // + // Get APs prepared, and put failing APs into FailedCpuList. + // If "SingleThread", only 1 AP will put into ready state, other AP wi= ll 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 !=3D NULL) { + mCpuMpData.FailedList[mCpuMpData.FailedListIndex++] =3D Index; + } + + continue; + } + + ASSERT (GetApState (CpuData) =3D=3D CpuStateIdle); + CpuData->State =3D APInitialState; + + mCpuMpData.StartCount++; + if (SingleThread) { + APInitialState =3D 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 =3D 0; Index < mCpuMpData.NumberOfProcessors; Index++) { + CpuData =3D &mCpuMpData.CpuData[Index]; + if (IsProcessorBSP (Index)) { + // Skip BSP + continue; + } + + if (!IsProcessorEnabled (Index)) { + // Skip Disabled processors + continue; + } + + if (GetApState (CpuData) =3D=3D CpuStateReady) { + SetApProcedure (CpuData, Procedure, ProcedureArgument); + } + } + + // + // Save data into private data structure, and create timer to poll AP st= ate + // before exiting + // + mCpuMpData.Procedure =3D Procedure; + mCpuMpData.ProcedureArgument =3D ProcedureArgument; + mCpuMpData.WaitEvent =3D WaitEvent; + mCpuMpData.Timeout =3D TimeoutInMicroseconds; + mCpuMpData.TimeoutActive =3D (BOOLEAN)(TimeoutInMicroseconds !=3D 0); + Status =3D gBS->SetTimer ( + mCpuMpData.CheckAllAPsEvent, + TimerPeriodic, + POLL_INTERVAL_US + ); + return Status; +} + +/** Handles execution of StartupAllAPs when no wait event has been specifi= ed. + + @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 C= PUs. + + @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 =3D TimeoutInMicroseconds; + + while (TRUE) { + for (Index =3D 0; Index < mCpuMpData.NumberOfProcessors; Index++) { + CpuData =3D &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 =3D DispatchCpu (Index); + if (EFI_ERROR (Status)) { + CpuData->State =3D CpuStateIdle; + Status =3D EFI_NOT_READY; + goto Done; + } + + break; + + case CpuStateFinished: + mCpuMpData.FinishCount++; + if (SingleThread) { + Status =3D GetNextBlockedNumber (&NextIndex); + if (!EFI_ERROR (Status)) { + mCpuMpData.CpuData[NextIndex].State =3D CpuStateReady; + } + } + + CpuData->State =3D CpuStateIdle; + break; + + default: + break; + } + } + + if (mCpuMpData.FinishCount =3D=3D mCpuMpData.StartCount) { + Status =3D EFI_SUCCESS; + goto Done; + } + + if ((TimeoutInMicroseconds !=3D 0) && (Timeout =3D=3D 0)) { + Status =3D EFI_TIMEOUT; + goto Done; + } + + Timeout -=3D CalculateAndStallInterval (Timeout); + } + +Done: + if (FailedCpuList !=3D NULL) { + if (mCpuMpData.FailedListIndex =3D=3D 0) { + FreePool (*FailedCpuList); + *FailedCpuList =3D 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 @@ +#=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D +# Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +#=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D + +.text +.align 3 + +#include +#include + +#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 =3D processor index + +// Find index in gProcessorIDs for current processor +1: + ldr x3, [x1, x2, lsl #3] // x4 =3D gProcessorIDs + x2 * 8 + cmp x3, #-1 // check if we've reached the end of gProces= sorIDs + beq ProcessorNotFound + add x2, x2, 1 // x2++ + cmp x0, x3 // if mpidr_el1 !=3D gProcessorIDs[x] then l= oop + 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 =3D (ProcessorIndex + 1) * gApStackSize + add sp, x0, x3 // sp =3D 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 . --=20 2.30.2 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#93329): https://edk2.groups.io/g/devel/message/93329 Mute This Topic: https://groups.io/mt/93518787/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- From nobody Tue May 14 06:52:19 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) client-ip=66.175.222.108; envelope-from=bounce+27952+93327+1787277+3901457@groups.io; helo=mail02.groups.io; Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+93327+1787277+3901457@groups.io; dmarc=fail(p=none dis=none) header.from=quicinc.com ARC-Seal: i=1; a=rsa-sha256; t=1662523451; cv=none; d=zohomail.com; s=zohoarc; b=n0c2jjHjfmFJKk0K0EAfMLkEQtx/XFE0DOSGDxicWSWpKfXdwmzND6ClokGPPCnF+isf6nq3ScxLihZob9IUzOaSA6DM7LZKHgTqoOFaxM2KB1HB3AgT9j/c2zRjutWS/ZWdtMIHO7ymlwboySA8GoU89eZePERCPNhQ9zBTGhI= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1662523451; h=Content-Type:Content-Transfer-Encoding:Cc:Date:From:In-Reply-To:List-Subscribe:List-Id:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=6AG0FN+CVgWgc7pgzIKFPr7KlebLs8BiuNsgV0Oab60=; b=LEdK/JBC4PtThIDLNXpqWNGF+JyTrU42of7MRKy4NmSZoWL7Tt36BxHruSaLhwmkS5SXaSuh+yXTdLkMJEUYIMBJVzb+Eni7Jz7Sd1R3Q08nmEUInZP+owjzK2Kpwjay+rdSroFX3UvXpnXlIWNK+lje3ZZPVKpWvMwMUcXWkGo= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of groups.io designates 66.175.222.108 as permitted sender) smtp.mailfrom=bounce+27952+93327+1787277+3901457@groups.io; dmarc=fail header.from= (p=none dis=none) Received: from mail02.groups.io (mail02.groups.io [66.175.222.108]) by mx.zohomail.com with SMTPS id 1662523451206560.5813977072262; Tue, 6 Sep 2022 21:04:11 -0700 (PDT) Return-Path: X-Received: by 127.0.0.2 with SMTP id QUfaYY1788612xirc7CIlwRe; Tue, 06 Sep 2022 21:04:10 -0700 X-Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) by mx.groups.io with SMTP id smtpd.web08.3893.1662523450074657505 for ; Tue, 06 Sep 2022 21:04:10 -0700 X-Received: from pps.filterd (m0279862.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.5/8.17.1.5) with ESMTP id 2873mwNo007577; Wed, 7 Sep 2022 04:04:00 GMT X-Received: from nalasppmta04.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3je0mfv2fj-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 07 Sep 2022 04:03:50 +0000 X-Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA04.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 28743oNA015817 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT); Wed, 7 Sep 2022 04:03:50 GMT X-Received: from linbox.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.29; Tue, 6 Sep 2022 21:03:49 -0700 From: "Rebecca Cran" To: , , , Sami Mujawar , Jian J Wang , Liming Gao CC: Rebecca Cran Subject: [edk2-devel] [PATCH v2 2/2] MdeModulePkg: Add new Application/MpServicesTest application Date: Tue, 6 Sep 2022 22:03:26 -0600 Message-ID: <20220907040326.388003-3-rebecca@quicinc.com> In-Reply-To: <20220907040326.388003-1-rebecca@quicinc.com> References: <20220907040326.388003-1-rebecca@quicinc.com> MIME-Version: 1.0 X-Originating-IP: [10.80.80.8] X-ClientProxiedBy: nasanex01a.na.qualcomm.com (10.52.223.231) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-GUID: p-vSLJGWK3QPlC_ejuYHqTBSVXqx0j87 X-Proofpoint-ORIG-GUID: p-vSLJGWK3QPlC_ejuYHqTBSVXqx0j87 Precedence: Bulk List-Unsubscribe: List-Subscribe: List-Help: Sender: devel@edk2.groups.io List-Id: Mailing-List: list devel@edk2.groups.io; contact devel+owner@edk2.groups.io Reply-To: devel@edk2.groups.io,quic_rcran@quicinc.com X-Gm-Message-State: F07aJiEta8SiJBqG7DG7uHDFx1787277AA= Content-Transfer-Encoding: quoted-printable DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=groups.io; q=dns/txt; s=20140610; t=1662523450; bh=9fLvFrftgmkPwXRNHML5Q/B7TTbuAHIGO8seSSy/Uds=; h=CC:Content-Type:Date:From:Reply-To:Subject:To; b=hyl8hskFxliPle7HFNVzeIWu7ZjH3X9/wkn88ypTJJsWP2IwFmg2WvSezdTsfKnoDEr iNSViEhHbMdkQCY9v/SH/FK+u9qTdsZILMagRWHeFWGNR/HuTJZtGcRikpIRnzvhbBtZu SLt9+aQyFwRhDyddljVG/MKXc1uKnvaoLA0= X-ZohoMail-DKIM: pass (identity @groups.io) X-ZM-MESSAGEID: 1662523453090100001 Content-Type: text/plain; charset="utf-8" The MpServicesTest application exercises the EFI_MP_SERVICES_PROTOCOL. usage: MpServicesTest -A [-O] MpServicesTest -T MpServicesTest -S MpServicesTest -P MpServicesTest -U MpServicesTest -W MpServicesTest -E MpServicesTest -D MpServicesTest -h Parameter: -A: Run all APs. -O: Run APs sequentially (use with -A). -T: Specify timeout in milliseconds. Default is to wait forever. -S: Specify the single AP to run. -P: Print processor information. -U: Set the specified AP to the Unhealthy status (use with -E/-D). -W: Run WhoAmI and print index of BSP. -E: Enable the specified AP. -D: Disable the specified AP. -h: Print this help page. Signed-off-by: Rebecca Cran Reviewed-by: Ard Biesheuvel --- MdeModulePkg/MdeModulePkg.dsc | 2 + MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf | 40 ++ MdeModulePkg/Application/MpServicesTest/Options.h | 39 ++ MdeModulePkg/Application/MpServicesTest/MpServicesTest.c | 555 +++++++++= +++++++++++ MdeModulePkg/Application/MpServicesTest/Options.c | 215 ++++++++ 5 files changed, 851 insertions(+) diff --git a/MdeModulePkg/MdeModulePkg.dsc b/MdeModulePkg/MdeModulePkg.dsc index 45a8ec84ad69..295ff4ddfcd8 100644 --- a/MdeModulePkg/MdeModulePkg.dsc +++ b/MdeModulePkg/MdeModulePkg.dsc @@ -166,6 +166,7 @@ [LibraryClasses.common.UEFI_APPLICATION] MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAll= ocationLib.inf DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf + ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf =20 [LibraryClasses.common.MM_STANDALONE] HobLib|MdeModulePkg/Library/BaseHobLibNull/BaseHobLibNull.inf @@ -445,6 +446,7 @@ [Components] MdeModulePkg/Library/BaseVariableFlashInfoLib/BaseVariableFlashInfoLib.i= nf =20 [Components.IA32, Components.X64, Components.AARCH64] + MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf MdeModulePkg/Universal/EbcDxe/EbcDxe.inf MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf diff --git a/MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf b/M= deModulePkg/Application/MpServicesTest/MpServicesTest.inf new file mode 100644 index 000000000000..07ee4afec845 --- /dev/null +++ b/MdeModulePkg/Application/MpServicesTest/MpServicesTest.inf @@ -0,0 +1,40 @@ +## @file +# UEFI Application to exercise EFI_MP_SERVICES_PROTOCOL. +# +# Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserve= d.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION =3D 1.29 + BASE_NAME =3D MpServicesTest + FILE_GUID =3D 43e9defa-7209-4b0d-b136-cc4ca02cb469 + MODULE_TYPE =3D UEFI_APPLICATION + VERSION_STRING =3D 0.1 + ENTRY_POINT =3D UefiMain + +# +# The following information is for reference only and not required by the = build tools. +# +# VALID_ARCHITECTURES =3D IA32 X64 AARCH64 +# + +[Sources] + MpServicesTest.c + Options.c + Options.h + +[Packages] + MdePkg/MdePkg.dec + +[LibraryClasses] + BaseLib + ShellLib + UefiApplicationEntryPoint + UefiLib + +[Protocols] + gEfiMpServiceProtocolGuid ## CONSUMES + diff --git a/MdeModulePkg/Application/MpServicesTest/Options.h b/MdeModuleP= kg/Application/MpServicesTest/Options.h new file mode 100644 index 000000000000..cb28230ab095 --- /dev/null +++ b/MdeModulePkg/Application/MpServicesTest/Options.h @@ -0,0 +1,39 @@ +/** @file + Options handling code. + + Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved= .
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#ifndef MPSERVICESTEST_OPTIONS_H_ +#define MPSERVICESTEST_OPTIONS_H_ + +#define INFINITE_TIMEOUT 0 + +typedef struct { + UINTN Timeout; + UINTN ProcessorIndex; + BOOLEAN RunAllAPs; + BOOLEAN RunSingleAP; + BOOLEAN DisableProcessor; + BOOLEAN EnableProcessor; + BOOLEAN SetProcessorHealthy; + BOOLEAN SetProcessorUnhealthy; + BOOLEAN PrintProcessorInformation; + BOOLEAN PrintBspProcessorIndex; + BOOLEAN RunAPsSequentially; +} MP_SERVICES_TEST_OPTIONS; + +/** + Parses any arguments provided on the command line. + + @param Options The arguments structure. + + @return EFI_SUCCESS on success, or an error code. +**/ +EFI_STATUS +ParseArguments ( + MP_SERVICES_TEST_OPTIONS *Options + ); + +#endif /* MPSERVICESTEST_OPTIONS_H_ */ diff --git a/MdeModulePkg/Application/MpServicesTest/MpServicesTest.c b/Mde= ModulePkg/Application/MpServicesTest/MpServicesTest.c new file mode 100644 index 000000000000..1cea8f52f25d --- /dev/null +++ b/MdeModulePkg/Application/MpServicesTest/MpServicesTest.c @@ -0,0 +1,555 @@ +/** @file + UEFI Application to exercise EFI_MP_SERVICES_PROTOCOL. + + Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved= .
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Options.h" + +#define APFUNC_BUFFER_LEN 256 + +typedef struct { + EFI_MP_SERVICES_PROTOCOL *Mp; + CHAR16 **Buffer; +} APFUNC_ARG; + +/** The procedure to run with the MP Services interface. + + @param Arg The procedure argument. + +**/ +STATIC +VOID +EFIAPI +ApFunction ( + IN OUT VOID *Arg + ) +{ + APFUNC_ARG *Param; + UINTN ProcessorId; + + if (Arg !=3D NULL) { + Param =3D Arg; + + Param->Mp->WhoAmI (Param->Mp, &ProcessorId); + UnicodeSPrint (Param->Buffer[ProcessorId], APFUNC_BUFFER_LEN, L"Hello = from CPU %ld\n", ProcessorId); + } +} + +/** + Fetches the number of processors and which processor is the BSP. + + @param Mp MP Services Protocol. + @param NumProcessors Number of processors. + @param BspIndex The index of the BSP. +**/ +STATIC +EFI_STATUS +GetProcessorInformation ( + IN EFI_MP_SERVICES_PROTOCOL *Mp, + OUT UINTN *NumProcessors, + OUT UINTN *BspIndex + ) +{ + EFI_STATUS Status; + UINTN NumEnabledProcessors; + + Status =3D Mp->GetNumberOfProcessors (Mp, NumProcessors, &NumEnabledProc= essors); + if (EFI_ERROR (Status)) { + return Status; + } + + Status =3D Mp->WhoAmI (Mp, BspIndex); + if (EFI_ERROR (Status)) { + return Status; + } + + return EFI_SUCCESS; +} + +/** Displays information returned from MP Services Protocol. + + @param Mp The MP Services Protocol + @param BspIndex On return, contains the index of the BSP. + + @return The number of CPUs in the system. + +**/ +STATIC +UINTN +PrintProcessorInformation ( + IN EFI_MP_SERVICES_PROTOCOL *Mp, + OUT UINTN *BspIndex + ) +{ + EFI_STATUS Status; + EFI_PROCESSOR_INFORMATION CpuInfo; + UINTN Index; + UINTN NumCpu; + UINTN NumEnabledCpu; + + Status =3D Mp->GetNumberOfProcessors (Mp, &NumCpu, &NumEnabledCpu); + if (EFI_ERROR (Status)) { + Print (L"GetNumberOfProcessors failed: %r\n", Status); + } else { + Print (L"Number of CPUs: %ld, Enabled: %d\n", NumCpu, NumEnabledCpu); + } + + for (Index =3D 0; Index < NumCpu; Index++) { + Status =3D Mp->GetProcessorInfo (Mp, CPU_V2_EXTENDED_TOPOLOGY | Index,= &CpuInfo); + if (EFI_ERROR (Status)) { + Print (L"GetProcessorInfo for Processor %d failed: %r\n", Index, Sta= tus); + } else { + Print ( + L"Processor %d:\n" + L"\tID: %016lx\n" + L"\tStatus: %s | ", + Index, + CpuInfo.ProcessorId, + (CpuInfo.StatusFlag & PROCESSOR_AS_BSP_BIT) ? L"BSP" : L"AP" + ); + + if ((CpuInfo.StatusFlag & PROCESSOR_AS_BSP_BIT) && (BspIndex !=3D NU= LL)) { + *BspIndex =3D Index; + } + + Print (L"%s | ", (CpuInfo.StatusFlag & PROCESSOR_ENABLED_BIT) ? L"En= abled" : L"Disabled"); + Print (L"%s\n", (CpuInfo.StatusFlag & PROCESSOR_HEALTH_STATUS_BIT) ?= L"Healthy" : L"Faulted"); + + Print ( + L"\tLocation: Package %d, Core %d, Thread %d\n" + L"\tExtended Information: Package %d, Module %d, Tile %d, Die %d, = Core %d, Thread %d\n\n", + CpuInfo.Location.Package, + CpuInfo.Location.Core, + CpuInfo.Location.Thread, + CpuInfo.ExtendedInformation.Location2.Package, + CpuInfo.ExtendedInformation.Location2.Module, + CpuInfo.ExtendedInformation.Location2.Tile, + CpuInfo.ExtendedInformation.Location2.Die, + CpuInfo.ExtendedInformation.Location2.Core, + CpuInfo.ExtendedInformation.Location2.Thread + ); + } + } + + return NumCpu; +} + +/** Allocates memory in ApArg for the single AP specified. + + @param ApArg Pointer to the AP argument structure. + @param Mp The MP Services Protocol. + @param ProcessorIndex The index of the AP. + + @retval EFI_SUCCESS Memory was successfully allocated. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +STATIC +EFI_STATUS +AllocateApFuncBufferSingleAP ( + IN APFUNC_ARG *ApArg, + IN EFI_MP_SERVICES_PROTOCOL *Mp, + IN UINTN ProcessorIndex + ) +{ + ApArg->Mp =3D Mp; + + ApArg->Buffer =3D AllocateZeroPool ((ProcessorIndex + 1) * sizeof (VOID = *)); + if (ApArg->Buffer =3D=3D NULL) { + Print (L"Failed to allocate buffer for AP buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + + ApArg->Buffer[ProcessorIndex] =3D AllocateZeroPool (APFUNC_BUFFER_LEN); + if (ApArg->Buffer[ProcessorIndex] =3D=3D NULL) { + Print (L"Failed to allocate buffer for AP buffer\n"); + FreePool (ApArg->Buffer); + return EFI_OUT_OF_RESOURCES; + } + + return EFI_SUCCESS; +} + +/** Allocates memory in ApArg for all APs. + + @param ApArg Pointer to the AP argument structure. + @param Mp The MP Services Protocol. + @param NumCpus The number of CPUs. + + @retval EFI_SUCCESS Memory was successfully allocated. + @retval EFI_OUT_OF_RESOURCES Failed to allocate memory. + +**/ +STATIC +EFI_STATUS +AllocateApFuncBufferAllAPs ( + IN APFUNC_ARG *ApArg, + IN EFI_MP_SERVICES_PROTOCOL *Mp, + IN UINTN NumCpus + ) +{ + INT32 Index; + + ApArg->Mp =3D Mp; + + ApArg->Buffer =3D AllocateZeroPool (NumCpus * sizeof (VOID *)); + if (ApArg->Buffer =3D=3D NULL) { + Print (L"Failed to allocate buffer for AP message\n"); + return EFI_OUT_OF_RESOURCES; + } + + for (Index =3D 0; Index < NumCpus; Index++) { + ApArg->Buffer[Index] =3D AllocateZeroPool (APFUNC_BUFFER_LEN); + if (ApArg->Buffer[Index] =3D=3D NULL) { + Print (L"Failed to allocate buffer for AP message\n"); + for (--Index; Index >=3D 0; Index++) { + FreePool (ApArg->Buffer[Index]); + } + + FreePool (ApArg->Buffer); + return EFI_OUT_OF_RESOURCES; + } + } + + return EFI_SUCCESS; +} + +/** Frees memory in ApArg for all APs. + + @param ApArg Pointer to the AP argument structure. + @param NumCpus The number of CPUs. + +**/ +STATIC +VOID +FreeApFuncBuffer ( + APFUNC_ARG *ApArg, + UINTN NumCpus + ) +{ + UINTN Index; + + for (Index =3D 0; Index < NumCpus; Index++) { + if (ApArg->Buffer[Index] !=3D NULL) { + FreePool (ApArg->Buffer[Index]); + } + } + + FreePool (ApArg->Buffer); +} + +/** Runs a specified AP. + + @param Mp The MP Services Protocol. + @param ProcessorIndex The processor index. + @param Timeout Timeout in milliseconds. + + @return EFI_SUCCESS on success, or an error code. + +**/ +STATIC +EFI_STATUS +StartupThisAP ( + IN EFI_MP_SERVICES_PROTOCOL *Mp, + IN UINTN ProcessorIndex, + IN UINTN Timeout + ) +{ + EFI_STATUS Status; + APFUNC_ARG ApArg; + + Status =3D AllocateApFuncBufferSingleAP (&ApArg, Mp, ProcessorIndex); + if (EFI_ERROR (Status)) { + return Status; + } + + Status =3D AllocateApFuncBufferSingleAP (&ApArg, Mp, ProcessorIndex); + if (EFI_ERROR (Status)) { + return Status; + } + + Print ( + L"StartupThisAP on Processor %d with %d%s timeout...", + ProcessorIndex, + Timeout, + (Timeout =3D=3D INFINITE_TIMEOUT) ? L" (infinite)" : L"ms" + ); + Status =3D Mp->StartupThisAP ( + Mp, + ApFunction, + ProcessorIndex, + NULL, + Timeout * 1000, + &ApArg, + NULL + ); + if (EFI_ERROR (Status)) { + Print (L"failed: %r\n", Status); + return Status; + } else { + Print (L"done.\n"); + Print (ApArg.Buffer[ProcessorIndex]); + } + + FreeApFuncBuffer (&ApArg, ProcessorIndex + 1); + + return EFI_SUCCESS; +} + +/** Runs all APs. + + @param Mp The MP Services Protocol. + @param NumCpus The number of CPUs in the system. + @param Timeout Timeout in milliseconds. + @param RunAPsSequentially Run APs sequentially (FALSE: run simultaneousl= y) + + @return EFI_SUCCESS on success, or an error code. + +**/ +STATIC +EFI_STATUS +StartupAllAPs ( + IN EFI_MP_SERVICES_PROTOCOL *Mp, + IN UINTN NumCpus, + IN UINTN Timeout, + IN BOOLEAN RunAPsSequentially + ) +{ + EFI_STATUS Status; + UINTN Index; + APFUNC_ARG ApArg; + + Status =3D AllocateApFuncBufferAllAPs (&ApArg, Mp, NumCpus); + if (EFI_ERROR (Status)) { + return Status; + } + + Print ( + L"Running with SingleThread TRUE, %d%s timeout...", + Timeout, + (Timeout =3D=3D INFINITE_TIMEOUT) ? L" (infinite)" : L"ms" + ); + Status =3D Mp->StartupAllAPs ( + Mp, + ApFunction, + RunAPsSequentially, + NULL, + Timeout * 1000, + &ApArg, + NULL + ); + if (EFI_ERROR (Status)) { + Print (L"failed: %r\n", Status); + return Status; + } else { + Print (L"done.\n"); + for (Index =3D 0; Index < NumCpus; Index++) { + Print (ApArg.Buffer[Index]); + } + } + + FreeApFuncBuffer (&ApArg, NumCpus); + return EFI_SUCCESS; +} + +/** + Enables the specified AP. + + @param Mp The MP Services Protocol. + @param ProcessorIndex The processor to enable. + @param ProcessorHealthy The health status of the processor. + + @return EFI_SUCCESS on success, or an error code. +**/ +STATIC +EFI_STATUS +EnableAP ( + IN EFI_MP_SERVICES_PROTOCOL *Mp, + UINTN ProcessorIndex, + BOOLEAN ProcessorHealthy + ) +{ + EFI_STATUS Status; + UINT32 HealthFlag; + + if (ProcessorHealthy) { + Print (L"Enabling Processor %d with HealthFlag healthy...", ProcessorI= ndex); + HealthFlag =3D PROCESSOR_HEALTH_STATUS_BIT; + } else { + Print (L"Enabling Processor %d with HealthFlag faulted...", ProcessorI= ndex); + HealthFlag =3D 0; + } + + Status =3D Mp->EnableDisableAP (Mp, ProcessorIndex, TRUE, &HealthFlag); + if (EFI_ERROR (Status)) { + Print (L"failed: %r\n", Status); + return Status; + } else { + Print (L"done.\n"); + } + + return Status; +} + +/** + Disables the specified AP. + + @param Mp The MP Services Protocol. + @param ProcessorIndex The processor to disable. + @param ProcessorHealthy The health status of the processor. + + @return EFI_SUCCESS on success, or an error code. +**/ +STATIC +EFI_STATUS +DisableAP ( + IN EFI_MP_SERVICES_PROTOCOL *Mp, + UINTN ProcessorIndex, + BOOLEAN ProcessorHealthy + ) +{ + EFI_STATUS Status; + UINT32 HealthFlag; + + if (ProcessorHealthy) { + Print (L"Disabling Processor %d with HealthFlag healthy...", Processor= Index); + HealthFlag =3D PROCESSOR_HEALTH_STATUS_BIT; + } else { + Print (L"Disabling Processor %d with HealthFlag faulted...", Processor= Index); + HealthFlag =3D 0; + } + + Status =3D Mp->EnableDisableAP (Mp, ProcessorIndex, FALSE, &HealthFlag); + if (EFI_ERROR (Status)) { + Print (L"failed: %r\n", Status); + return Status; + } else { + Print (L"done.\n"); + } + + return Status; +} + +/** + The user Entry Point for Application. The user code starts with this fun= ction + as the real entry point for the application. + + @param[in] ImageHandle The firmware allocated handle for the EFI imag= e. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry po= int. + +**/ +EFI_STATUS +EFIAPI +UefiMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_MP_SERVICES_PROTOCOL *Mp; + UINTN BspIndex; + UINTN CpuIndex; + UINTN NumCpus; + BOOLEAN ProcessorHealthy; + MP_SERVICES_TEST_OPTIONS Options; + + BspIndex =3D 0; + + Status =3D gBS->LocateProtocol ( + &gEfiMpServiceProtocolGuid, + NULL, + (VOID **)&Mp + ); + if (EFI_ERROR (Status)) { + Print (L"Failed to locate EFI_MP_SERVICES_PROTOCOL (%r). Not installed= on platform?\n", Status); + return EFI_NOT_FOUND; + } + + Status =3D ParseArguments (&Options); + if (EFI_ERROR (Status)) { + return EFI_INVALID_PARAMETER; + } + + Status =3D GetProcessorInformation (Mp, &NumCpus, &BspIndex); + if (EFI_ERROR (Status)) { + Print (L"Error: Failed to fetch processor information.\n"); + return Status; + } + + if (Options.PrintBspProcessorIndex) { + Status =3D Mp->WhoAmI (Mp, &CpuIndex); + if (EFI_ERROR (Status)) { + Print (L"WhoAmI failed: %r\n", Status); + return Status; + } else { + Print (L"BSP: %016lx\n", CpuIndex); + } + } + + if (Options.PrintProcessorInformation) { + NumCpus =3D PrintProcessorInformation (Mp, &BspIndex); + if (NumCpus < 2) { + Print (L"Error: Uniprocessor system found.\n"); + return EFI_INVALID_PARAMETER; + } + } + + if (Options.RunSingleAP) { + Status =3D StartupThisAP ( + Mp, + Options.ProcessorIndex, + Options.Timeout + ); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (Options.RunAllAPs) { + Status =3D StartupAllAPs (Mp, NumCpus, Options.Timeout, Options.RunAPs= Sequentially); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (Options.EnableProcessor) { + ProcessorHealthy =3D TRUE; + if (Options.SetProcessorUnhealthy) { + ProcessorHealthy =3D FALSE; + } + + Status =3D EnableAP (Mp, Options.ProcessorIndex, ProcessorHealthy); + if (EFI_ERROR (Status)) { + return Status; + } + } + + if (Options.DisableProcessor) { + ProcessorHealthy =3D TRUE; + if (Options.SetProcessorUnhealthy) { + ProcessorHealthy =3D FALSE; + } + + Status =3D DisableAP (Mp, Options.ProcessorIndex, ProcessorHealthy); + if (EFI_ERROR (Status)) { + return Status; + } + } + + return EFI_SUCCESS; +} diff --git a/MdeModulePkg/Application/MpServicesTest/Options.c b/MdeModuleP= kg/Application/MpServicesTest/Options.c new file mode 100644 index 000000000000..2ea2c94b7c74 --- /dev/null +++ b/MdeModulePkg/Application/MpServicesTest/Options.c @@ -0,0 +1,215 @@ +/** @file + Options handling code. + + Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved= .
+ Copyright (c) 2010-2019 Finnbarr P. Murphy. All rights reserved.
+ SPDX-License-Identifier: BSD-2-Clause-Patent +**/ + +#include +#include +#include +#include +#include +#include + +#include "Options.h" + +STATIC UINTN Argc; +STATIC CHAR16 **Argv; + +/** + + This function provides argc and argv. + + @return Status +**/ +EFI_STATUS +GetArg ( + VOID + ) +{ + EFI_STATUS Status; + EFI_SHELL_PARAMETERS_PROTOCOL *ShellParameters; + + Status =3D gBS->HandleProtocol ( + gImageHandle, + &gEfiShellParametersProtocolGuid, + (VOID **)&ShellParameters + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Argc =3D ShellParameters->Argc; + Argv =3D ShellParameters->Argv; + return EFI_SUCCESS; +} + +/** + Checks if the character is a decimal digit. + + @param Char The character to check. + + @return TRUE if the character is a decimal digit. +**/ +BOOLEAN +IsUnicodeDecimalDigit ( + CHAR16 Char + ) +{ + return ((BOOLEAN)(Char >=3D L'0' && Char <=3D L'9')); +} + +/** + Converts the string to an integer. + + @param String The input string. + @param Value The converted number. + + @return EFI_SUCCESS on success, or an error code. +**/ +EFI_STATUS +UnicodeStringToInteger ( + CHAR16 *String, + UINTN *Value + ) +{ + UINTN Result; + + Result =3D 0; + + if ((String =3D=3D NULL) || (StrSize (String) =3D=3D 0) || (Value =3D=3D= NULL)) { + return (EFI_INVALID_PARAMETER); + } + + while (IsUnicodeDecimalDigit (*String)) { + if (!(Result <=3D (DivU64x32 ((((UINT64) ~0) - (*String - L'0')), 10))= )) { + return (EFI_DEVICE_ERROR); + } + + Result =3D MultU64x32 (Result, 10) + (*String - L'0'); + String++; + } + + *Value =3D Result; + + return (EFI_SUCCESS); +} + +/** + Print app usage. +**/ +STATIC +VOID +PrintUsage ( + VOID + ) +{ + Print (L"MpServicesTest: usage\n"); + Print (L" MpServicesTest -A [-O]\n"); + Print (L" MpServicesTest -T \n"); + Print (L" MpServicesTest -S \n"); + Print (L" MpServicesTest -P\n"); + Print (L" MpServicesTest -U\n"); + Print (L" MpServicesTest -W\n"); + Print (L" MpServicesTest -E \n"); + Print (L" MpServicesTest -D \n"); + Print (L" MpServicesTest -h\n"); + Print (L"Parameter:\n"); + Print (L" -A: Run all APs.\n"); + Print (L" -O: Run APs sequentially (use with -A).\n"); + Print (L" -T: Specify timeout in milliseconds. Default is to wait fore= ver.\n"); + Print (L" -S: Specify the single AP to run.\n"); + Print (L" -P: Print processor information.\n"); + Print (L" -U: Set the specified AP to the Unhealthy status (use with -= E/-D).\n"); + Print (L" -W: Run WhoAmI and print index of BSP.\n"); + Print (L" -E: Enable the specified AP.\n"); + Print (L" -D: Disable the specified AP.\n"); + Print (L" -h: Print this help page.\n"); +} + +/** + Parses any arguments provided on the command line. + + @param Options The arguments structure. + + @return EFI_SUCCESS on success, or an error code. +**/ +EFI_STATUS +ParseArguments ( + MP_SERVICES_TEST_OPTIONS *Options + ) +{ + EFI_STATUS Status; + INT32 ArgIndex; + CONST CHAR16 *Argument; + BOOLEAN NeedsValue; + UINTN *Value; + + Status =3D GetArg (); + if (EFI_ERROR (Status)) { + Print (L"Please use the UEFI Shell to run this application!\n", Status= ); + return Status; + } + + if (Argc =3D=3D 1) { + PrintUsage (); + } + + ZeroMem (Options, sizeof (MP_SERVICES_TEST_OPTIONS)); + + for (ArgIndex =3D 1; ArgIndex < Argc; ArgIndex++) { + Argument =3D Argv[ArgIndex]; + NeedsValue =3D FALSE; + + if (StrCmp (Argument, L"-A") =3D=3D 0) { + Options->RunAllAPs =3D TRUE; + } else if (StrCmp (Argument, L"-O") =3D=3D 0) { + Options->RunAPsSequentially =3D TRUE; + } else if (StrCmp (Argument, L"-T") =3D=3D 0) { + NeedsValue =3D TRUE; + Value =3D &Options->Timeout; + } else if (StrCmp (Argument, L"-S") =3D=3D 0) { + Options->RunSingleAP =3D TRUE; + NeedsValue =3D TRUE; + Value =3D &Options->ProcessorIndex; + } else if (StrCmp (Argument, L"-P") =3D=3D 0) { + Options->PrintProcessorInformation =3D TRUE; + } else if (StrCmp (Argument, L"-U") =3D=3D 0) { + Options->SetProcessorUnhealthy =3D TRUE; + } else if (StrCmp (Argument, L"-W") =3D=3D 0) { + Options->PrintBspProcessorIndex =3D TRUE; + } else if (StrCmp (Argument, L"-E") =3D=3D 0) { + Options->EnableProcessor =3D TRUE; + NeedsValue =3D TRUE; + Value =3D &Options->ProcessorIndex; + } else if (StrCmp (Argument, L"-D") =3D=3D 0) { + Options->DisableProcessor =3D TRUE; + NeedsValue =3D TRUE; + Value =3D &Options->ProcessorIndex; + } else { + PrintUsage (); + ZeroMem (Options, sizeof (MP_SERVICES_TEST_OPTIONS)); + return EFI_SUCCESS; + } + + if (NeedsValue) { + if ((ArgIndex + 1) < Argc) { + Status =3D UnicodeStringToInteger (Argv[ArgIndex + 1], Value); + if (EFI_ERROR (Status)) { + Print (L"Error: option value must be a positive integer.\n"); + PrintUsage (); + return EFI_INVALID_PARAMETER; + } + + ArgIndex++; + } else { + PrintUsage (); + return EFI_INVALID_PARAMETER; + } + } + } + + return EFI_SUCCESS; +} --=20 2.30.2 -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D- Groups.io Links: You receive all messages sent to this group. View/Reply Online (#93327): https://edk2.groups.io/g/devel/message/93327 Mute This Topic: https://groups.io/mt/93518552/1787277 Group Owner: devel+owner@edk2.groups.io Unsubscribe: https://edk2.groups.io/g/devel/unsub [importer@patchew.org] -=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-=3D-