/** @file ProcessorSubClass.c Copyright (c) 2021, NUVIA Inc. All rights reserved.
Copyright (c) 2015, Hisilicon Limited. All rights reserved. Copyright (c) 2015, Linaro Limited. All rights reserved. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SmbiosProcessor.h" extern UINT8 ProcessorSubClassStrings[]; #define CACHE_SOCKETED_SHIFT 3 #define CACHE_LOCATION_SHIFT 5 #define CACHE_ENABLED_SHIFT 7 #define CACHE_OPERATION_MODE_SHIFT 8 typedef enum { CacheModeWriteThrough = 0, ///< Cache is write-through CacheModeWriteBack, ///< Cache is write-back CacheModeVariesWithAddress, ///< Cache mode varies by address CacheModeUnknown, ///< Cache mode is unknown CacheModeMax } CACHE_OPERATION_MODE; typedef enum { CacheLocationInternal = 0, ///< Cache is internal to the processor CacheLocationExternal, ///< Cache is external to the processor CacheLocationReserved, ///< Reserved CacheLocationUnknown, ///< Cache location is unknown CacheLocationMax } CACHE_LOCATION; EFI_HII_HANDLE mHiiHandle; EFI_SMBIOS_PROTOCOL *mSmbios; SMBIOS_TABLE_TYPE4 mSmbiosProcessorTableTemplate = { { // Hdr EFI_SMBIOS_TYPE_PROCESSOR_INFORMATION, // Type sizeof (SMBIOS_TABLE_TYPE4), // Length 0 // Handle }, 1, // Socket CentralProcessor, // ProcessorType ProcessorFamilyIndicatorFamily2, // ProcessorFamily 2, // ProcessorManufacture { // ProcessorId { // Signature 0 }, { // FeatureFlags 0 } }, 3, // ProcessorVersion { // Voltage 0 }, 0, // ExternalClock 0, // MaxSpeed 0, // CurrentSpeed 0, // Status ProcessorUpgradeUnknown, // ProcessorUpgrade 0xFFFF, // L1CacheHandle 0xFFFF, // L2CacheHandle 0xFFFF, // L3CacheHandle 4, // SerialNumber 5, // AssetTag 6, // PartNumber 0, // CoreCount 0, //EnabledCoreCount 0, // ThreadCount 0, // ProcessorCharacteristics ProcessorFamilyARM, // ProcessorFamily2 0, // CoreCount2 0, // EnabledCoreCount2 0 // ThreadCount2 }; /** Sets the HII variable `StringId` is `Pcd` isn't empty. @param Pcd The FixedAtBuild PCD that contains the string to fetch. @param StringId The string identifier to set. **/ #define SET_HII_STRING_IF_PCD_NOT_EMPTY(Pcd, StringId) \ do { \ CHAR16 *Str; \ Str = (CHAR16*)PcdGetPtr (Pcd); \ if (StrLen (Str) > 0) { \ HiiSetString (mHiiHandle, StringId, Str, NULL); \ } \ } while (0) /** Fetches the specified processor's frequency in Hz. @param ProcessorNumber The processor number @return The clock frequency in MHz **/ UINT16 GetCpuFrequency ( IN UINT8 ProcessorNumber ) { return (UINT16)(OemGetCpuFreq (ProcessorNumber) / 1000 / 1000); } /** Gets a description of the specified cache. @param[in] CacheLevel Zero-based cache level (e.g. L1 cache is 0). @param[in] DataCache Cache is a data cache. @param[in] UnifiedCache Cache is a unified cache. @param[out] CacheSocketStr The description of the specified cache @return The number of Unicode characters in CacheSocketStr not including the terminating NUL. **/ UINTN GetCacheSocketStr ( IN UINT8 CacheLevel, IN BOOLEAN DataCache, IN BOOLEAN UnifiedCache, OUT CHAR16 *CacheSocketStr ) { UINTN CacheSocketStrLen; if (CacheLevel == CpuCacheL1 && !DataCache && !UnifiedCache) { CacheSocketStrLen = UnicodeSPrint ( CacheSocketStr, SMBIOS_STRING_MAX_LENGTH - 1, L"L%x Instruction Cache", CacheLevel); } else if (CacheLevel == CpuCacheL1 && DataCache) { CacheSocketStrLen = UnicodeSPrint (CacheSocketStr, SMBIOS_STRING_MAX_LENGTH - 1, L"L%x Data Cache", CacheLevel); } else { CacheSocketStrLen = UnicodeSPrint (CacheSocketStr, SMBIOS_STRING_MAX_LENGTH - 1, L"L%x Cache", CacheLevel); } return CacheSocketStrLen; } /** Fills in the Type 7 record with the cache architecture information read from the CPU registers. @param[in] CacheLevel Cache level (e.g. L1, L2). @param[in] DataCache Cache is a data cache. @param[in] UnifiedCache Cache is a unified cache. @param[out] Type7Record The Type 7 record to fill in. **/ VOID ConfigureCacheArchitectureInformation ( IN UINT8 CacheLevel, IN BOOLEAN DataCache, IN BOOLEAN UnifiedCache, OUT SMBIOS_TABLE_TYPE7 *Type7Record ) { UINT8 Associativity; UINT32 CacheSize32; UINT16 CacheSize16; UINT64 CacheSize64; if (!DataCache && !UnifiedCache) { Type7Record->SystemCacheType = CacheTypeInstruction; } else if (DataCache) { Type7Record->SystemCacheType = CacheTypeData; } else if (UnifiedCache) { Type7Record->SystemCacheType = CacheTypeUnified; } else { ASSERT(FALSE); } CacheSize64 = SmbiosProcessorGetCacheSize (CacheLevel, DataCache, UnifiedCache ); Associativity = SmbiosProcessorGetCacheAssociativity (CacheLevel, DataCache, UnifiedCache ); CacheSize64 /= 1024; // Minimum granularity is 1K // Encode the cache size into the format SMBIOS wants if (CacheSize64 < MAX_INT16) { CacheSize16 = CacheSize64; CacheSize32 = CacheSize16; } else if ((CacheSize64 / 64) < MAX_INT16) { CacheSize16 = (1 << 15) | (CacheSize64 / 64); CacheSize32 = CacheSize16; } else { if ((CacheSize64 / 1024) <= 2047) { CacheSize32 = CacheSize64; } else { CacheSize32 = (1 << 31) | (CacheSize64 / 64); } CacheSize16 = -1; } Type7Record->MaximumCacheSize = CacheSize16; Type7Record->InstalledSize = CacheSize16; Type7Record->MaximumCacheSize2 = CacheSize32; Type7Record->InstalledSize2 = CacheSize32; switch (Associativity) { case 2: Type7Record->Associativity = CacheAssociativity2Way; break; case 4: Type7Record->Associativity = CacheAssociativity4Way; break; case 8: Type7Record->Associativity = CacheAssociativity8Way; break; case 12: Type7Record->Associativity = CacheAssociativity12Way; break; case 16: Type7Record->Associativity = CacheAssociativity16Way; break; case 20: Type7Record->Associativity = CacheAssociativity20Way; break; case 24: Type7Record->Associativity = CacheAssociativity24Way; break; case 32: Type7Record->Associativity = CacheAssociativity32Way; break; case 48: Type7Record->Associativity = CacheAssociativity48Way; break; case 64: Type7Record->Associativity = CacheAssociativity64Way; break; default: Type7Record->Associativity = CacheAssociativityOther; break; } Type7Record->CacheConfiguration = (CacheModeUnknown << CACHE_OPERATION_MODE_SHIFT) | (1 << CACHE_ENABLED_SHIFT) | (CacheLocationUnknown << CACHE_LOCATION_SHIFT) | (0 << CACHE_SOCKETED_SHIFT) | (CacheLevel - 1); } /** Allocates and initializes an SMBIOS_TABLE_TYPE7 structure. @param[in] CacheLevel The cache level (L1-L7). @param[in] DataCache Cache is a data cache. @param[in] UnifiedCache Cache is a unified cache. @return A pointer to the Type 7 structure. Returns NULL on failure. **/ SMBIOS_TABLE_TYPE7 * AllocateAndInitCacheInformation ( IN UINT8 CacheLevel, IN BOOLEAN DataCache, IN BOOLEAN UnifiedCache ) { SMBIOS_TABLE_TYPE7 *Type7Record; EFI_STRING CacheSocketStr; UINTN CacheSocketStrLen; UINTN StringBufferSize; CHAR8 *OptionalStrStart; UINTN TableSize; // Allocate and fetch the cache description StringBufferSize = sizeof (CHAR16) * SMBIOS_STRING_MAX_LENGTH; CacheSocketStr = AllocateZeroPool (StringBufferSize); if (CacheSocketStr == NULL) { return NULL; } CacheSocketStrLen = GetCacheSocketStr (CacheLevel, DataCache, UnifiedCache, CacheSocketStr); TableSize = sizeof (SMBIOS_TABLE_TYPE7) + CacheSocketStrLen + 1 + 1; Type7Record = AllocateZeroPool (TableSize); if (Type7Record == NULL) { FreePool(CacheSocketStr); return NULL; } Type7Record->Hdr.Type = EFI_SMBIOS_TYPE_CACHE_INFORMATION; Type7Record->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE7); Type7Record->Hdr.Handle = SMBIOS_HANDLE_PI_RESERVED; Type7Record->SocketDesignation = 1; Type7Record->SupportedSRAMType.Unknown = 1; Type7Record->CurrentSRAMType.Unknown = 1; Type7Record->CacheSpeed = 0; Type7Record->ErrorCorrectionType = CacheErrorUnknown; OptionalStrStart = (CHAR8 *)(Type7Record + 1); UnicodeStrToAsciiStrS (CacheSocketStr, OptionalStrStart, CacheSocketStrLen + 1); FreePool (CacheSocketStr); return Type7Record; } /** Add Type 7 SMBIOS Record for Cache Information. @param[in] ProcessorIndex Processor number of specified processor. @param[out] L1CacheHandle Pointer to the handle of the L1 Cache SMBIOS record. @param[out] L2CacheHandle Pointer to the handle of the L2 Cache SMBIOS record. @param[out] L3CacheHandle Pointer to the handle of the L3 Cache SMBIOS record. **/ VOID AddSmbiosCacheTypeTable ( IN UINTN ProcessorIndex, OUT EFI_SMBIOS_HANDLE *L1CacheHandle, OUT EFI_SMBIOS_HANDLE *L2CacheHandle, OUT EFI_SMBIOS_HANDLE *L3CacheHandle ) { EFI_STATUS Status; SMBIOS_TABLE_TYPE7 *Type7Record; EFI_SMBIOS_HANDLE SmbiosHandle; UINT8 CacheLevel; UINT8 MaxCacheLevel; BOOLEAN DataCacheType; BOOLEAN SeparateCaches; Status = EFI_SUCCESS; MaxCacheLevel = 0; // See if there's an L1 cache present. MaxCacheLevel = SmbiosProcessorGetMaxCacheLevel (); if (MaxCacheLevel < 1) { return; } for (CacheLevel = 1; CacheLevel <= MaxCacheLevel; CacheLevel++) { Type7Record = NULL; SeparateCaches = SmbiosProcessorHasSeparateCaches (CacheLevel); // At each level of cache, we can have a single type (unified, instruction or data), // or two types - separate data and instruction caches. If we have separate // instruction and data caches, then on the first iteration (CacheSubLevel = 0) // process the instruction cache. for (DataCacheType = 0; DataCacheType <= 1; DataCacheType++) { // If there's no separate data/instruction cache, skip the second iteration if (DataCacheType == 1 && !SeparateCaches) { continue; } Type7Record = AllocateAndInitCacheInformation (CacheLevel, DataCacheType, !SeparateCaches ); if (Type7Record == NULL) { continue; } ConfigureCacheArchitectureInformation(CacheLevel, DataCacheType, !SeparateCaches, Type7Record ); // Allow the platform to fill in other information such as speed, SRAM type etc. if (!OemGetCacheInformation (ProcessorIndex, CacheLevel, DataCacheType, !SeparateCaches, Type7Record)) { continue; } SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED; // Finally, install the table Status = mSmbios->Add (mSmbios, NULL, &SmbiosHandle, (EFI_SMBIOS_TABLE_HEADER *)Type7Record); if (EFI_ERROR (Status)) { continue; } // Config L1/L2/L3 Cache Handle switch (CacheLevel) { case CpuCacheL1: *L1CacheHandle = SmbiosHandle; break; case CpuCacheL2: *L2CacheHandle = SmbiosHandle; break; case CpuCacheL3: *L3CacheHandle = SmbiosHandle; break; default: break; } } } } /** Allocates a Type 4 Processor Information structure and sets the strings following the data fields. @param[out] Type4Record The Type 4 structure to allocate and initialize @param[in] ProcessorIndex The index of the processor @param[in] Populated Whether the specified processor is populated. @retval EFI_SUCCESS The Type 4 structure was successfully allocated and the strings initialized. @retval EFI_OUT_OF_RESOURCES Could not allocate memory needed. **/ EFI_STATUS AllocateType4AndSetProcessorInformationStrings ( SMBIOS_TABLE_TYPE4 **Type4Record, UINT8 ProcessorIndex, BOOLEAN Populated ) { EFI_STATUS Status; EFI_STRING_ID ProcessorManu; EFI_STRING_ID ProcessorVersion; EFI_STRING_ID SerialNumber; EFI_STRING_ID AssetTag; EFI_STRING_ID PartNumber; EFI_STRING ProcessorStr; EFI_STRING ProcessorManuStr; EFI_STRING ProcessorVersionStr; EFI_STRING SerialNumberStr; EFI_STRING AssetTagStr; EFI_STRING PartNumberStr; CHAR8 *OptionalStrStart; CHAR8 *StrStart; UINTN ProcessorStrLen; UINTN ProcessorManuStrLen; UINTN ProcessorVersionStrLen; UINTN SerialNumberStrLen; UINTN AssetTagStrLen; UINTN PartNumberStrLen; UINTN TotalSize; UINTN StringBufferSize; Status = EFI_SUCCESS; ProcessorManuStr = NULL; ProcessorVersionStr = NULL; SerialNumberStr = NULL; AssetTagStr = NULL; PartNumberStr = NULL; ProcessorManu = STRING_TOKEN (STR_PROCESSOR_MANUFACTURE); ProcessorVersion = STRING_TOKEN (STR_PROCESSOR_VERSION); SerialNumber = STRING_TOKEN (STR_PROCESSOR_SERIAL_NUMBER); AssetTag = STRING_TOKEN (STR_PROCESSOR_ASSET_TAG); PartNumber = STRING_TOKEN (STR_PROCESSOR_PART_NUMBER); SET_HII_STRING_IF_PCD_NOT_EMPTY (PcdProcessorManufacturer, ProcessorManu); SET_HII_STRING_IF_PCD_NOT_EMPTY (PcdProcessorVersion, ProcessorVersion); SET_HII_STRING_IF_PCD_NOT_EMPTY (PcdProcessorSerialNumber, SerialNumber); SET_HII_STRING_IF_PCD_NOT_EMPTY (PcdProcessorAssetTag, AssetTag); SET_HII_STRING_IF_PCD_NOT_EMPTY (PcdProcessorPartNumber, PartNumber); // Processor Designation StringBufferSize = sizeof (CHAR16) * SMBIOS_STRING_MAX_LENGTH; ProcessorStr = AllocateZeroPool (StringBufferSize); if (ProcessorStr == NULL) { return EFI_OUT_OF_RESOURCES; } ProcessorStrLen = UnicodeSPrint (ProcessorStr, StringBufferSize, L"CPU%02d", ProcessorIndex + 1); // Processor Manufacture ProcessorManuStr = HiiGetPackageString (&gEfiCallerIdGuid, ProcessorManu, NULL); ProcessorManuStrLen = StrLen (ProcessorManuStr); // Processor Version ProcessorVersionStr = HiiGetPackageString (&gEfiCallerIdGuid, ProcessorVersion, NULL); ProcessorVersionStrLen = StrLen (ProcessorVersionStr); // Serial Number SerialNumberStr = HiiGetPackageString (&gEfiCallerIdGuid, SerialNumber, NULL); SerialNumberStrLen = StrLen (SerialNumberStr); // Asset Tag AssetTagStr = HiiGetPackageString (&gEfiCallerIdGuid, AssetTag, NULL); AssetTagStrLen = StrLen (AssetTagStr); // Part Number PartNumberStr = HiiGetPackageString (&gEfiCallerIdGuid, PartNumber, NULL); PartNumberStrLen = StrLen (PartNumberStr); TotalSize = sizeof (SMBIOS_TABLE_TYPE4) + ProcessorStrLen + 1 + ProcessorManuStrLen + 1 + ProcessorVersionStrLen + 1 + SerialNumberStrLen + 1 + AssetTagStrLen + 1 + PartNumberStrLen + 1 + 1; *Type4Record = AllocateZeroPool (TotalSize); if (*Type4Record == NULL) { Status = EFI_OUT_OF_RESOURCES; goto Exit; } CopyMem (*Type4Record, &mSmbiosProcessorTableTemplate, sizeof (SMBIOS_TABLE_TYPE4)); OptionalStrStart = (CHAR8 *)(*Type4Record + 1); UnicodeStrToAsciiStrS ( ProcessorStr, OptionalStrStart, ProcessorStrLen + 1 ); StrStart = OptionalStrStart + ProcessorStrLen + 1; UnicodeStrToAsciiStrS ( ProcessorManuStr, StrStart, ProcessorManuStrLen + 1 ); StrStart += ProcessorManuStrLen + 1; UnicodeStrToAsciiStrS ( ProcessorVersionStr, StrStart, ProcessorVersionStrLen + 1 ); StrStart += ProcessorVersionStrLen + 1; UnicodeStrToAsciiStrS ( SerialNumberStr, StrStart, SerialNumberStrLen + 1 ); StrStart += SerialNumberStrLen + 1; UnicodeStrToAsciiStrS ( AssetTagStr, StrStart, AssetTagStrLen + 1 ); StrStart += AssetTagStrLen + 1; UnicodeStrToAsciiStrS ( PartNumberStr, StrStart, PartNumberStrLen + 1 ); Exit: FreePool (ProcessorStr); FreePool (ProcessorManuStr); FreePool (ProcessorVersionStr); FreePool (SerialNumberStr); FreePool (AssetTagStr); FreePool (PartNumberStr); return Status; } /** Add Type 4 SMBIOS Record for Processor Information. @param[in] ProcessorIndex Processor index of specified processor. **/ EFI_STATUS AddSmbiosProcessorTypeTable ( IN UINTN ProcessorIndex ) { EFI_STATUS Status; SMBIOS_TABLE_TYPE4 *Type4Record; EFI_SMBIOS_HANDLE SmbiosHandle; EFI_SMBIOS_HANDLE L1CacheHandle; EFI_SMBIOS_HANDLE L2CacheHandle; EFI_SMBIOS_HANDLE L3CacheHandle; UINT8 *LegacyVoltage; PROCESSOR_STATUS_DATA ProcessorStatus; UINT64 *ProcessorId; PROCESSOR_CHARACTERISTIC_FLAGS ProcessorCharacteristics; OEM_MISC_PROCESSOR_DATA MiscProcessorData; BOOLEAN ProcessorPopulated; Type4Record = NULL; MiscProcessorData.Voltage = 0; MiscProcessorData.CurrentSpeed = 0; MiscProcessorData.CoreCount = 0; MiscProcessorData.CoresEnabled = 0; MiscProcessorData.ThreadCount = 0; MiscProcessorData.MaxSpeed = 0; L1CacheHandle = 0xFFFF; L2CacheHandle = 0xFFFF; L3CacheHandle = 0xFFFF; ProcessorPopulated = OemIsProcessorPresent (ProcessorIndex); Status = AllocateType4AndSetProcessorInformationStrings ( &Type4Record, ProcessorIndex, ProcessorPopulated ); if (EFI_ERROR (Status)) { return Status; } OemGetProcessorInformation (ProcessorIndex, &ProcessorStatus, (PROCESSOR_CHARACTERISTIC_FLAGS*) &Type4Record->ProcessorCharacteristics, &MiscProcessorData); if (ProcessorPopulated) { AddSmbiosCacheTypeTable (ProcessorIndex, &L1CacheHandle, &L2CacheHandle, &L3CacheHandle); } LegacyVoltage = (UINT8*)&Type4Record->Voltage; *LegacyVoltage = MiscProcessorData.Voltage; Type4Record->CurrentSpeed = MiscProcessorData.CurrentSpeed; Type4Record->MaxSpeed = MiscProcessorData.MaxSpeed; Type4Record->Status = ProcessorStatus.Data; Type4Record->L1CacheHandle = L1CacheHandle; Type4Record->L2CacheHandle = L2CacheHandle; Type4Record->L3CacheHandle = L3CacheHandle; Type4Record->CoreCount = MiscProcessorData.CoreCount; Type4Record->CoreCount2 = MiscProcessorData.CoreCount; Type4Record->EnabledCoreCount = MiscProcessorData.CoresEnabled; Type4Record->EnabledCoreCount2 = MiscProcessorData.CoresEnabled; Type4Record->ThreadCount = MiscProcessorData.ThreadCount; Type4Record->ThreadCount2 = MiscProcessorData.ThreadCount; Type4Record->CurrentSpeed = GetCpuFrequency (ProcessorIndex); Type4Record->ExternalClock = (UINT16)(SmbiosGetExternalClockFrequency () / 1000 / 1000); ProcessorId = (UINT64*)&Type4Record->ProcessorId; *ProcessorId = SmbiosGetProcessorId (); ProcessorCharacteristics = SmbiosGetProcessorCharacteristics (); Type4Record->ProcessorCharacteristics |= *((UINT64*)&ProcessorCharacteristics); Type4Record->ProcessorFamily = SmbiosGetProcessorFamily (); Type4Record->ProcessorFamily2 = SmbiosGetProcessorFamily2 (); SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED; Status = mSmbios->Add (mSmbios, NULL, &SmbiosHandle, (EFI_SMBIOS_TABLE_HEADER *)Type4Record); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "[%a]:[%dL] Smbios Type04 Table Log Failed! %r \n", __FUNCTION__, __LINE__, Status)); } FreePool (Type4Record); return Status; } /** Standard EFI driver point. @param ImageHandle Handle for the image of this driver @param SystemTable Pointer to the EFI System Table @retval EFI_SUCCESS The data was successfully stored. **/ EFI_STATUS EFIAPI ProcessorSubClassEntryPoint( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; UINT32 ProcessorIndex; // // Locate dependent protocols // Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID**)&mSmbios); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Could not locate SMBIOS protocol. %r\n", Status)); return Status; } // // Add our default strings to the HII database. They will be modified later. // mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid, NULL, ProcessorSubClassStrings, NULL, NULL ); if (mHiiHandle == NULL) { return EFI_OUT_OF_RESOURCES; } // // Add SMBIOS tables for populated sockets. // for (ProcessorIndex = 0; ProcessorIndex < OemGetMaxProcessors (); ProcessorIndex++) { Status = AddSmbiosProcessorTypeTable (ProcessorIndex); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "Add Processor Type Table Failed! %r.\n", Status)); return Status; } } return Status; }