summaryrefslogtreecommitdiff
path: root/EmulatorPkg/CpuRuntimeDxe
diff options
context:
space:
mode:
authorjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>2011-06-28 16:47:23 +0000
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>2011-06-28 16:47:23 +0000
commitbb89ec1a7ec2f8d35033df9e47b3604925da3bd3 (patch)
tree32d38e02ccab98dbac4c3014a12ac365775e8eb3 /EmulatorPkg/CpuRuntimeDxe
parentd3e0289ccf641481f2cbdcbb0d5868c393b7edbb (diff)
downloadedk2-platforms-bb89ec1a7ec2f8d35033df9e47b3604925da3bd3.tar.xz
InOsEmuPkg: Rename package to EmulatorPkg & Sec to Host
* Rename InOsEmuPkg to EmulatorPkg * Rename Unix/Sec to Unix/Host Signed-off-by: jljusten Reviewed-by: andrewfish Reviewed-by: geekboy15a git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11918 6f19259b-4bc3-4df7-8a09-765794883524
Diffstat (limited to 'EmulatorPkg/CpuRuntimeDxe')
-rw-r--r--EmulatorPkg/CpuRuntimeDxe/Cpu.c374
-rw-r--r--EmulatorPkg/CpuRuntimeDxe/Cpu.inf76
-rw-r--r--EmulatorPkg/CpuRuntimeDxe/CpuDriver.h246
-rw-r--r--EmulatorPkg/CpuRuntimeDxe/CpuIo.c333
-rw-r--r--EmulatorPkg/CpuRuntimeDxe/MpService.c1366
-rw-r--r--EmulatorPkg/CpuRuntimeDxe/Strings.unibin0 -> 2144 bytes
6 files changed, 2395 insertions, 0 deletions
diff --git a/EmulatorPkg/CpuRuntimeDxe/Cpu.c b/EmulatorPkg/CpuRuntimeDxe/Cpu.c
new file mode 100644
index 0000000000..5ec315bea2
--- /dev/null
+++ b/EmulatorPkg/CpuRuntimeDxe/Cpu.c
@@ -0,0 +1,374 @@
+/*++ @file
+ Emu driver to produce CPU Architectural Protocol.
+
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+Portions copyright (c) 2011, Apple Inc. All rights reserved.
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include "CpuDriver.h"
+
+UINT64 mTimerPeriod;
+
+CPU_ARCH_PROTOCOL_PRIVATE mCpuTemplate = {
+ CPU_ARCH_PROT_PRIVATE_SIGNATURE,
+ NULL,
+ {
+ EmuFlushCpuDataCache,
+ EmuEnableInterrupt,
+ EmuDisableInterrupt,
+ EmuGetInterruptState,
+ EmuInit,
+ EmuRegisterInterruptHandler,
+ EmuGetTimerValue,
+ EmuSetMemoryAttributes,
+ 0,
+ 4
+ },
+ {
+ {
+ CpuMemoryServiceRead,
+ CpuMemoryServiceWrite
+ },
+ {
+ CpuIoServiceRead,
+ CpuIoServiceWrite
+ }
+ },
+ TRUE
+};
+
+#define EFI_CPU_DATA_MAXIMUM_LENGTH 0x100
+
+
+
+//
+// Service routines for the driver
+//
+EFI_STATUS
+EFIAPI
+EmuFlushCpuDataCache (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS Start,
+ IN UINT64 Length,
+ IN EFI_CPU_FLUSH_TYPE FlushType
+ )
+{
+ if (FlushType == EfiCpuFlushTypeWriteBackInvalidate) {
+ //
+ // Only WB flush is supported. We actually need do nothing on Emu emulator
+ // environment. Classify this to follow EFI spec
+ //
+ return EFI_SUCCESS;
+ }
+ //
+ // Other flush types are not supported by Emu emulator
+ //
+ return EFI_UNSUPPORTED;
+}
+
+EFI_STATUS
+EFIAPI
+EmuEnableInterrupt (
+ IN EFI_CPU_ARCH_PROTOCOL *This
+ )
+{
+ CPU_ARCH_PROTOCOL_PRIVATE *Private;
+
+ Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
+ Private->InterruptState = TRUE;
+ gEmuThunk->EnableInterrupt ();
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+EmuDisableInterrupt (
+ IN EFI_CPU_ARCH_PROTOCOL *This
+ )
+{
+ CPU_ARCH_PROTOCOL_PRIVATE *Private;
+
+ Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
+ Private->InterruptState = FALSE;
+ gEmuThunk->DisableInterrupt ();
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+EmuGetInterruptState (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ OUT BOOLEAN *State
+ )
+{
+ CPU_ARCH_PROTOCOL_PRIVATE *Private;
+
+ if (State == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
+ *State = Private->InterruptState;
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+EmuInit (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_CPU_INIT_TYPE InitType
+ )
+{
+ CPU_ARCH_PROTOCOL_PRIVATE *Private;
+
+ Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
+ return EFI_UNSUPPORTED;
+}
+
+EFI_STATUS
+EFIAPI
+EmuRegisterInterruptHandler (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_EXCEPTION_TYPE InterruptType,
+ IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
+ )
+{
+ CPU_ARCH_PROTOCOL_PRIVATE *Private;
+
+ //
+ // Do parameter checking for EFI spec conformance
+ //
+ if (InterruptType < 0 || InterruptType > 0xff) {
+ return EFI_UNSUPPORTED;
+ }
+ //
+ // Do nothing for Emu emulation
+ //
+ Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
+ return EFI_UNSUPPORTED;
+}
+
+EFI_STATUS
+EFIAPI
+EmuGetTimerValue (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN UINT32 TimerIndex,
+ OUT UINT64 *TimerValue,
+ OUT UINT64 *TimerPeriod OPTIONAL
+ )
+{
+ if (TimerValue == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (TimerIndex != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ *TimerValue = gEmuThunk->QueryPerformanceCounter ();
+
+ if (TimerPeriod != NULL) {
+ *TimerPeriod = mTimerPeriod;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+EFIAPI
+EmuSetMemoryAttributes (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes
+ )
+{
+ CPU_ARCH_PROTOCOL_PRIVATE *Private;
+
+ //
+ // Check for invalid parameter for Spec conformance
+ //
+ if (Length == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // Do nothing for Nt32 emulation
+ //
+ Private = CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS (This);
+ return EFI_UNSUPPORTED;
+}
+
+
+
+/**
+ Logs SMBIOS record.
+
+ @param Smbios Pointer to SMBIOS protocol instance.
+ @param Buffer Pointer to the data buffer.
+
+**/
+VOID
+LogSmbiosData (
+ IN EFI_SMBIOS_PROTOCOL *Smbios,
+ IN UINT8 *Buffer
+ )
+{
+ EFI_STATUS Status;
+ EFI_SMBIOS_HANDLE SmbiosHandle;
+
+ SmbiosHandle = 0;
+ Status = Smbios->Add (
+ Smbios,
+ NULL,
+ &SmbiosHandle,
+ (EFI_SMBIOS_TABLE_HEADER*)Buffer
+ );
+ ASSERT_EFI_ERROR (Status);
+}
+
+VOID
+CpuUpdateSmbios (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINT32 TotalSize;
+ EFI_SMBIOS_PROTOCOL *Smbios;
+ EFI_HII_HANDLE HiiHandle;
+ STRING_REF Token;
+ UINTN CpuVerStrLen;
+ EFI_STRING CpuVerStr;
+ SMBIOS_TABLE_TYPE4 *SmbiosRecord;
+ CHAR8 *OptionalStrStart;
+
+ //
+ // Locate Smbios protocol.
+ //
+ Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID **)&Smbios);
+
+ if (EFI_ERROR (Status)) {
+ return;
+ }
+
+ //
+ // Initialize strings to HII database
+ //
+ HiiHandle = HiiAddPackages (
+ &gEfiCallerIdGuid,
+ NULL,
+ CpuStrings,
+ NULL
+ );
+ ASSERT (HiiHandle != NULL);
+
+ Token = STRING_TOKEN (STR_PROCESSOR_VERSION);
+ CpuVerStr = HiiGetPackageString(&gEfiCallerIdGuid, Token, NULL);
+ CpuVerStrLen = StrLen(CpuVerStr);
+ ASSERT (CpuVerStrLen <= SMBIOS_STRING_MAX_LENGTH);
+
+ TotalSize = sizeof(SMBIOS_TABLE_TYPE4) + CpuVerStrLen + 1 + 1;
+ SmbiosRecord = AllocatePool(TotalSize);
+ ZeroMem(SmbiosRecord, TotalSize);
+
+ SmbiosRecord->Hdr.Type = EFI_SMBIOS_TYPE_PROCESSOR_INFORMATION;
+ SmbiosRecord->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE4);
+ //
+ // Make handle chosen by smbios protocol.add automatically.
+ //
+ SmbiosRecord->Hdr.Handle = 0;
+ //
+ // Processor version is the 1st string.
+ //
+ SmbiosRecord->ProcessorVersion = 1;
+ //
+ // Store CPU frequency data record to data hub - It's an emulator so make up a value
+ //
+ SmbiosRecord->CurrentSpeed = 1234;
+
+ OptionalStrStart = (CHAR8 *)(SmbiosRecord + 1);
+ UnicodeStrToAsciiStr(CpuVerStr, OptionalStrStart);
+
+ //
+ // Now we have got the full smbios record, call smbios protocol to add this record.
+ //
+ LogSmbiosData(Smbios, (UINT8 *) SmbiosRecord);
+ FreePool (SmbiosRecord);
+}
+
+
+
+/**
+ Callback function for idle events.
+
+ @param Event Event whose notification function is being invoked.
+ @param Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+IdleLoopEventCallback (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ gEmuThunk->CpuSleep ();
+}
+
+
+EFI_STATUS
+EFIAPI
+InitializeCpu (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ )
+{
+ EFI_STATUS Status;
+ UINT64 Frequency;
+ EFI_EVENT IdleLoopEvent;
+
+ //
+ // Retrieve the frequency of the performance counter in Hz.
+ //
+ Frequency = gEmuThunk->QueryPerformanceFrequency ();
+
+ //
+ // Convert frequency in Hz to a clock period in femtoseconds.
+ //
+ mTimerPeriod = DivU64x64Remainder (1000000000000000ULL, Frequency, NULL);
+
+ CpuUpdateSmbios ();
+
+ CpuMpServicesInit ();
+
+ Status = gBS->CreateEventEx (
+ EVT_NOTIFY_SIGNAL,
+ TPL_NOTIFY,
+ IdleLoopEventCallback,
+ NULL,
+ &gIdleLoopEventGuid,
+ &IdleLoopEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &mCpuTemplate.Handle,
+ &gEfiCpuArchProtocolGuid, &mCpuTemplate.Cpu,
+ &gEfiCpuIo2ProtocolGuid, &mCpuTemplate.CpuIo,
+ NULL
+ );
+ ASSERT_EFI_ERROR (Status);
+
+ return Status;
+}
diff --git a/EmulatorPkg/CpuRuntimeDxe/Cpu.inf b/EmulatorPkg/CpuRuntimeDxe/Cpu.inf
new file mode 100644
index 0000000000..ed8237f916
--- /dev/null
+++ b/EmulatorPkg/CpuRuntimeDxe/Cpu.inf
@@ -0,0 +1,76 @@
+## @file
+# Component description file for Cpu module.
+#
+# This CPU module abstracts the interrupt subsystem of a platform and the CPU-specific setjump-long pair.
+#
+# Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+#
+# This program and the accompanying materials
+# are licensed and made available under the terms and conditions of the BSD License
+# which accompanies this distribution. The full text of the license may be found at
+# http://opensource.org/licenses/bsd-license.php
+#
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+#
+##
+
+[Defines]
+ INF_VERSION = 0x00010005
+ BASE_NAME = Cpu
+ FILE_GUID = f3794b60-8985-11db-8e53-0040d02b1835
+ MODULE_TYPE = DXE_DRIVER
+ VERSION_STRING = 1.0
+
+ ENTRY_POINT = InitializeCpu
+
+#
+# The following information is for reference only and not required by the build tools.
+#
+# VALID_ARCHITECTURES = IA32 X64 IPF EBC
+#
+
+[Sources]
+ CpuIo.c
+ Cpu.c
+ CpuDriver.h
+ Strings.uni
+ MpService.c
+
+
+[Packages]
+ MdePkg/MdePkg.dec
+ MdeModulePkg/MdeModulePkg.dec
+ IntelFrameworkPkg/IntelFrameworkPkg.dec
+ EmulatorPkg/EmulatorPkg.dec
+
+
+[LibraryClasses]
+ UefiBootServicesTableLib
+ MemoryAllocationLib
+ BaseMemoryLib
+ UefiDriverEntryPoint
+ UefiLib
+ HiiLib
+ DebugLib
+ BaseLib
+ EmuThunkLib
+ PcdLib
+
+[Protocols]
+ gEmuIoThunkProtocolGuid # PROTOCOL_NOTIFY SOMETIMES_CONSUMED
+ gEfiSmbiosProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
+ gEfiHiiProtocolGuid # PROTOCOL SOMETIMES_CONSUMED
+ gEfiCpuIo2ProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+ gEfiCpuArchProtocolGuid # PROTOCOL ALWAYS_PRODUCED
+ gEmuThreadThunkProtocolGuid
+ gEfiMpServiceProtocolGuid
+
+[Guids]
+ gIdleLoopEventGuid ## CONSUMES ## GUID
+
+[Pcd]
+ gEmulatorPkgTokenSpaceGuid.PcdEmuMpServicesPollingInterval
+
+[Depex]
+ gEfiSmbiosProtocolGuid
diff --git a/EmulatorPkg/CpuRuntimeDxe/CpuDriver.h b/EmulatorPkg/CpuRuntimeDxe/CpuDriver.h
new file mode 100644
index 0000000000..69505ff0e4
--- /dev/null
+++ b/EmulatorPkg/CpuRuntimeDxe/CpuDriver.h
@@ -0,0 +1,246 @@
+/*++ @file
+
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+Portions copyright (c) 2011, Apple Inc. All rights reserved.
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#ifndef _CPU_ARCHITECTURAL_PROTOCOL_DRIVER_H_
+#define _CPU_ARCHITECTURAL_PROTOCOL_DRIVER_H_
+
+
+#include <FrameworkDxe.h>
+#include <IndustryStandard/SmBios.h>
+
+#include <Protocol/Cpu.h>
+#include <Protocol/Smbios.h>
+#include <Protocol/FrameworkHii.h>
+#include <Protocol/MpService.h>
+#include <Protocol/EmuThread.h>
+#include <Protocol/CpuIo2.h>
+
+#include <Guid/DataHubRecords.h>
+#include <Guid/IdleLoopEvent.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HiiLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/EmuThunkLib.h>
+#include <Library/UefiLib.h>
+#include <Library/PcdLib.h>
+
+
+extern UINT8 CpuStrings[];
+
+//
+// Internal Data Structures
+//
+#define CPU_ARCH_PROT_PRIVATE_SIGNATURE SIGNATURE_32 ('c', 'a', 'p', 'd')
+
+typedef struct {
+ UINTN Signature;
+ EFI_HANDLE Handle;
+
+ EFI_CPU_ARCH_PROTOCOL Cpu;
+ EFI_CPU_IO2_PROTOCOL CpuIo;
+
+ //
+ // Local Data for CPU interface goes here
+ //
+ BOOLEAN InterruptState;
+
+} CPU_ARCH_PROTOCOL_PRIVATE;
+
+#define CPU_ARCH_PROTOCOL_PRIVATE_DATA_FROM_THIS(a) \
+ CR (a, \
+ CPU_ARCH_PROTOCOL_PRIVATE, \
+ Cpu, \
+ CPU_ARCH_PROT_PRIVATE_SIGNATURE \
+ )
+
+
+
+typedef enum {
+ CPU_STATE_IDLE,
+ CPU_STATE_BLOCKED,
+ CPU_STATE_READY,
+ CPU_STATE_BUSY,
+ CPU_STATE_FINISHED
+} PROCESSOR_STATE;
+
+
+//
+// Define Individual Processor Data block.
+//
+typedef struct {
+ EFI_PROCESSOR_INFORMATION Info;
+ EFI_AP_PROCEDURE Procedure;
+ VOID *Parameter;
+ VOID *StateLock;
+ VOID *ProcedureLock;
+ PROCESSOR_STATE State;
+ EFI_EVENT CheckThisAPEvent;
+} PROCESSOR_DATA_BLOCK;
+
+
+//
+// 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;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+ UINTN Timeout;
+ UINTN *FailedList;
+ UINTN FailedListIndex;
+ BOOLEAN TimeoutActive;
+} MP_SYSTEM_DATA;
+
+
+
+
+
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceRead (
+ IN EFI_CPU_IO2_PROTOCOL *This,
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 Address,
+ IN UINTN Count,
+ IN OUT VOID *Buffer
+ );
+
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceWrite (
+ IN EFI_CPU_IO2_PROTOCOL *This,
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 Address,
+ IN UINTN Count,
+ IN OUT VOID *Buffer
+ );
+
+EFI_STATUS
+EFIAPI
+CpuIoServiceRead (
+ IN EFI_CPU_IO2_PROTOCOL *This,
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 UserAddress,
+ IN UINTN Count,
+ IN OUT VOID *UserBuffer
+ );
+
+EFI_STATUS
+EFIAPI
+CpuIoServiceWrite (
+ IN EFI_CPU_IO2_PROTOCOL *This,
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 UserAddress,
+ IN UINTN Count,
+ IN OUT VOID *UserBuffer
+ );
+
+EFI_STATUS
+EFIAPI
+InitializeCpu (
+ IN EFI_HANDLE ImageHandle,
+ IN EFI_SYSTEM_TABLE *SystemTable
+ );
+
+EFI_STATUS
+EFIAPI
+EmuFlushCpuDataCache (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS Start,
+ IN UINT64 Length,
+ IN EFI_CPU_FLUSH_TYPE FlushType
+ );
+
+EFI_STATUS
+EFIAPI
+EmuEnableInterrupt (
+ IN EFI_CPU_ARCH_PROTOCOL *This
+ );
+
+EFI_STATUS
+EFIAPI
+EmuDisableInterrupt (
+ IN EFI_CPU_ARCH_PROTOCOL *This
+ );
+
+EFI_STATUS
+EFIAPI
+EmuGetInterruptState (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ OUT BOOLEAN *State
+ );
+
+EFI_STATUS
+EFIAPI
+EmuInit (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_CPU_INIT_TYPE InitType
+ );
+
+EFI_STATUS
+EFIAPI
+EmuRegisterInterruptHandler (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_EXCEPTION_TYPE InterruptType,
+ IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler
+ );
+
+EFI_STATUS
+EFIAPI
+EmuGetTimerValue (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN UINT32 TimerIndex,
+ OUT UINT64 *TimerValue,
+ OUT UINT64 *TimerPeriod OPTIONAL
+ );
+
+EFI_STATUS
+EFIAPI
+EmuSetMemoryAttributes (
+ IN EFI_CPU_ARCH_PROTOCOL *This,
+ IN EFI_PHYSICAL_ADDRESS BaseAddress,
+ IN UINT64 Length,
+ IN UINT64 Attributes
+ );
+
+EFI_STATUS
+CpuMpServicesInit (
+ VOID
+ );
+
+EFI_STATUS
+EFIAPI
+CpuMpServicesWhoAmI (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *ProcessorNumber
+ );
+
+extern EFI_MP_SERVICES_PROTOCOL mMpSercicesTemplate;
+
+
+#endif
diff --git a/EmulatorPkg/CpuRuntimeDxe/CpuIo.c b/EmulatorPkg/CpuRuntimeDxe/CpuIo.c
new file mode 100644
index 0000000000..6f63375f4f
--- /dev/null
+++ b/EmulatorPkg/CpuRuntimeDxe/CpuIo.c
@@ -0,0 +1,333 @@
+/*++ @file
+ This is the code that publishes the CPU I/O Protocol.
+ The intent herein is to have a single I/O service that can load
+ as early as possible, extend into runtime, and be layered upon by
+ the implementations of architectural protocols and the PCI Root
+ Bridge I/O Protocol.
+
+
+Copyright (c) 2006, Intel Corporation. All rights reserved.<BR>
+Portions copyright (c) 2011, Apple Inc. All rights reserved.
+This program and the accompanying materials
+are licensed and made available under the terms and conditions of the BSD License
+which accompanies this distribution. The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+#include <FrameworkDxe.h>
+#include <Protocol/Cpu.h>
+#include <Protocol/DataHub.h>
+#include <Guid/DataHubRecords.h>
+#include <Protocol/CpuIo2.h>
+#include <Protocol/FrameworkHii.h>
+
+#include <Library/BaseLib.h>
+#include <Library/DebugLib.h>
+#include <Library/HiiLib.h>
+#include <Library/UefiLib.h>
+#include <Library/UefiDriverEntryPoint.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <CpuDriver.h>
+
+#define IA32_MAX_IO_ADDRESS 0xFFFF
+#define IA32_MAX_MEM_ADDRESS 0xFFFFFFFF
+
+EFI_STATUS
+CpuIoCheckAddressRange (
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 Address,
+ IN UINTN Count,
+ IN VOID *Buffer,
+ IN UINT64 Limit
+ );
+
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceRead (
+ IN EFI_CPU_IO2_PROTOCOL *This,
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 Address,
+ IN UINTN Count,
+ IN OUT VOID *Buffer
+ )
+/*++
+
+Routine Description:
+
+ Perform the Memory Access Read service for the CPU I/O Protocol
+
+Arguments:
+
+ Pointer to an instance of the CPU I/O Protocol
+ Width of the Memory Access
+ Address of the Memory access
+ Count of the number of accesses to perform
+ Pointer to the buffer to read or write from memory
+
+Returns:
+
+ Status
+
+ EFI_SUCCESS - The data was read from or written to the EFI
+ System.
+ EFI_INVALID_PARAMETER - Width is invalid for this EFI System.
+ EFI_INVALID_PARAMETER - Buffer is NULL.
+ EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.
+ EFI_UNSUPPORTED - The address range specified by Address, Width,
+ and Count is not valid for this EFI System.
+
+**/
+{
+ EFI_STATUS Status;
+
+ if (!Buffer) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = CpuIoCheckAddressRange (Width, Address, Count, Buffer, IA32_MAX_MEM_ADDRESS);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Do nothing for Nt32 version
+ //
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+CpuMemoryServiceWrite (
+ IN EFI_CPU_IO2_PROTOCOL *This,
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 Address,
+ IN UINTN Count,
+ IN OUT VOID *Buffer
+ )
+/*++
+
+Routine Description:
+
+ Perform the Memory Access Read service for the CPU I/O Protocol
+
+Arguments:
+
+ Pointer to an instance of the CPU I/O Protocol
+ Width of the Memory Access
+ Address of the Memory access
+ Count of the number of accesses to perform
+ Pointer to the buffer to read or write from memory
+
+Returns:
+
+ Status
+
+ EFI_SUCCESS - The data was read from or written to the EFI System.
+ EFI_INVALID_PARAMETER - Width is invalid for this EFI System.
+ EFI_INVALID_PARAMETER - Buffer is NULL.
+ EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.
+ EFI_UNSUPPORTED - The address range specified by Address, Width, and
+ Count is not valid for this EFI System.
+
+**/
+{
+ EFI_STATUS Status;
+
+ if (!Buffer) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = CpuIoCheckAddressRange (Width, Address, Count, Buffer, IA32_MAX_MEM_ADDRESS);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Do nothing for Nt32 version
+ //
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+CpuIoServiceRead (
+ IN EFI_CPU_IO2_PROTOCOL *This,
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 UserAddress,
+ IN UINTN Count,
+ IN OUT VOID *UserBuffer
+ )
+/*++
+
+Routine Description:
+
+ This is the service that implements the I/O read
+
+Arguments:
+
+ Pointer to an instance of the CPU I/O Protocol
+ Width of the Memory Access
+ Address of the I/O access
+ Count of the number of accesses to perform
+ Pointer to the buffer to read or write from I/O space
+
+Returns:
+
+ Status
+ EFI_SUCCESS - The data was read from or written to the EFI System.
+ EFI_INVALID_PARAMETER - Width is invalid for this EFI System.
+ EFI_INVALID_PARAMETER - Buffer is NULL.
+ EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.
+ EFI_UNSUPPORTED - The address range specified by Address, Width, and
+ Count is not valid for this EFI System.
+**/
+{
+ UINTN Address;
+ EFI_STATUS Status;
+
+ if (!UserBuffer) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Address = (UINTN) UserAddress;
+
+ if (Width >= EfiCpuIoWidthMaximum) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = CpuIoCheckAddressRange (Width, Address, Count, UserBuffer, IA32_MAX_IO_ADDRESS);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Do nothing for Nt32 version
+ //
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+EFIAPI
+CpuIoServiceWrite (
+ IN EFI_CPU_IO2_PROTOCOL *This,
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 UserAddress,
+ IN UINTN Count,
+ IN OUT VOID *UserBuffer
+ )
+/*++
+
+Routine Description:
+
+
+ This is the service that implements the I/O Write
+
+Arguments:
+
+ Pointer to an instance of the CPU I/O Protocol
+ Width of the Memory Access
+ Address of the I/O access
+ Count of the number of accesses to perform
+ Pointer to the buffer to read or write from I/O space
+
+Returns:
+
+ Status
+
+ Status
+ EFI_SUCCESS - The data was read from or written to the EFI System.
+ EFI_INVALID_PARAMETER - Width is invalid for this EFI System.
+ EFI_INVALID_PARAMETER - Buffer is NULL.
+ EFI_UNSUPPORTED - The Buffer is not aligned for the given Width.
+ EFI_UNSUPPORTED - The address range specified by Address, Width, and
+ Count is not valid for this EFI System.
+
+**/
+{
+ UINTN Address;
+ EFI_STATUS Status;
+
+ if (!UserBuffer) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Address = (UINTN) UserAddress;
+
+ if (Width >= EfiCpuIoWidthMaximum) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = CpuIoCheckAddressRange (Width, Address, Count, UserBuffer, IA32_MAX_IO_ADDRESS);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Do nothing for Nt32 version
+ //
+ return EFI_SUCCESS;
+}
+
+
+/*++
+
+Routine Description:
+
+Arguments:
+
+ Width - TODO: add argument description
+ Address - TODO: add argument description
+ Count - TODO: add argument description
+ Buffer - TODO: add argument description
+ Limit - TODO: add argument description
+
+Returns:
+
+ EFI_UNSUPPORTED - TODO: Add description for return value
+ EFI_UNSUPPORTED - TODO: Add description for return value
+ EFI_UNSUPPORTED - TODO: Add description for return value
+ EFI_SUCCESS - TODO: Add description for return value
+
+**/
+EFI_STATUS
+CpuIoCheckAddressRange (
+ IN EFI_CPU_IO_PROTOCOL_WIDTH Width,
+ IN UINT64 Address,
+ IN UINTN Count,
+ IN VOID *Buffer,
+ IN UINT64 Limit
+ )
+{
+ UINTN AlignMask;
+
+ if (Address > Limit) {
+ return EFI_UNSUPPORTED;
+ }
+
+ //
+ // For FiFo type, the target address won't increase during the access, so treat count as 1
+ //
+ if (Width >= EfiCpuIoWidthFifoUint8 && Width <= EfiCpuIoWidthFifoUint64) {
+ Count = 1;
+ }
+
+ Width = Width & 0x03;
+ if (Address - 1 + (1 << Width) * Count > Limit) {
+ return EFI_UNSUPPORTED;
+ }
+
+ AlignMask = (1 << Width) - 1;
+ if ((UINTN) Buffer & AlignMask) {
+ return EFI_UNSUPPORTED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+
diff --git a/EmulatorPkg/CpuRuntimeDxe/MpService.c b/EmulatorPkg/CpuRuntimeDxe/MpService.c
new file mode 100644
index 0000000000..7f3b1995f2
--- /dev/null
+++ b/EmulatorPkg/CpuRuntimeDxe/MpService.c
@@ -0,0 +1,1366 @@
+/** @file
+ Construct MP Services Protocol on top of the EMU Thread protocol.
+ This code makes APs show up in the emulator. PcdEmuApCount is the
+ number of APs the emulator should produce.
+
+ The MP Services Protocol provides a generalized way of performing following 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 one 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, maintain,
+ 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 processors, such
+ as MTRR synchronization for memory space attributes setting in DXE Services.
+ MP Services Protocol may be used by non-CPU DXE drivers to speed up platform boot
+ by taking advantage of the processing capabilities of the APs, for example, using
+ APs to help test system memory in parallel with other device initialization.
+ Diagnostics applications may also use this protocol for multi-processor.
+
+Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>
+Portitions Copyright (c) 2011, Apple Inc. All rights reserved.
+This program and the accompanying materials are licensed and made available under
+the terms and conditions of the BSD License that accompanies this distribution.
+The full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php.
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+
+**/
+
+#include "CpuDriver.h"
+
+
+MP_SYSTEM_DATA gMPSystem;
+EMU_THREAD_THUNK_PROTOCOL *gThread = NULL;
+EFI_EVENT gReadToBootEvent;
+BOOLEAN gReadToBoot = FALSE;
+UINTN gPollInterval;
+
+
+BOOLEAN
+IsBSP (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ UINTN ProcessorNumber;
+
+ Status = CpuMpServicesWhoAmI (&mMpSercicesTemplate, &ProcessorNumber);
+ if (EFI_ERROR (Status)) {
+ return FALSE;
+ }
+
+ return (gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0;
+}
+
+
+VOID
+SetApProcedure (
+ IN PROCESSOR_DATA_BLOCK *Processor,
+ IN EFI_AP_PROCEDURE Procedure,
+ IN VOID *ProcedureArgument
+ )
+{
+ gThread->MutexLock (Processor->ProcedureLock);
+ Processor->Parameter = ProcedureArgument;
+ Processor->Procedure = Procedure;
+ gThread->MutexUnlock (Processor->ProcedureLock);
+}
+
+
+EFI_STATUS
+GetNextBlockedNumber (
+ OUT UINTN *NextNumber
+ )
+{
+ UINTN Number;
+ PROCESSOR_STATE ProcessorState;
+ PROCESSOR_DATA_BLOCK *Data;
+
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+ Data = &gMPSystem.ProcessorData[Number];
+ if ((Data->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ // Skip BSP
+ continue;
+ }
+
+ gThread->MutexLock (Data->StateLock);
+ ProcessorState = Data->State;
+ gThread->MutexUnlock (Data->StateLock);
+
+ if (ProcessorState == CPU_STATE_BLOCKED) {
+ *NextNumber = Number;
+ return EFI_SUCCESS;
+ }
+ }
+
+ return EFI_NOT_FOUND;
+}
+
+
+
+
+/**
+ 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 processors
+ 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 processors
+ is returned in NumberOfProcessors, the number of currently enabled processor
+ is returned in NumberOfEnabledProcessors, and EFI_SUCCESS is returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[out] NumberOfProcessors Pointer to the total number of logical
+ processors in the system, including the BSP
+ and disabled APs.
+ @param[out] NumberOfEnabledProcessors Pointer to the number of enabled logical
+ processors that exist in system, including
+ the BSP.
+
+ @retval EFI_SUCCESS The number of logical processors and enabled
+ 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.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesGetNumberOfProcessors (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *NumberOfProcessors,
+ OUT UINTN *NumberOfEnabledProcessors
+ )
+{
+ if ((NumberOfProcessors == NULL) || (NumberOfEnabledProcessors == NULL)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ *NumberOfProcessors = gMPSystem.NumberOfProcessors;
+ *NumberOfEnabledProcessors = gMPSystem.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 processor
+ on the platform. Note the following:
+ - The processor information may change during the course of a boot session.
+ - 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 and is not provided
+ by this service.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ instance.
+ @param[in] ProcessorNumber The handle number of processor.
+ @param[out] ProcessorInfoBuffer A pointer to the buffer where information for
+ the requested processor is deposited.
+
+ @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 platform.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesGetProcessorInfo (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ OUT EFI_PROCESSOR_INFORMATION *ProcessorInfoBuffer
+ )
+{
+ if (ProcessorInfoBuffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ CopyMem (ProcessorInfoBuffer, &gMPSystem.ProcessorData[ProcessorNumber], 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 supports
+ both blocking and non-blocking requests. The non-blocking requests use EFI
+ events so the BSP can detect when the APs have finished. This service may 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 specified by
+ Procedure one by one, in ascending order of processor handle number. Otherwise,
+ all the enabled APs execute the function specified by Procedure 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 Procedure 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 controlled. 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 certain 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-blocking
+ mode creates WaitEvent by calling the EFI CreateEvent() service. The caller
+ invokes EFI_MP_SERVICES_PROTOCOL.StartupAllAPs(). If the parameter 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 tasks.
+ -# 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() function. If
+ FailedCpuList is not NULL, its content is available when WaitEvent 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 failed
+ APs. The buffer is allocated by MP Service Protocol using AllocatePool().
+ It is the caller's responsibility to free the buffer with FreePool() service.
+ -# This invocation of SignalEvent() function informs the caller that invoked
+ 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 complete the specified task
+ prior to the timeout.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL
+ 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 execute
+ the function specified by Procedure one by
+ one, in ascending order of processor handle
+ number. If FALSE, then all the enabled APs
+ execute the function specified by Procedure
+ simultaneously.
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure, or TimeoutInMicroseconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ 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.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ 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 successfully, then its
+ content is set to NULL. If not all APs
+ finish before timeout expires, then its
+ content is set to address of 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 with 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 failed CPU is terminated by
+ END_OF_CPU_LIST.
+
+ @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 after 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 before
+ all enabled APs have finished.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesStartupAllAps (
+ 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;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+ UINTN ListIndex;
+ UINTN Number;
+ UINTN NextNumber;
+ PROCESSOR_STATE APInitialState;
+ PROCESSOR_STATE ProcessorState;
+ INTN Timeout;
+
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (gMPSystem.NumberOfProcessors == 1) {
+ return EFI_NOT_STARTED;
+ }
+
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((WaitEvent != NULL) && gReadToBoot) {
+ return EFI_UNSUPPORTED;
+ }
+
+
+ if (FailedCpuList != NULL) {
+ gMPSystem.FailedList = AllocatePool ((gMPSystem.NumberOfProcessors + 1) * sizeof (UINTN));
+ if (gMPSystem.FailedList == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ SetMemN (gMPSystem.FailedList, (gMPSystem.NumberOfProcessors + 1) * sizeof (UINTN), END_OF_CPU_LIST);
+ gMPSystem.FailedListIndex = 0;
+ *FailedCpuList = gMPSystem.FailedList;
+ }
+
+ Timeout = TimeoutInMicroseconds;
+
+ ListIndex = 0;
+ ProcessorData = NULL;
+
+ gMPSystem.FinishCount = 0;
+ gMPSystem.StartCount = 0;
+ gMPSystem.SingleThread = SingleThread;
+ APInitialState = CPU_STATE_READY;
+
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+ ProcessorData = &gMPSystem.ProcessorData[Number];
+
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+ // Skip Disabled processors
+ gMPSystem.FailedList[gMPSystem.FailedListIndex++] = Number;
+ continue;
+ }
+
+ //
+ // Get APs prepared, and put failing APs into FailedCpuList
+ // if "SingleThread", only 1 AP will put to ready state, other AP will be put to ready
+ // state 1 by 1, until the previous 1 finished its task
+ // if not "SingleThread", all APs are put to ready state from the beginning
+ //
+ if (ProcessorData->State == CPU_STATE_IDLE) {
+ gMPSystem.StartCount++;
+
+ gThread->MutexLock (&ProcessorData->StateLock);
+ ProcessorData->State = APInitialState;
+ gThread->MutexUnlock (&ProcessorData->StateLock);
+
+ if (SingleThread) {
+ APInitialState = CPU_STATE_BLOCKED;
+ }
+ } else {
+ return EFI_NOT_READY;
+ }
+ }
+
+ if (WaitEvent != NULL) {
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+ ProcessorData = &gMPSystem.ProcessorData[Number];
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+ // Skip Disabled processors
+ continue;
+ }
+
+ SetApProcedure (ProcessorData, Procedure, ProcedureArgument);
+ }
+
+ //
+ // Save data into private data structure, and create timer to poll AP state before exiting
+ //
+ gMPSystem.Procedure = Procedure;
+ gMPSystem.ProcedureArgument = ProcedureArgument;
+ gMPSystem.WaitEvent = WaitEvent;
+ gMPSystem.Timeout = TimeoutInMicroseconds;
+ gMPSystem.TimeoutActive = (BOOLEAN)(TimeoutInMicroseconds != 0);
+ Status = gBS->SetTimer (
+ gMPSystem.CheckAllAPsEvent,
+ TimerPeriodic,
+ gPollInterval
+ );
+ return Status;
+
+ }
+
+ while (TRUE) {
+ for (Number = 0; Number < gMPSystem.NumberOfProcessors; Number++) {
+ ProcessorData = &gMPSystem.ProcessorData[Number];
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+ // Skip Disabled processors
+ continue;
+ }
+
+ gThread->MutexLock (ProcessorData->StateLock);
+ ProcessorState = ProcessorData->State;
+ gThread->MutexUnlock (ProcessorData->StateLock);
+
+ switch (ProcessorState) {
+ case CPU_STATE_READY:
+ SetApProcedure (ProcessorData, Procedure, ProcedureArgument);
+ break;
+
+ case CPU_STATE_FINISHED:
+ gMPSystem.FinishCount++;
+ if (SingleThread) {
+ Status = GetNextBlockedNumber (&NextNumber);
+ if (!EFI_ERROR (Status)) {
+ gMPSystem.ProcessorData[NextNumber].State = CPU_STATE_READY;
+ }
+ }
+
+ ProcessorData->State = CPU_STATE_IDLE;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (gMPSystem.FinishCount == gMPSystem.StartCount) {
+ Status = EFI_SUCCESS;
+ goto Done;
+ }
+
+ if ((TimeoutInMicroseconds != 0) && (Timeout < 0)) {
+ Status = EFI_TIMEOUT;
+ goto Done;
+ }
+
+ gBS->Stall (gPollInterval);
+ Timeout -= gPollInterval;
+ }
+
+Done:
+ if (FailedCpuList != NULL) {
+ if (gMPSystem.FailedListIndex == 0) {
+ FreePool (*FailedCpuList);
+ *FailedCpuList = NULL;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This service lets the caller get one enabled AP to execute a caller-provided
+ function. The caller can request the BSP to either wait for the completion
+ of the AP or just proceed with the next task by using the EFI event mechanism.
+ See EFI_MP_SERVICES_PROTOCOL.StartupAllAPs() for more details on non-blocking
+ execution support. This service may only be called from the BSP.
+
+ This function is used to dispatch one enabled AP to the function specified by
+ Procedure passing in the argument specified by ProcedureArgument. If WaitEvent
+ is NULL, execution is in blocking mode. The BSP waits until the AP finishes or
+ TimeoutInMicroSecondss expires. Otherwise, execution is in non-blocking mode.
+ BSP proceeds to the next task without waiting for the AP. 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 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_PROTOCOL
+ 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 range 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.GetNumberOfProcessors().
+ @param[in] WaitEvent The event created by the caller with CreateEvent()
+ service. If it is NULL, then execute in
+ blocking mode. BSP waits until all APs finish
+ or TimeoutInMicroseconds expires. If it's
+ not NULL, then execute in non-blocking mode.
+ BSP requests the function specified by
+ Procedure to be started on all the enabled
+ APs, and go on executing immediately. If
+ all return from Procedure or TimeoutInMicroseconds
+ expires, this event is signaled. The BSP
+ can use the CheckEvent() or WaitForEvent()
+ services to check the state of event. Type
+ EFI_EVENT is defined in CreateEvent() in
+ the Unified Extensible Firmware Interface
+ Specification.
+ @param[in] TimeoutInMicrosecsond Indicates the time limit in microseconds for
+ APs to return from Procedure, either for
+ blocking or non-blocking mode. Zero means
+ infinity. If the timeout expires before
+ 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.StartupAllAPs()
+ or EFI_MP_SERVICES_PROTOCOL.StartupThisAP().
+ If the timeout expires in blocking mode,
+ BSP returns EFI_TIMEOUT. If the timeout
+ expires in non-blocking mode, WaitEvent
+ 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 ignored.
+ In non-blocking mode, if AP returns from
+ Procedure before the timeout expires, its
+ content is set to TRUE. Otherwise, the
+ value is set to FALSE. The caller can
+ determine if the AP returned from Procedure
+ 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 been
+ dispatched to specified AP.
+ @retval EFI_UNSUPPORTED A non-blocking mode request was made after 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 before
+ 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 disabled AP.
+ @retval EFI_INVALID_PARAMETER Procedure is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesStartupThisAP (
+ 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;
+ INTN Timeout;
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (Procedure == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
+ return EFI_NOT_READY;
+ }
+
+ if ((WaitEvent != NULL) && gReadToBoot) {
+ return EFI_UNSUPPORTED;
+ }
+
+ Timeout = TimeoutInMicroseconds;
+
+ gMPSystem.StartCount = 1;
+ gMPSystem.FinishCount = 0;
+
+ SetApProcedure (&gMPSystem.ProcessorData[ProcessorNumber], Procedure, ProcedureArgument);
+
+ if (WaitEvent != NULL) {
+ // Non Blocking
+ gMPSystem.WaitEvent = WaitEvent;
+ Status = gBS->SetTimer (
+ gMPSystem.ProcessorData[ProcessorNumber].CheckThisAPEvent,
+ TimerPeriodic,
+ gPollInterval
+ );
+ return EFI_SUCCESS;
+ }
+
+ // Blocking
+ while (TRUE) {
+ gThread->MutexLock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+ if (gMPSystem.ProcessorData[ProcessorNumber].State == CPU_STATE_FINISHED) {
+ gMPSystem.ProcessorData[ProcessorNumber].State = CPU_STATE_IDLE;
+ gThread->MutexUnlock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+ break;
+ }
+
+ gThread->MutexUnlock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+ if ((TimeoutInMicroseconds != 0) && (Timeout < 0)) {
+ return EFI_TIMEOUT;
+ }
+
+ gBS->Stall (gPollInterval);
+ Timeout -= gPollInterval;
+ }
+
+ return EFI_SUCCESS;
+
+}
+
+
+/**
+ This service switches the requested AP to be the BSP from that point onward.
+ 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 onward.
+ 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_GROUP_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 instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range 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.GetNumberOfProcessors().
+ @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 prior 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 BSP or
+ a disabled AP.
+ @retval EFI_NOT_READY The specified AP is busy.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesSwitchBSP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableOldBSP
+ )
+{
+ UINTN Index;
+
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
+ if ((gMPSystem.ProcessorData[Index].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ break;
+ }
+ }
+ ASSERT (Index != gMPSystem.NumberOfProcessors);
+
+ if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
+ return EFI_NOT_READY;
+ }
+
+ // Skip for now as we need switch a bunch of stack stuff around and it's complex
+ // May not be worth it?
+ return EFI_NOT_READY;
+}
+
+
+/**
+ This service lets the caller enable or disable an AP from this point onward.
+ This service may only be called from the BSP.
+
+ This service allows the caller enable or disable an AP from this point onward.
+ 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 implementation
+ dependent. If an AP is enabled, then the implementation must guarantee that 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 return
+ from this service, then EFI_UNSUPPORTED must be returned.
+
+ @param[in] This A pointer to the EFI_MP_SERVICES_PROTOCOL instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range 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.GetNumberOfProcessors().
+ @param[in] EnableAP Specifies the new state for the processor for
+ enabled, FALSE for disabled.
+ @param[in] HealthFlag If not NULL, a pointer to a value that specifies
+ 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. All other
+ bits are ignored. If it is NULL, this parameter
+ is ignored.
+
+ @retval EFI_SUCCESS The specified AP was enabled or disabled successfully.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP cannot be completed
+ prior to this service returning.
+ @retval EFI_UNSUPPORTED Enabling or disabling an AP is not supported.
+ @retval EFI_DEVICE_ERROR The calling processor is an AP.
+ @retval EFI_NOT_FOUND Processor with the handle specified by ProcessorNumber
+ does not exist.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber specifies the BSP.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesEnableDisableAP (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ IN UINTN ProcessorNumber,
+ IN BOOLEAN EnableAP,
+ IN UINT32 *HealthFlag OPTIONAL
+ )
+{
+ if (!IsBSP ()) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (ProcessorNumber >= gMPSystem.NumberOfProcessors) {
+ return EFI_NOT_FOUND;
+ }
+
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) != 0) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (gMPSystem.ProcessorData[ProcessorNumber].State != CPU_STATE_IDLE) {
+ return EFI_UNSUPPORTED;
+ }
+
+ gThread->MutexLock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+ if (EnableAP) {
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0 ) {
+ gMPSystem.NumberOfEnabledProcessors++;
+ }
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= PROCESSOR_ENABLED_BIT;
+ } else {
+ if ((gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_ENABLED_BIT) == PROCESSOR_ENABLED_BIT ) {
+ gMPSystem.NumberOfEnabledProcessors--;
+ }
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= ~PROCESSOR_ENABLED_BIT;
+ }
+
+ if (HealthFlag != NULL) {
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag &= ~PROCESSOR_HEALTH_STATUS_BIT;
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= (*HealthFlag & PROCESSOR_HEALTH_STATUS_BIT);
+ }
+
+ gThread->MutexUnlock (&gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+ return EFI_SUCCESS;
+}
+
+
+/**
+ This return the handle number for the calling processor. This service may be
+ called from the BSP and APs.
+
+ This service returns the processor handle number for the calling processor.
+ 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 retrieved
+ with EFI_MP_SERVICES_PROTOCOL.GetNumberOfProcessors(). This service may be
+ called from the BSP and APs. If ProcessorNumber is NULL, then EFI_INVALID_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 instance.
+ @param[in] ProcessorNumber The handle number of AP that is to become the new
+ BSP. The range 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.GetNumberOfProcessors().
+
+ @retval EFI_SUCCESS The current processor handle number was returned
+ in ProcessorNumber.
+ @retval EFI_INVALID_PARAMETER ProcessorNumber is NULL.
+
+**/
+EFI_STATUS
+EFIAPI
+CpuMpServicesWhoAmI (
+ IN EFI_MP_SERVICES_PROTOCOL *This,
+ OUT UINTN *ProcessorNumber
+ )
+{
+ UINTN Index;
+ UINT64 ProcessorId;
+
+ if (ProcessorNumber == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ ProcessorId = gThread->Self ();
+ for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
+ if (gMPSystem.ProcessorData[Index].Info.ProcessorId == ProcessorId) {
+ break;
+ }
+ }
+
+ *ProcessorNumber = Index;
+ return EFI_SUCCESS;
+}
+
+
+
+EFI_MP_SERVICES_PROTOCOL mMpSercicesTemplate = {
+ CpuMpServicesGetNumberOfProcessors,
+ CpuMpServicesGetProcessorInfo,
+ CpuMpServicesStartupAllAps,
+ CpuMpServicesStartupThisAP,
+ CpuMpServicesSwitchBSP,
+ CpuMpServicesEnableDisableAP,
+ CpuMpServicesWhoAmI
+};
+
+
+
+/*++
+ If timeout occurs in StartupAllAps(), a timer is set, which invokes this
+ procedure periodically to check whether all APs have finished.
+
+
+--*/
+VOID
+EFIAPI
+CpuCheckAllAPsStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ UINTN ProcessorNumber;
+ UINTN NextNumber;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+ PROCESSOR_DATA_BLOCK *NextData;
+ EFI_STATUS Status;
+ PROCESSOR_STATE ProcessorState;
+ UINTN Cpu;
+ BOOLEAN Found;
+
+ if (gMPSystem.TimeoutActive) {
+ gMPSystem.Timeout -= gPollInterval;
+ }
+
+ ProcessorData = (PROCESSOR_DATA_BLOCK *) Context;
+
+ for (ProcessorNumber = 0; ProcessorNumber < gMPSystem.NumberOfProcessors; ProcessorNumber++) {
+ if ((ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+ // Skip Disabled processors
+ continue;
+ }
+
+ // This is an Interrupt Service routine.
+ // This can grab a lock that is held in a non-interrupt
+ // context. Meaning deadlock. Which is a bad thing.
+ // So, try lock it. If we can get it, cool, do our thing.
+ // otherwise, just dump out & try again on the next iteration.
+ Status = gThread->MutexTryLock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+ if (EFI_ERROR(Status)) {
+ return;
+ }
+ ProcessorState = gMPSystem.ProcessorData[ProcessorNumber].State;
+ gThread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+ switch (ProcessorState) {
+ case CPU_STATE_READY:
+ SetApProcedure (ProcessorData, gMPSystem.Procedure, gMPSystem.ProcedureArgument);
+ break;
+
+ case CPU_STATE_FINISHED:
+ if (gMPSystem.SingleThread) {
+ Status = GetNextBlockedNumber (&NextNumber);
+ if (!EFI_ERROR (Status)) {
+ NextData = &gMPSystem.ProcessorData[NextNumber];
+
+ gThread->MutexLock (&NextData->ProcedureLock);
+ NextData->State = CPU_STATE_READY;
+ gThread->MutexUnlock (&NextData->ProcedureLock);
+
+ SetApProcedure (NextData, gMPSystem.Procedure, gMPSystem.ProcedureArgument);
+ }
+ }
+
+ gMPSystem.ProcessorData[ProcessorNumber].State = CPU_STATE_IDLE;
+ gMPSystem.FinishCount++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (gMPSystem.TimeoutActive && gMPSystem.Timeout < 0) {
+ //
+ // Timeout
+ //
+ if (gMPSystem.FailedList != NULL) {
+ for (ProcessorNumber = 0; ProcessorNumber < gMPSystem.NumberOfProcessors; ProcessorNumber++) {
+ if ((ProcessorData[ProcessorNumber].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ if ((ProcessorData->Info.StatusFlag & PROCESSOR_ENABLED_BIT) == 0) {
+ // Skip Disabled processors
+ continue;
+ }
+
+ // Mark the
+ Status = gThread->MutexTryLock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+ if (EFI_ERROR(Status)) {
+ return;
+ }
+ ProcessorState = gMPSystem.ProcessorData[ProcessorNumber].State;
+ gThread->MutexUnlock (gMPSystem.ProcessorData[ProcessorNumber].StateLock);
+
+ if (ProcessorState != CPU_STATE_IDLE) {
+ // If we are retrying make sure we don't double count
+ for (Cpu = 0, Found = FALSE; Cpu < gMPSystem.NumberOfProcessors; Cpu++) {
+ if (gMPSystem.FailedList[Cpu] == END_OF_CPU_LIST) {
+ break;
+ }
+ if (gMPSystem.FailedList[ProcessorNumber] == Cpu) {
+ Found = TRUE;
+ break;
+ }
+ }
+ if (!Found) {
+ gMPSystem.FailedList[gMPSystem.FailedListIndex++] = Cpu;
+ }
+ }
+ }
+ }
+ // Force terminal exit
+ gMPSystem.FinishCount = gMPSystem.StartCount;
+ }
+
+ if (gMPSystem.FinishCount != gMPSystem.StartCount) {
+ return;
+ }
+
+ gBS->SetTimer (
+ gMPSystem.CheckAllAPsEvent,
+ TimerCancel,
+ 0
+ );
+
+ if (gMPSystem.FailedListIndex == 0) {
+ if (gMPSystem.FailedList != NULL) {
+ FreePool (gMPSystem.FailedList);
+ gMPSystem.FailedList = NULL;
+ }
+ }
+
+ Status = gBS->SignalEvent (gMPSystem.WaitEvent);
+
+ return ;
+}
+
+VOID
+EFIAPI
+CpuCheckThisAPStatus (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ EFI_STATUS Status;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+ PROCESSOR_STATE ProcessorState;
+
+ ProcessorData = (PROCESSOR_DATA_BLOCK *) Context;
+
+ //
+ // This is an Interrupt Service routine.
+ // that can grab a lock that is held in a non-interrupt
+ // context. Meaning deadlock. Which is a badddd thing.
+ // So, try lock it. If we can get it, cool, do our thing.
+ // otherwise, just dump out & try again on the next iteration.
+ //
+ Status = gThread->MutexTryLock (ProcessorData->StateLock);
+ if (EFI_ERROR(Status)) {
+ return;
+ }
+ ProcessorState = ProcessorData->State;
+ gThread->MutexUnlock (ProcessorData->StateLock);
+
+ if (ProcessorState == CPU_STATE_FINISHED) {
+ Status = gBS->SetTimer (ProcessorData->CheckThisAPEvent, TimerCancel, 0);
+ ASSERT_EFI_ERROR (Status);
+
+ Status = gBS->SignalEvent (gMPSystem.WaitEvent);
+ ASSERT_EFI_ERROR (Status);
+
+ gThread->MutexLock (ProcessorData->StateLock);
+ ProcessorData->State = CPU_STATE_IDLE;
+ gThread->MutexUnlock (ProcessorData->StateLock);
+ }
+
+ return ;
+}
+
+
+/*++
+ This function is called by all processors (both BSP and AP) once and collects MP related data
+
+ MPSystemData - Pointer to the data structure containing MP related data
+ BSP - TRUE if the CPU is BSP
+
+ EFI_SUCCESS - Data for the processor collected and filled in
+
+--*/
+EFI_STATUS
+FillInProcessorInformation (
+ IN BOOLEAN BSP,
+ IN UINTN ProcessorNumber
+ )
+{
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+
+ ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
+
+ gMPSystem.ProcessorData[ProcessorNumber].Info.ProcessorId = gThread->Self ();
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag = PROCESSOR_ENABLED_BIT | PROCESSOR_HEALTH_STATUS_BIT;
+ if (BSP) {
+ gMPSystem.ProcessorData[ProcessorNumber].Info.StatusFlag |= PROCESSOR_AS_BSP_BIT;
+ }
+
+ gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Package = ProcessorNumber;
+ gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Core = 0;
+ gMPSystem.ProcessorData[ProcessorNumber].Info.Location.Thread = 0;
+ gMPSystem.ProcessorData[ProcessorNumber].State = BSP ? CPU_STATE_BUSY : CPU_STATE_IDLE;
+
+ gMPSystem.ProcessorData[ProcessorNumber].Procedure = NULL;
+ gMPSystem.ProcessorData[ProcessorNumber].Parameter = NULL;
+ gMPSystem.ProcessorData[ProcessorNumber].StateLock = gThread->MutexInit ();
+ gMPSystem.ProcessorData[ProcessorNumber].ProcedureLock = gThread->MutexInit ();
+
+ return EFI_SUCCESS;
+}
+
+VOID *
+EFIAPI
+CpuDriverApIdolLoop (
+ VOID *Context
+ )
+{
+ EFI_AP_PROCEDURE Procedure;
+ VOID *Parameter;
+ UINTN ProcessorNumber;
+ PROCESSOR_DATA_BLOCK *ProcessorData;
+
+ ProcessorNumber = (UINTN)Context;
+ ProcessorData = &gMPSystem.ProcessorData[ProcessorNumber];
+
+ ProcessorData->Info.ProcessorId = gThread->Self ();
+
+ while (TRUE) {
+ //
+ // Make a local copy on the stack to be extra safe
+ //
+ gThread->MutexLock (ProcessorData->ProcedureLock);
+ Procedure = ProcessorData->Procedure;
+ Parameter = ProcessorData->Parameter;
+ gThread->MutexUnlock (ProcessorData->ProcedureLock);
+
+ if (Procedure != NULL) {
+ gThread->MutexLock (ProcessorData->StateLock);
+ ProcessorData->State = CPU_STATE_BUSY;
+ gThread->MutexUnlock (ProcessorData->StateLock);
+
+ Procedure (Parameter);
+
+ gThread->MutexLock (ProcessorData->ProcedureLock);
+ ProcessorData->Procedure = NULL;
+ gThread->MutexUnlock (ProcessorData->ProcedureLock);
+
+ gThread->MutexLock (ProcessorData->StateLock);
+ ProcessorData->State = CPU_STATE_FINISHED;
+ gThread->MutexUnlock (ProcessorData->StateLock);
+ }
+
+ // Poll 5 times a seconds, 200ms
+ // Don't want to burn too many system resources doing nothing.
+ gEmuThunk->Sleep (200 * 1000);
+ }
+
+ return 0;
+}
+
+
+EFI_STATUS
+InitializeMpSystemData (
+ IN UINTN NumberOfProcessors
+ )
+{
+ EFI_STATUS Status;
+ UINTN Index;
+
+
+ //
+ // Clear the data structure area first.
+ //
+ ZeroMem (&gMPSystem, sizeof (MP_SYSTEM_DATA));
+
+ //
+ // First BSP fills and inits all known values, including it's own records.
+ //
+ gMPSystem.NumberOfProcessors = NumberOfProcessors;
+ gMPSystem.NumberOfEnabledProcessors = NumberOfProcessors;
+
+ gMPSystem.ProcessorData = AllocateZeroPool (gMPSystem.NumberOfProcessors * sizeof (PROCESSOR_DATA_BLOCK));
+ ASSERT (gMPSystem.ProcessorData != NULL);
+
+ FillInProcessorInformation (TRUE, 0);
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ CpuCheckAllAPsStatus,
+ NULL,
+ &gMPSystem.CheckAllAPsEvent
+ );
+ ASSERT_EFI_ERROR (Status);
+
+
+ for (Index = 0; Index < gMPSystem.NumberOfProcessors; Index++) {
+ if ((gMPSystem.ProcessorData[Index].Info.StatusFlag & PROCESSOR_AS_BSP_BIT) == PROCESSOR_AS_BSP_BIT) {
+ // Skip BSP
+ continue;
+ }
+
+ FillInProcessorInformation (FALSE, Index);
+
+ Status = gThread->CreateThread (
+ (VOID *)&gMPSystem.ProcessorData[Index].Info.ProcessorId,
+ NULL,
+ CpuDriverApIdolLoop,
+ (VOID *)Index
+ );
+
+
+ Status = gBS->CreateEvent (
+ EVT_TIMER | EVT_NOTIFY_SIGNAL,
+ TPL_CALLBACK,
+ CpuCheckThisAPStatus,
+ (VOID *) &gMPSystem.ProcessorData[Index],
+ &gMPSystem.ProcessorData[Index].CheckThisAPEvent
+ );
+ }
+
+ return EFI_SUCCESS;
+}
+
+
+
+/**
+ Invoke a notification event
+
+ @param Event Event whose notification function is being invoked.
+ @param Context The pointer to the notification function's context,
+ which is implementation-dependent.
+
+**/
+VOID
+EFIAPI
+CpuReadToBootFunction (
+ IN EFI_EVENT Event,
+ IN VOID *Context
+ )
+{
+ gReadToBoot = TRUE;
+}
+
+
+
+EFI_STATUS
+CpuMpServicesInit (
+ VOID
+ )
+{
+ EFI_STATUS Status;
+ EFI_HANDLE Handle;
+ EMU_IO_THUNK_PROTOCOL *IoThunk;
+ UINTN MaxCpus;
+
+ MaxCpus = 1; // BSP
+
+ IoThunk = GetIoThunkInstance (&gEmuThreadThunkProtocolGuid, 0);
+ if (IoThunk != NULL) {
+ Status = IoThunk->Open (IoThunk);
+ if (!EFI_ERROR (Status)) {
+ if (IoThunk->ConfigString != NULL) {
+ MaxCpus += StrDecimalToUintn (IoThunk->ConfigString);
+ gThread = IoThunk->Interface;
+ }
+ }
+ }
+
+ if (MaxCpus == 1) {
+ // We are not MP so nothing to do
+ return EFI_SUCCESS;
+ }
+
+ gPollInterval = PcdGet64 (PcdEmuMpServicesPollingInterval);
+
+ Status = InitializeMpSystemData (MaxCpus);
+ if (EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ Status = EfiCreateEventReadyToBootEx (TPL_CALLBACK, CpuReadToBootFunction, NULL, &gReadToBootEvent);
+ ASSERT_EFI_ERROR (Status);
+
+ //
+ // Now install the MP services protocol.
+ //
+ Handle = NULL;
+ Status = gBS->InstallMultipleProtocolInterfaces (
+ &Handle,
+ &gEfiMpServiceProtocolGuid, &mMpSercicesTemplate,
+ NULL
+ );
+ return Status;
+}
+
+
diff --git a/EmulatorPkg/CpuRuntimeDxe/Strings.uni b/EmulatorPkg/CpuRuntimeDxe/Strings.uni
new file mode 100644
index 0000000000..c8a226e038
--- /dev/null
+++ b/EmulatorPkg/CpuRuntimeDxe/Strings.uni
Binary files differ