/** @file CPU Features Initialize functions. Copyright (c) 2017, 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. **/ #include "RegisterCpuFeatures.h" /** Worker function to save PcdCpuFeaturesCapability. @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer */ VOID SetCapabilityPcd ( IN UINT8 *SupportedFeatureMask ) { EFI_STATUS Status; UINTN BitMaskSize; BitMaskSize = PcdGetSize (PcdCpuFeaturesCapability); Status = PcdSetPtrS (PcdCpuFeaturesCapability, &BitMaskSize, SupportedFeatureMask); ASSERT_EFI_ERROR (Status); } /** Worker function to save PcdCpuFeaturesSetting. @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer **/ VOID SetSettingPcd ( IN UINT8 *SupportedFeatureMask ) { EFI_STATUS Status; UINTN BitMaskSize; BitMaskSize = PcdGetSize (PcdCpuFeaturesSetting); Status = PcdSetPtrS (PcdCpuFeaturesSetting, &BitMaskSize, SupportedFeatureMask); ASSERT_EFI_ERROR (Status); } /** Worker function to get PcdCpuFeaturesSupport. @return The pointer to CPU feature bits mask buffer. **/ UINT8 * GetSupportPcds ( VOID ) { UINTN BitMaskSize; UINT8 *SupportBitMask; BitMaskSize = PcdGetSize (PcdCpuFeaturesSupport); SupportBitMask = AllocateZeroPool (BitMaskSize); SupportBitMask = (UINT8 *) PcdGetPtr (PcdCpuFeaturesSupport); return SupportBitMask; } /** Worker function to get PcdCpuFeaturesUserConfiguration. @return The pointer to CPU feature bits mask buffer. **/ UINT8 * GetConfigurationPcds ( VOID ) { UINTN BitMaskSize; UINT8 *SupportBitMask; BitMaskSize = PcdGetSize (PcdCpuFeaturesUserConfiguration); SupportBitMask = AllocateZeroPool (BitMaskSize); SupportBitMask = (UINT8 *) PcdGetPtr (PcdCpuFeaturesUserConfiguration); return SupportBitMask; } /** Collects CPU type and feature information. @param[in, out] CpuInfo The pointer to CPU feature information **/ VOID FillProcessorInfo ( IN OUT REGISTER_CPU_FEATURE_INFORMATION *CpuInfo ) { CPUID_VERSION_INFO_EAX Eax; CPUID_VERSION_INFO_ECX Ecx; CPUID_VERSION_INFO_EDX Edx; UINT32 DisplayedFamily; UINT32 DisplayedModel; AsmCpuid (CPUID_VERSION_INFO, &Eax.Uint32, NULL, &Ecx.Uint32, &Edx.Uint32); DisplayedFamily = Eax.Bits.FamilyId; if (Eax.Bits.FamilyId == 0x0F) { DisplayedFamily |= (Eax.Bits.ExtendedFamilyId << 4); } DisplayedModel = Eax.Bits.Model; if (Eax.Bits.FamilyId == 0x06 || Eax.Bits.FamilyId == 0x0f) { DisplayedModel |= (Eax.Bits.ExtendedModelId << 4); } CpuInfo->DisplayFamily = DisplayedFamily; CpuInfo->DisplayModel = DisplayedModel; CpuInfo->SteppingId = Eax.Bits.SteppingId; CpuInfo->ProcessorType = Eax.Bits.ProcessorType; CpuInfo->CpuIdVersionInfoEcx.Uint32 = Ecx.Uint32; CpuInfo->CpuIdVersionInfoEdx.Uint32 = Edx.Uint32; } /** Prepares for private data used for CPU features. @param[in] NumberOfCpus Number of processor in system **/ VOID CpuInitDataInitialize ( IN UINTN NumberOfCpus ) { EFI_STATUS Status; UINTN ProcessorNumber; EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer; CPU_FEATURES_ENTRY *CpuFeature; CPU_FEATURES_INIT_ORDER *InitOrder; CPU_FEATURES_DATA *CpuFeaturesData; LIST_ENTRY *Entry; CpuFeaturesData = GetCpuFeaturesData (); CpuFeaturesData->InitOrder = AllocateZeroPool (sizeof (CPU_FEATURES_INIT_ORDER) * NumberOfCpus); ASSERT (CpuFeaturesData->InitOrder != NULL); CpuFeaturesData->BitMaskSize = PcdGetSize (PcdCpuFeaturesSupport); // // Collect CPU Features information // Entry = GetFirstNode (&CpuFeaturesData->FeatureList); while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) { CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry); ASSERT (CpuFeature->InitializeFunc != NULL); if (CpuFeature->GetConfigDataFunc != NULL) { CpuFeature->ConfigData = CpuFeature->GetConfigDataFunc (NumberOfCpus); } Entry = Entry->ForwardLink; } for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) { InitOrder = &CpuFeaturesData->InitOrder[ProcessorNumber]; InitOrder->FeaturesSupportedMask = AllocateZeroPool (CpuFeaturesData->BitMaskSize); InitializeListHead (&InitOrder->OrderList); Status = GetProcessorInformation (ProcessorNumber, &ProcessorInfoBuffer); ASSERT_EFI_ERROR (Status); CopyMem ( &InitOrder->CpuInfo.ProcessorInfo, &ProcessorInfoBuffer, sizeof (EFI_PROCESSOR_INFORMATION) ); } // // Get support and configuration PCDs // CpuFeaturesData->SupportPcds = GetSupportPcds (); CpuFeaturesData->ConfigurationPcds = GetConfigurationPcds (); } /** Worker function to do OR operation on CPU feature supported bits mask buffer. @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer @param[in] OrFeatureBitMask The feature bit mask to do OR operation **/ VOID SupportedMaskOr ( IN UINT8 *SupportedFeatureMask, IN UINT8 *OrFeatureBitMask ) { UINTN Index; UINTN BitMaskSize; UINT8 *Data1; UINT8 *Data2; BitMaskSize = PcdGetSize (PcdCpuFeaturesSupport); Data1 = SupportedFeatureMask; Data2 = OrFeatureBitMask; for (Index = 0; Index < BitMaskSize; Index++) { *(Data1++) |= *(Data2++); } } /** Worker function to do AND operation on CPU feature supported bits mask buffer. @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer @param[in] AndFeatureBitMask The feature bit mask to do AND operation **/ VOID SupportedMaskAnd ( IN UINT8 *SupportedFeatureMask, IN UINT8 *AndFeatureBitMask ) { UINTN Index; UINTN BitMaskSize; UINT8 *Data1; UINT8 *Data2; BitMaskSize = PcdGetSize (PcdCpuFeaturesSupport); Data1 = SupportedFeatureMask; Data2 = AndFeatureBitMask; for (Index = 0; Index < BitMaskSize; Index++) { *(Data1++) &= *(Data2++); } } /** Worker function to check if the compared CPU feature set in the CPU feature supported bits mask buffer. @param[in] SupportedFeatureMask The pointer to CPU feature bits mask buffer @param[in] ComparedFeatureBitMask The feature bit mask to be compared @retval TRUE The ComparedFeatureBitMask is set in CPU feature supported bits mask buffer. @retval FALSE The ComparedFeatureBitMask is not set in CPU feature supported bits mask buffer. **/ BOOLEAN IsBitMaskMatch ( IN UINT8 *SupportedFeatureMask, IN UINT8 *ComparedFeatureBitMask ) { UINTN Index; UINTN BitMaskSize; UINT8 *Data1; UINT8 *Data2; BitMaskSize = PcdGetSize (PcdCpuFeaturesSupport); Data1 = SupportedFeatureMask; Data2 = ComparedFeatureBitMask; for (Index = 0; Index < BitMaskSize; Index++) { if (((*(Data1++)) & (*(Data2++))) != 0) { return TRUE; } } return FALSE; } /** Collects processor data for calling processor. @param[in,out] Buffer The pointer to private data buffer. **/ VOID EFIAPI CollectProcessorData ( IN OUT VOID *Buffer ) { UINTN ProcessorNumber; CPU_FEATURES_ENTRY *CpuFeature; REGISTER_CPU_FEATURE_INFORMATION *CpuInfo; LIST_ENTRY *Entry; CPU_FEATURES_DATA *CpuFeaturesData; CpuFeaturesData = GetCpuFeaturesData (); ProcessorNumber = GetProcessorIndex (); CpuInfo = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo; // // collect processor information // FillProcessorInfo (CpuInfo); Entry = GetFirstNode (&CpuFeaturesData->FeatureList); while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) { CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry); if (IsBitMaskMatch (CpuFeaturesData->SupportPcds, CpuFeature->FeatureMask)) { if (CpuFeature->SupportFunc == NULL) { // // If SupportFunc is NULL, then the feature is supported. // SupportedMaskOr ( CpuFeaturesData->InitOrder[ProcessorNumber].FeaturesSupportedMask, CpuFeature->FeatureMask ); } else if (CpuFeature->SupportFunc (ProcessorNumber, CpuInfo, CpuFeature->ConfigData)) { SupportedMaskOr ( CpuFeaturesData->InitOrder[ProcessorNumber].FeaturesSupportedMask, CpuFeature->FeatureMask ); } } Entry = Entry->ForwardLink; } } /** Dump the contents of a CPU register table. @param[in] ProcessorNumber The index of the CPU to show the register table contents @note This service could be called by BSP only. **/ VOID DumpRegisterTableOnProcessor ( IN UINTN ProcessorNumber ) { CPU_FEATURES_DATA *CpuFeaturesData; UINTN FeatureIndex; CPU_REGISTER_TABLE *RegisterTable; CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; CPU_REGISTER_TABLE_ENTRY *RegisterTableEntryHead; UINT32 DebugPrintErrorLevel; DebugPrintErrorLevel = (ProcessorNumber == 0) ? DEBUG_INFO : DEBUG_VERBOSE; CpuFeaturesData = GetCpuFeaturesData (); // // Debug information // RegisterTable = &CpuFeaturesData->RegisterTable[ProcessorNumber]; DEBUG ((DebugPrintErrorLevel, "RegisterTable->TableLength = %d\n", RegisterTable->TableLength)); RegisterTableEntryHead = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry; for (FeatureIndex = 0; FeatureIndex < RegisterTable->TableLength; FeatureIndex++) { RegisterTableEntry = &RegisterTableEntryHead[FeatureIndex]; switch (RegisterTableEntry->RegisterType) { case Msr: DEBUG (( DebugPrintErrorLevel, "Processor: %d: MSR: %x, Bit Start: %d, Bit Length: %d, Value: %lx\r\n", ProcessorNumber, RegisterTableEntry->Index, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitLength, RegisterTableEntry->Value )); break; case ControlRegister: DEBUG (( DebugPrintErrorLevel, "Processor: %d: CR: %x, Bit Start: %d, Bit Length: %d, Value: %lx\r\n", ProcessorNumber, RegisterTableEntry->Index, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitLength, RegisterTableEntry->Value )); break; case MemoryMapped: DEBUG (( DebugPrintErrorLevel, "Processor: %d: MMIO: %x, Bit Start: %d, Bit Length: %d, Value: %lx\r\n", ProcessorNumber, RegisterTableEntry->Index, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitLength, RegisterTableEntry->Value )); break; case CacheControl: DEBUG (( DebugPrintErrorLevel, "Processor: %d: CACHE: %x, Bit Start: %d, Bit Length: %d, Value: %lx\r\n", ProcessorNumber, RegisterTableEntry->Index, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitLength, RegisterTableEntry->Value )); break; default: break; } } } /** Analysis register CPU features on each processor and save CPU setting in CPU register table. @param[in] NumberOfCpus Number of processor in system **/ VOID AnalysisProcessorFeatures ( IN UINTN NumberOfCpus ) { EFI_STATUS Status; UINTN ProcessorNumber; CPU_FEATURES_ENTRY *CpuFeature; CPU_FEATURES_ENTRY *CpuFeatureInOrder; CPU_FEATURES_INIT_ORDER *CpuInitOrder; REGISTER_CPU_FEATURE_INFORMATION *CpuInfo; LIST_ENTRY *Entry; CPU_FEATURES_DATA *CpuFeaturesData; CpuFeaturesData = GetCpuFeaturesData (); CpuFeaturesData->CapabilityPcds = AllocatePool (CpuFeaturesData->BitMaskSize); SetMem (CpuFeaturesData->CapabilityPcds, CpuFeaturesData->BitMaskSize, 0xFF); for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) { CpuInitOrder = &CpuFeaturesData->InitOrder[ProcessorNumber]; // // Calculate the last capability on all processors // SupportedMaskAnd (CpuFeaturesData->CapabilityPcds, CpuInitOrder->FeaturesSupportedMask); } // // Calculate the last setting // CpuFeaturesData->SettingPcds = AllocateCopyPool (CpuFeaturesData->BitMaskSize, CpuFeaturesData->CapabilityPcds); SupportedMaskAnd (CpuFeaturesData->SettingPcds, CpuFeaturesData->ConfigurationPcds); // // Save PCDs and display CPU PCDs // SetCapabilityPcd (CpuFeaturesData->CapabilityPcds); SetSettingPcd (CpuFeaturesData->SettingPcds); // // Dump the last CPU feature list // DEBUG_CODE ( DEBUG ((DEBUG_INFO, "Last CPU features list...\n")); Entry = GetFirstNode (&CpuFeaturesData->FeatureList); while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) { CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry); if (IsBitMaskMatch (CpuFeature->FeatureMask, CpuFeaturesData->CapabilityPcds)) { if (IsBitMaskMatch (CpuFeature->FeatureMask, CpuFeaturesData->SettingPcds)) { DEBUG ((DEBUG_INFO, "[Enable ] ")); } else { DEBUG ((DEBUG_INFO, "[Disable ] ")); } } else { DEBUG ((DEBUG_INFO, "[Unsupport] ")); } DumpCpuFeature (CpuFeature); Entry = Entry->ForwardLink; } DEBUG ((DEBUG_INFO, "PcdCpuFeaturesSupport:\n")); DumpCpuFeatureMask (CpuFeaturesData->SupportPcds); DEBUG ((DEBUG_INFO, "PcdCpuFeaturesUserConfiguration:\n")); DumpCpuFeatureMask (CpuFeaturesData->ConfigurationPcds); DEBUG ((DEBUG_INFO, "PcdCpuFeaturesCapability:\n")); DumpCpuFeatureMask (CpuFeaturesData->CapabilityPcds); DEBUG ((DEBUG_INFO, "PcdCpuFeaturesSetting:\n")); DumpCpuFeatureMask (CpuFeaturesData->SettingPcds); ); for (ProcessorNumber = 0; ProcessorNumber < NumberOfCpus; ProcessorNumber++) { CpuInitOrder = &CpuFeaturesData->InitOrder[ProcessorNumber]; Entry = GetFirstNode (&CpuFeaturesData->FeatureList); while (!IsNull (&CpuFeaturesData->FeatureList, Entry)) { // // Insert each feature into processor's order list // CpuFeature = CPU_FEATURE_ENTRY_FROM_LINK (Entry); if (IsBitMaskMatch (CpuFeature->FeatureMask, CpuFeaturesData->CapabilityPcds)) { CpuFeatureInOrder = AllocateCopyPool (sizeof (CPU_FEATURES_ENTRY), CpuFeature); InsertTailList (&CpuInitOrder->OrderList, &CpuFeatureInOrder->Link); } Entry = Entry->ForwardLink; } // // Go through ordered feature list to initialize CPU features // CpuInfo = &CpuFeaturesData->InitOrder[ProcessorNumber].CpuInfo; Entry = GetFirstNode (&CpuInitOrder->OrderList); while (!IsNull (&CpuInitOrder->OrderList, Entry)) { CpuFeatureInOrder = CPU_FEATURE_ENTRY_FROM_LINK (Entry); if (IsBitMaskMatch (CpuFeatureInOrder->FeatureMask, CpuFeaturesData->SettingPcds)) { Status = CpuFeatureInOrder->InitializeFunc (ProcessorNumber, CpuInfo, CpuFeatureInOrder->ConfigData, TRUE); } else { Status = CpuFeatureInOrder->InitializeFunc (ProcessorNumber, CpuInfo, CpuFeatureInOrder->ConfigData, FALSE); } ASSERT_EFI_ERROR (Status); Entry = Entry->ForwardLink; } // // Dump the RegisterTable // DumpRegisterTableOnProcessor (ProcessorNumber); } } /** Initialize the CPU registers from a register table. @param[in] ProcessorNumber The index of the CPU executing this function. @note This service could be called by BSP/APs. **/ VOID ProgramProcessorRegister ( IN UINTN ProcessorNumber ) { CPU_FEATURES_DATA *CpuFeaturesData; CPU_REGISTER_TABLE *RegisterTable; CPU_REGISTER_TABLE_ENTRY *RegisterTableEntry; UINTN Index; UINTN Value; CPU_REGISTER_TABLE_ENTRY *RegisterTableEntryHead; CpuFeaturesData = GetCpuFeaturesData (); RegisterTable = &CpuFeaturesData->RegisterTable[ProcessorNumber]; // // Traverse Register Table of this logical processor // RegisterTableEntryHead = (CPU_REGISTER_TABLE_ENTRY *) (UINTN) RegisterTable->RegisterTableEntry; for (Index = 0; Index < RegisterTable->TableLength; Index++) { RegisterTableEntry = &RegisterTableEntryHead[Index]; // // Check the type of specified register // switch (RegisterTableEntry->RegisterType) { // // The specified register is Control Register // case ControlRegister: switch (RegisterTableEntry->Index) { case 0: Value = AsmReadCr0 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, RegisterTableEntry->Value ); AsmWriteCr0 (Value); break; case 2: Value = AsmReadCr2 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, RegisterTableEntry->Value ); AsmWriteCr2 (Value); break; case 3: Value = AsmReadCr3 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, RegisterTableEntry->Value ); AsmWriteCr3 (Value); break; case 4: Value = AsmReadCr4 (); Value = (UINTN) BitFieldWrite64 ( Value, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, RegisterTableEntry->Value ); AsmWriteCr4 (Value); break; case 8: // // Do we need to support CR8? // break; default: break; } break; // // The specified register is Model Specific Register // case Msr: // // Get lock to avoid Package/Core scope MSRs programming issue in parallel execution mode // AcquireSpinLock (&CpuFeaturesData->MsrLock); if (RegisterTableEntry->ValidBitLength >= 64) { // // If length is not less than 64 bits, then directly write without reading // AsmWriteMsr64 ( RegisterTableEntry->Index, RegisterTableEntry->Value ); } else { // // Set the bit section according to bit start and length // AsmMsrBitFieldWrite64 ( RegisterTableEntry->Index, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, RegisterTableEntry->Value ); } ReleaseSpinLock (&CpuFeaturesData->MsrLock); break; // // MemoryMapped operations // case MemoryMapped: AcquireSpinLock (&CpuFeaturesData->MemoryMappedLock); MmioBitFieldWrite32 ( RegisterTableEntry->Index, RegisterTableEntry->ValidBitStart, RegisterTableEntry->ValidBitStart + RegisterTableEntry->ValidBitLength - 1, (UINT32)RegisterTableEntry->Value ); ReleaseSpinLock (&CpuFeaturesData->MemoryMappedLock); break; // // Enable or disable cache // case CacheControl: // // If value of the entry is 0, then disable cache. Otherwise, enable cache. // if (RegisterTableEntry->Value == 0) { AsmDisableCache (); } else { AsmEnableCache (); } break; default: break; } } } /** Programs registers for the calling processor. @param[in,out] Buffer The pointer to private data buffer. **/ VOID EFIAPI SetProcessorRegister ( IN OUT VOID *Buffer ) { UINTN ProcessorNumber; ProcessorNumber = GetProcessorIndex (); ProgramProcessorRegister (ProcessorNumber); } /** Performs CPU features detection. This service will invoke MP service to check CPU features' capabilities on BSP/APs. @note This service could be called by BSP only. **/ VOID EFIAPI CpuFeaturesDetect ( VOID ) { UINTN NumberOfCpus; UINTN NumberOfEnabledProcessors; GetNumberOfProcessor (&NumberOfCpus, &NumberOfEnabledProcessors); CpuInitDataInitialize (NumberOfCpus); // // Wakeup all APs for data collection. // StartupAPsWorker (CollectProcessorData); // // Collect data on BSP // CollectProcessorData (NULL); AnalysisProcessorFeatures (NumberOfCpus); } /** Performs CPU features Initialization. This service will invoke MP service to perform CPU features initialization on BSP/APs per user configuration. @note This service could be called by BSP only. **/ VOID EFIAPI CpuFeaturesInitialize ( VOID ) { CPU_FEATURES_DATA *CpuFeaturesData; UINTN OldBspNumber; CpuFeaturesData = GetCpuFeaturesData (); OldBspNumber = GetProcessorIndex(); CpuFeaturesData->BspNumber = OldBspNumber; // // Wakeup all APs for programming. // StartupAPsWorker (SetProcessorRegister); // // Programming BSP // SetProcessorRegister (NULL); // // Switch to new BSP if required // if (CpuFeaturesData->BspNumber != OldBspNumber) { SwitchNewBsp (CpuFeaturesData->BspNumber); } }