/*++
Copyright (c) 2006 - 2020, Intel Corporation. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent
Module Name:
MiscProcessorInformationFunction.c
Abstract:
Onboard processor information boot time changes.
SMBIOS type 4.
--*/
#include "CommonHeader.h"
#include "MiscSubclassDriver.h"
#include
#include
#include
#define EfiProcessorFamilyIntelAtomProcessor 0x2B
EFI_GUID mProcessorProducerGuid;
/**
Get cache SMBIOS record handle.
@param Smbios Pointer to SMBIOS protocol instance.
@param CacheLevel Level of cache, starting from one.
@param Handle Returned record handle.
**/
VOID
GetCacheHandle (
IN EFI_SMBIOS_PROTOCOL *Smbios,
IN UINT8 CacheLevel,
OUT EFI_SMBIOS_HANDLE *Handle
)
{
UINT16 CacheConfig;
EFI_STATUS Status;
EFI_SMBIOS_TYPE RecordType;
EFI_SMBIOS_TABLE_HEADER *Buffer;
*Handle = 0;
RecordType = EFI_SMBIOS_TYPE_CACHE_INFORMATION;
do {
Status = Smbios->GetNext (
Smbios,
Handle,
&RecordType,
&Buffer,
NULL
);
if (!EFI_ERROR(Status)) {
CacheConfig = *(UINT16*)((UINT8*)Buffer + 5);
if ((CacheConfig & 0x7) == (CacheLevel -1) ) {
return;
}
}
} while (!EFI_ERROR(Status));
*Handle = 0xFFFF;
}
#define BSEL_CR_OVERCLOCK_CONTROL 0xCD
#define FUSE_BSEL_MASK 0x03
UINT16 miFSBFrequencyTable[4] = {
83, // 83.3MHz
100, // 100MHz
133, // 133MHz
117 // 116.7MHz
};
/**
Determine the processor core frequency
@param None
@retval Processor core frequency multiplied by 3
**/
UINT16
DetermineiFsbFromMsr (
VOID
)
{
//
// Determine the processor core frequency
//
UINT64 Temp;
Temp = (AsmReadMsr64 (BSEL_CR_OVERCLOCK_CONTROL)) & FUSE_BSEL_MASK;
return miFSBFrequencyTable[(UINT32)(Temp)];
}
CHAR16 *
CpuidSocVendorBrandString (
VOID
)
{
UINT32 MaximumExtendedFunction;
//
// Array to store brand string from 3 brand string leafs with
// 4 32-bit brand string values per leaf and an extra value to
// null terminate the string.
//
UINT32 BrandString[3 * 4 + 1];
CHAR8 *AsciiBrandString;
CHAR16 *UnicodeBrandString;
UINTN Length;
AsmCpuid (CPUID_EXTENDED_FUNCTION, &MaximumExtendedFunction, NULL, NULL, NULL);
ZeroMem (&BrandString, sizeof (BrandString));
if (CPUID_BRAND_STRING1 <= MaximumExtendedFunction) {
AsmCpuid (
CPUID_BRAND_STRING1,
&BrandString[0],
&BrandString[1],
&BrandString[2],
&BrandString[3]
);
}
if (CPUID_BRAND_STRING2 <= MaximumExtendedFunction) {
AsmCpuid (
CPUID_BRAND_STRING2,
&BrandString[4],
&BrandString[5],
&BrandString[6],
&BrandString[7]
);
}
if (CPUID_BRAND_STRING3 <= MaximumExtendedFunction) {
AsmCpuid (
CPUID_BRAND_STRING3,
&BrandString[8],
&BrandString[9],
&BrandString[10],
&BrandString[11]
);
}
//
// Skip spaces at the beginning of the brand string
//
for (AsciiBrandString = (CHAR8 *)BrandString; *AsciiBrandString == ' '; AsciiBrandString++);
DEBUG ((DEBUG_INFO, "Processor Brand String = %a\n", AsciiBrandString));
//
// Convert ASCII brand string to an allocated Unicode brand string
//
Length = AsciiStrLen (AsciiBrandString) + 1;
UnicodeBrandString = AllocatePool (Length * sizeof (CHAR16));
AsciiStrToUnicodeStrS (AsciiBrandString, UnicodeBrandString, Length);
DEBUG ((DEBUG_INFO, "Processor Unicode Brand String = %s\n", UnicodeBrandString));
return UnicodeBrandString;
}
UINT64
MeasureTscFrequency (
VOID
)
{
EFI_TPL CurrentTpl;
UINT64 BeginValue;
UINT64 EndValue;
UINT64 Frequency;
//
// Wait for 10000us = 10ms for the calculation
// It needs a precise timer to calculate the ticks
//
CurrentTpl = gBS->RaiseTPL (TPL_HIGH_LEVEL);
BeginValue = AsmReadTsc ();
MicroSecondDelay (10000);
EndValue = AsmReadTsc ();
gBS->RestoreTPL (CurrentTpl);
Frequency = MultU64x32 (EndValue - BeginValue, 1000);
Frequency = DivU64x32 (Frequency, 10);
return Frequency;
}
MISC_SMBIOS_TABLE_FUNCTION (MiscProcessorInformation)
{
CHAR8 *OptionalStrStart;
UINTN OptionalStrSize;
EFI_STRING SerialNumber;
CHAR16 *Version=NULL;
CHAR16 *Manufacturer=NULL;
CHAR16 *Socket=NULL;
CHAR16 *AssetTag=NULL;
CHAR16 *PartNumber=NULL;
UINTN SerialNumberStrLen=0;
UINTN VersionStrLen=0;
UINTN ManufacturerStrLen=0;
UINTN SocketStrLen=0;
UINTN AssetTagStrLen=0;
UINTN PartNumberStrLen=0;
UINTN ProcessorVoltage=(BIT7 | 9);
UINT32 Eax01;
UINT32 Ebx01;
UINT32 Ecx01;
UINT32 Edx01;
STRING_REF TokenToGet;
EFI_STATUS Status;
EFI_SMBIOS_HANDLE SmbiosHandle;
SMBIOS_TABLE_TYPE4 *SmbiosRecord;
EFI_CPU_DATA_RECORD *ForType4InputData;
UINT16 L1CacheHandle=0;
UINT16 L2CacheHandle=0;
UINT16 L3CacheHandle=0;
UINTN NumberOfEnabledProcessors=0 ;
UINTN NumberOfProcessors=0;
UINT64 Frequency = 0;
EFI_MP_SERVICES_PROTOCOL *MpService;
PROCESSOR_ID_DATA *ProcessorId = NULL;
//
// First check for invalid parameters.
//
if (RecordData == NULL) {
return EFI_INVALID_PARAMETER;
}
ForType4InputData = (EFI_CPU_DATA_RECORD *)RecordData;
ProcessorId = AllocateZeroPool(sizeof(PROCESSOR_ID_DATA));
if (ProcessorId == NULL) {
return EFI_INVALID_PARAMETER;
}
//
// Token to get for Socket Name
//
TokenToGet = STRING_TOKEN (STR_MISC_SOCKET_NAME);
Socket = SmbiosMiscGetString (TokenToGet);
SocketStrLen = StrLen(Socket);
if (SocketStrLen > SMBIOS_STRING_MAX_LENGTH) {
return EFI_UNSUPPORTED;
}
//
// Token to get for Processor Manufacturer
//
TokenToGet = STRING_TOKEN (STR_MISC_PROCESSOR_MAUFACTURER);
Manufacturer = SmbiosMiscGetString (TokenToGet);
ManufacturerStrLen = StrLen(Manufacturer);
if (ManufacturerStrLen > SMBIOS_STRING_MAX_LENGTH) {
return EFI_UNSUPPORTED;
}
//
// Token to get for Processor Version
//
Version = CpuidSocVendorBrandString ();
VersionStrLen = StrLen(Version);
if (VersionStrLen > SMBIOS_STRING_MAX_LENGTH) {
return EFI_UNSUPPORTED;
}
//
// Token to get for Serial Number
//
TokenToGet = STRING_TOKEN (STR_MISC_PROCESSOR_SERIAL_NUMBER);
SerialNumber = SmbiosMiscGetString (TokenToGet);
SerialNumberStrLen = StrLen(SerialNumber);
if (SerialNumberStrLen > SMBIOS_STRING_MAX_LENGTH) {
return EFI_UNSUPPORTED;
}
//
// Token to get for Assert Tag Information
//
TokenToGet = STRING_TOKEN (STR_MISC_ASSERT_TAG_DATA);
AssetTag = SmbiosMiscGetString (TokenToGet);
AssetTagStrLen = StrLen(AssetTag);
if (AssetTagStrLen > SMBIOS_STRING_MAX_LENGTH) {
return EFI_UNSUPPORTED;
}
//
// Token to get for part number Information
//
TokenToGet = STRING_TOKEN (STR_MISC_PART_NUMBER);
PartNumber = SmbiosMiscGetString (TokenToGet);
PartNumberStrLen = StrLen(PartNumber);
if (PartNumberStrLen > SMBIOS_STRING_MAX_LENGTH) {
return EFI_UNSUPPORTED;
}
//
// Two zeros following the last string.
//
OptionalStrSize = AssetTagStrLen + 1 + SocketStrLen + 1+ ManufacturerStrLen +1 + VersionStrLen+ 1+ SerialNumberStrLen + 1 + PartNumberStrLen+ 1 + 1;
SmbiosRecord = AllocateZeroPool(sizeof (SMBIOS_TABLE_TYPE4) + OptionalStrSize);
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;
SmbiosRecord-> Socket= 1;
SmbiosRecord -> ProcessorManufacture = 2;
SmbiosRecord -> ProcessorVersion = 3;
SmbiosRecord ->SerialNumber =4;
SmbiosRecord-> AssetTag= 5;
SmbiosRecord-> PartNumber= 6;
//
// Processor Type
//
ForType4InputData-> VariableRecord.ProcessorType= EfiCentralProcessor;
SmbiosRecord -> ProcessorType = ForType4InputData-> VariableRecord.ProcessorType;
//
// Processor Family
//
ForType4InputData-> VariableRecord.ProcessorFamily= EfiProcessorFamilyIntelAtomProcessor; //0x2B;;
SmbiosRecord -> ProcessorFamily = ForType4InputData-> VariableRecord.ProcessorFamily;
SmbiosRecord -> ExternalClock = DetermineiFsbFromMsr();
//
// Processor ID
//
AsmCpuid(0x001, &Eax01, &Ebx01, &Ecx01, &Edx01);
ProcessorId->Signature = *(PROCESSOR_SIGNATURE *)&Eax01;
ProcessorId->FeatureFlags = *(PROCESSOR_FEATURE_FLAGS *)&Edx01;
SmbiosRecord -> ProcessorId = *(PROCESSOR_ID_DATA *)ProcessorId;
//
// Processor Voltage
//
ForType4InputData-> VariableRecord.ProcessorVoltage= *(EFI_PROCESSOR_VOLTAGE_DATA *)&ProcessorVoltage;
SmbiosRecord -> Voltage = *(PROCESSOR_VOLTAGE *) &(ForType4InputData-> VariableRecord.ProcessorVoltage);
//
// Status
//
ForType4InputData-> VariableRecord.ProcessorHealthStatus= 0x41;//0x41;
SmbiosRecord -> Status = ForType4InputData-> VariableRecord.ProcessorHealthStatus;
//
// Processor Upgrade
//
SmbiosRecord -> ProcessorUpgrade = 0x008;
//
// Processor Family 2
//
SmbiosRecord -> ProcessorFamily2 = ForType4InputData-> VariableRecord.ProcessorFamily;
//
// Processor speed in MHz
//
Frequency = MeasureTscFrequency ();
Frequency = DivU64x32 (Frequency, 1000000);
SmbiosRecord-> CurrentSpeed = (UINT16)Frequency;
SmbiosRecord-> MaxSpeed = (UINT16)Frequency;
//
// Processor Characteristics
//
AsmCpuid(0x8000000, NULL, NULL, NULL, &Edx01);
Edx01= Edx01 >> 28;
Edx01 &= 0x01;
SmbiosRecord-> ProcessorCharacteristics= (UINT16)Edx01;
//
// Processor Core Count and Enabled core count
//
Status = gBS->LocateProtocol (
&gEfiMpServiceProtocolGuid,
NULL,
(void **)&MpService
);
if (!EFI_ERROR (Status)) {
//
// Determine the number of processors
//
MpService->GetNumberOfProcessors (
MpService,
&NumberOfProcessors,
&NumberOfEnabledProcessors
);
}
SmbiosRecord-> CoreCount= (UINT8)NumberOfProcessors;
SmbiosRecord-> EnabledCoreCount= (UINT8)NumberOfEnabledProcessors;
SmbiosRecord-> ThreadCount= (UINT8)NumberOfEnabledProcessors;
SmbiosRecord-> ProcessorCharacteristics = 0x2; // Unknown
//
// Processor Cache Handle
//
GetCacheHandle( Smbios,1, &L1CacheHandle);
GetCacheHandle( Smbios,2, &L2CacheHandle);
GetCacheHandle( Smbios,3, &L3CacheHandle);
//
// Updating Cache Handle Information
//
SmbiosRecord->L1CacheHandle = L1CacheHandle;
SmbiosRecord->L2CacheHandle = L2CacheHandle;
SmbiosRecord->L3CacheHandle = L3CacheHandle;
OptionalStrStart = (CHAR8 *)(SmbiosRecord + 1);
UnicodeStrToAsciiStrS (Socket, OptionalStrStart, OptionalStrSize);
OptionalStrStart += (SocketStrLen + 1);
OptionalStrSize -= (SocketStrLen + 1);
UnicodeStrToAsciiStrS (Manufacturer, OptionalStrStart, OptionalStrSize);
OptionalStrStart += (ManufacturerStrLen + 1);
OptionalStrSize -= (ManufacturerStrLen + 1);
UnicodeStrToAsciiStrS (Version, OptionalStrStart, OptionalStrSize);
OptionalStrStart += (VersionStrLen + 1);
OptionalStrSize -= (VersionStrLen + 1);
UnicodeStrToAsciiStrS (SerialNumber, OptionalStrStart, OptionalStrSize);
OptionalStrStart += (SerialNumberStrLen + 1);
OptionalStrSize -= (SerialNumberStrLen + 1);
UnicodeStrToAsciiStrS (AssetTag, OptionalStrStart, OptionalStrSize);
OptionalStrStart += (AssetTagStrLen + 1);
OptionalStrSize -= (AssetTagStrLen + 1);
UnicodeStrToAsciiStrS (PartNumber, OptionalStrStart, OptionalStrSize);
//
// Now we have got the full Smbios record, call Smbios protocol to add this record.
//
SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
Status = Smbios-> Add(
Smbios,
NULL,
&SmbiosHandle,
(EFI_SMBIOS_TABLE_HEADER *) SmbiosRecord
);
if (EFI_ERROR (Status)) return Status;
FreePool(SmbiosRecord);
return Status;
}