From bb89ec1a7ec2f8d35033df9e47b3604925da3bd3 Mon Sep 17 00:00:00 2001 From: jljusten Date: Tue, 28 Jun 2011 16:47:23 +0000 Subject: 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 --- EmulatorPkg/CpuRuntimeDxe/Cpu.c | 374 +++++++++ EmulatorPkg/CpuRuntimeDxe/Cpu.inf | 76 ++ EmulatorPkg/CpuRuntimeDxe/CpuDriver.h | 246 ++++++ EmulatorPkg/CpuRuntimeDxe/CpuIo.c | 333 ++++++++ EmulatorPkg/CpuRuntimeDxe/MpService.c | 1366 +++++++++++++++++++++++++++++++++ EmulatorPkg/CpuRuntimeDxe/Strings.uni | Bin 0 -> 2144 bytes 6 files changed, 2395 insertions(+) create mode 100644 EmulatorPkg/CpuRuntimeDxe/Cpu.c create mode 100644 EmulatorPkg/CpuRuntimeDxe/Cpu.inf create mode 100644 EmulatorPkg/CpuRuntimeDxe/CpuDriver.h create mode 100644 EmulatorPkg/CpuRuntimeDxe/CpuIo.c create mode 100644 EmulatorPkg/CpuRuntimeDxe/MpService.c create mode 100644 EmulatorPkg/CpuRuntimeDxe/Strings.uni (limited to 'EmulatorPkg/CpuRuntimeDxe') 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.
+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.
+# +# 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.
+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 +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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.
+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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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.
+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 Binary files /dev/null and b/EmulatorPkg/CpuRuntimeDxe/Strings.uni differ -- cgit v1.2.3