Skip to content
Snippets Groups Projects
llprocessor.cpp 90.1 KiB
Newer Older
James Cook's avatar
James Cook committed
				case 6:
					CPUInfo._L2.uiAssociativeWays = 8;
					break;
				case 8:
					CPUInfo._L2.uiAssociativeWays = 16;
					break;
				case 0xF:
					CPUInfo._L2.uiAssociativeWays = (unsigned int) -1;
					break;
				default:
					CPUInfo._L2.uiAssociativeWays = 0;
					break;
			}
			CPUInfo._L2.uiLineSize = ecxreg & 0xFF;
		}
	}
	else
	{
		// If we could not detect the ext. CPUID level 0x80000006 we
		// try to read the standard processor configuration.
		GetStandardProcessorConfiguration();
	}
	// After reading we translate the configuration to strings
	TranslateProcessorConfiguration();

	// And finally exit
	return true;
#else
	return FALSE;
#endif
}

// bool CProcessor::AnalyzeUnknownProcessor()
// ==========================================
// Private class function to analyze an unknown (No Intel or AMD) processor
///////////////////////////////////////////////////////////////////////////
bool CProcessor::AnalyzeUnknownProcessor()
{
#if LL_WINDOWS
	unsigned long eaxreg, ebxreg;

	// We check if the CPUID command is available
	if (!CheckCPUIDPresence())
		return false;

	// First of all we read the standard CPUID level 0x00000001
	// This level should be available on every x86-processor clone
	__asm
	{
        mov eax, 1
		cpuid
		mov eaxreg, eax
		mov ebxreg, ebx
	}
	// Then we mask the processor model, family, type and stepping
	CPUInfo.uiStepping = eaxreg & 0xF;
	CPUInfo.uiModel    = (eaxreg >> 4) & 0xF;
	CPUInfo.uiFamily   = (eaxreg >> 8) & 0xF;
	CPUInfo.uiType     = (eaxreg >> 12) & 0x3;

	// To have complete information we also mask the brand id
	CPUInfo.uiBrandID  = ebxreg & 0xF;

	// Then we get the standard processor extensions
	GetStandardProcessorExtensions();

	// Now we mark everything we do not know as unknown
	strcpy(strCPUName, "Unknown");		/*Flawfinder: ignore*/

	strcpy(CPUInfo._Data.strTLB, "Unknown");	/*Flawfinder: ignore*/
	strcpy(CPUInfo._Instruction.strTLB, "Unknown");		/*Flawfinder: ignore*/
	
	strcpy(CPUInfo._Trace.strCache, "Unknown");		/*Flawfinder: ignore*/
	strcpy(CPUInfo._L1.Data.strCache, "Unknown");		/*Flawfinder: ignore*/
	strcpy(CPUInfo._L1.Instruction.strCache, "Unknown");	/*Flawfinder: ignore*/
	strcpy(CPUInfo._L2.strCache, "Unknown");		/*Flawfinder: ignore*/
	strcpy(CPUInfo._L3.strCache, "Unknown");		/*Flawfinder: ignore*/

	strcpy(CPUInfo.strProcessorSerial, "Unknown / Not supported");	/*Flawfinder: ignore*/

	// For the family, model and brand id we can only print the numeric value
Karen Clark's avatar
Karen Clark committed
	snprintf(CPUInfo.strBrandID, sizeof(CPUInfo.strBrandID), "Brand-ID number %d", CPUInfo.uiBrandID);		/* Flawfinder: ignore */
	snprintf(CPUInfo.strFamily, sizeof(CPUInfo.strFamily), "Family number %d", CPUInfo.uiFamily);		/* Flawfinder: ignore */
	snprintf(CPUInfo.strModel, sizeof(CPUInfo.strModel), "Model number %d", CPUInfo.uiModel);		/* Flawfinder: ignore */
James Cook's avatar
James Cook committed

	// And thats it
	return true;
#else
	return FALSE;
#endif
}

// bool CProcessor::CheckCPUIDPresence()
// =====================================
// This function checks if the CPUID command is available on the current
// processor
////////////////////////////////////////////////////////////////////////
bool CProcessor::CheckCPUIDPresence()
{
#if LL_WINDOWS
	unsigned long BitChanged;
	
	// We've to check if we can toggle the flag register bit 21
	// If we can't the processor does not support the CPUID command
	__asm
	{
		pushfd
		pop eax
		mov ebx, eax
		xor eax, 0x00200000 
		push eax
		popfd
		pushfd
		pop eax
		xor eax,ebx 
		mov BitChanged, eax
	}

	return ((BitChanged) ? true : false);
#else
	return FALSE;
#endif
}

// void CProcessor::DecodeProcessorConfiguration(unsigned int cfg)
// ===============================================================
// This function (or switch ?!) just translates a one-byte processor configuration
// byte to understandable values
//////////////////////////////////////////////////////////////////////////////////
void CProcessor::DecodeProcessorConfiguration(unsigned int cfg)
{
	// First we ensure that there's only one single byte
	cfg &= 0xFF;

	// Then we do a big switch
	switch(cfg)
	{
		case 0:			// cfg = 0:  Unused
			break;
		case 0x1:		// cfg = 0x1:  code TLB present, 4 KB pages, 4 ways, 32 entries
			CPUInfo._Instruction.bPresent = true;
			strcpy(CPUInfo._Instruction.strPageSize, "4 KB");	/*Flawfinder: ignore*/
			CPUInfo._Instruction.uiAssociativeWays = 4;
			CPUInfo._Instruction.uiEntries = 32;
			break;
		case 0x2:		// cfg = 0x2:  code TLB present, 4 MB pages, fully associative, 2 entries
			CPUInfo._Instruction.bPresent = true;
			strcpy(CPUInfo._Instruction.strPageSize, "4 MB");	/*Flawfinder: ignore*/
			CPUInfo._Instruction.uiAssociativeWays = 4;
			CPUInfo._Instruction.uiEntries = 2;
			break;
		case 0x3:		// cfg = 0x3:  data TLB present, 4 KB pages, 4 ways, 64 entries
			CPUInfo._Data.bPresent = true;
			strcpy(CPUInfo._Data.strPageSize, "4 KB");		/*Flawfinder: ignore*/
			CPUInfo._Data.uiAssociativeWays = 4;
			CPUInfo._Data.uiEntries = 64;
			break;
		case 0x4:		// cfg = 0x4:  data TLB present, 4 MB pages, 4 ways, 8 entries
			CPUInfo._Data.bPresent = true;
			strcpy(CPUInfo._Data.strPageSize, "4 MB");	/*Flawfinder: ignore*/
			CPUInfo._Data.uiAssociativeWays = 4;
			CPUInfo._Data.uiEntries = 8;
			break;
		case 0x6:		// cfg = 0x6:  code L1 cache present, 8 KB, 4 ways, 32 byte lines
			CPUInfo._L1.Instruction.bPresent = true;
			strcpy(CPUInfo._L1.Instruction.strSize, "8 KB");	/*Flawfinder: ignore*/
			CPUInfo._L1.Instruction.uiAssociativeWays = 4;
			CPUInfo._L1.Instruction.uiLineSize = 32;
			break;
		case 0x8:		// cfg = 0x8:  code L1 cache present, 16 KB, 4 ways, 32 byte lines
			CPUInfo._L1.Instruction.bPresent = true;
			strcpy(CPUInfo._L1.Instruction.strSize, "16 KB");	/*Flawfinder: ignore*/
			CPUInfo._L1.Instruction.uiAssociativeWays = 4;
			CPUInfo._L1.Instruction.uiLineSize = 32;
			break;
		case 0xA:		// cfg = 0xA:  data L1 cache present, 8 KB, 2 ways, 32 byte lines
			CPUInfo._L1.Data.bPresent = true;
			strcpy(CPUInfo._L1.Data.strSize, "8 KB");	/*Flawfinder: ignore*/
			CPUInfo._L1.Data.uiAssociativeWays = 2;
			CPUInfo._L1.Data.uiLineSize = 32;
			break;
		case 0xC:		// cfg = 0xC:  data L1 cache present, 16 KB, 4 ways, 32 byte lines
			CPUInfo._L1.Data.bPresent = true;
			strcpy(CPUInfo._L1.Data.strSize, "16 KB");	/*Flawfinder: ignore*/
			CPUInfo._L1.Data.uiAssociativeWays = 4;
			CPUInfo._L1.Data.uiLineSize = 32;
			break;
		case 0x22:		// cfg = 0x22:  code and data L3 cache present, 512 KB, 4 ways, 64 byte lines, sectored
			CPUInfo._L3.bPresent = true;
			strcpy(CPUInfo._L3.strSize, "512 KB");	/*Flawfinder: ignore*/
			CPUInfo._L3.uiAssociativeWays = 4;
			CPUInfo._L3.uiLineSize = 64;
			CPUInfo._L3.bSectored = true;
			break;
		case 0x23:		// cfg = 0x23:  code and data L3 cache present, 1024 KB, 8 ways, 64 byte lines, sectored
			CPUInfo._L3.bPresent = true;
			strcpy(CPUInfo._L3.strSize, "1024 KB");	/*Flawfinder: ignore*/
			CPUInfo._L3.uiAssociativeWays = 8;
			CPUInfo._L3.uiLineSize = 64;
			CPUInfo._L3.bSectored = true;
			break;
		case 0x25:		// cfg = 0x25:  code and data L3 cache present, 2048 KB, 8 ways, 64 byte lines, sectored
			CPUInfo._L3.bPresent = true;
			strcpy(CPUInfo._L3.strSize, "2048 KB");	/*Flawfinder: ignore*/
			CPUInfo._L3.uiAssociativeWays = 8;
			CPUInfo._L3.uiLineSize = 64;
			CPUInfo._L3.bSectored = true;
			break;
		case 0x29:		// cfg = 0x29:  code and data L3 cache present, 4096 KB, 8 ways, 64 byte lines, sectored
			CPUInfo._L3.bPresent = true;
			strcpy(CPUInfo._L3.strSize, "4096 KB");	/*Flawfinder: ignore*/
			CPUInfo._L3.uiAssociativeWays = 8;
			CPUInfo._L3.uiLineSize = 64;
			CPUInfo._L3.bSectored = true;
			break;
		case 0x40:		// cfg = 0x40:  no integrated L2 cache (P6 core) or L3 cache (P4 core)
			break;
		case 0x41:		// cfg = 0x41:  code and data L2 cache present, 128 KB, 4 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "128 KB");		/*Flawfinder: ignore*/
			CPUInfo._L2.uiAssociativeWays = 4;
			CPUInfo._L2.uiLineSize = 32;
			break;
		case 0x42:		// cfg = 0x42:  code and data L2 cache present, 256 KB, 4 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "256 KB");		/*Flawfinder: ignore*/
			CPUInfo._L2.uiAssociativeWays = 4;
			CPUInfo._L2.uiLineSize = 32;
			break;
		case 0x43:		// cfg = 0x43:  code and data L2 cache present, 512 KB, 4 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "512 KB");		/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 4;
			CPUInfo._L2.uiLineSize = 32;
			break;
		case 0x44:		// cfg = 0x44:  code and data L2 cache present, 1024 KB, 4 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "1 MB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 4;
			CPUInfo._L2.uiLineSize = 32;
			break;
		case 0x45:		// cfg = 0x45:  code and data L2 cache present, 2048 KB, 4 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "2 MB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 4;
			CPUInfo._L2.uiLineSize = 32;
			break;
		case 0x50:		// cfg = 0x50:  code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 64 entries
			CPUInfo._Instruction.bPresent = true;
			strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB");	/* Flawfinder: ignore */
			CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
			CPUInfo._Instruction.uiEntries = 64;
			break;
		case 0x51:		// cfg = 0x51:  code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 128 entries
			CPUInfo._Instruction.bPresent = true;
			strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB");	/* Flawfinder: ignore */
			CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
			CPUInfo._Instruction.uiEntries = 128;
			break;
		case 0x52:		// cfg = 0x52:  code TLB present, 4 KB / 4 MB / 2 MB pages, fully associative, 256 entries
			CPUInfo._Instruction.bPresent = true;
			strcpy(CPUInfo._Instruction.strPageSize, "4 KB / 2 MB / 4 MB");	/* Flawfinder: ignore */
			CPUInfo._Instruction.uiAssociativeWays = (unsigned int) -1;
			CPUInfo._Instruction.uiEntries = 256;
			break;
		case 0x5B:		// cfg = 0x5B:  data TLB present, 4 KB / 4 MB pages, fully associative, 64 entries
			CPUInfo._Data.bPresent = true;
			strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB");	/* Flawfinder: ignore */
			CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
			CPUInfo._Data.uiEntries = 64;
			break;
		case 0x5C:		// cfg = 0x5C:  data TLB present, 4 KB / 4 MB pages, fully associative, 128 entries
			CPUInfo._Data.bPresent = true;
			strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB");	/* Flawfinder: ignore */
			CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
			CPUInfo._Data.uiEntries = 128;
			break;
		case 0x5d:		// cfg = 0x5D:  data TLB present, 4 KB / 4 MB pages, fully associative, 256 entries
			CPUInfo._Data.bPresent = true;
			strcpy(CPUInfo._Data.strPageSize, "4 KB / 4 MB");	/* Flawfinder: ignore */
			CPUInfo._Data.uiAssociativeWays = (unsigned int) -1;
			CPUInfo._Data.uiEntries = 256;
			break;
		case 0x66:		// cfg = 0x66:  data L1 cache present, 8 KB, 4 ways, 64 byte lines, sectored
			CPUInfo._L1.Data.bPresent = true;
			strcpy(CPUInfo._L1.Data.strSize, "8 KB");	/* Flawfinder: ignore */
			CPUInfo._L1.Data.uiAssociativeWays = 4;
			CPUInfo._L1.Data.uiLineSize = 64;
			break;
		case 0x67:		// cfg = 0x67:  data L1 cache present, 16 KB, 4 ways, 64 byte lines, sectored
			CPUInfo._L1.Data.bPresent = true;
			strcpy(CPUInfo._L1.Data.strSize, "16 KB");	/* Flawfinder: ignore */
			CPUInfo._L1.Data.uiAssociativeWays = 4;
			CPUInfo._L1.Data.uiLineSize = 64;
			break;
		case 0x68:		// cfg = 0x68:  data L1 cache present, 32 KB, 4 ways, 64 byte lines, sectored
			CPUInfo._L1.Data.bPresent = true;
			strcpy(CPUInfo._L1.Data.strSize, "32 KB");	/* Flawfinder: ignore */
			CPUInfo._L1.Data.uiAssociativeWays = 4;
			CPUInfo._L1.Data.uiLineSize = 64;
			break;
		case 0x70:		// cfg = 0x70:  trace L1 cache present, 12 KuOPs, 4 ways
James Cook's avatar
James Cook committed
			CPUInfo._Trace.bPresent = true;
			strcpy(CPUInfo._Trace.strSize, "12 K-micro-ops");	/* Flawfinder: ignore */
			CPUInfo._Trace.uiAssociativeWays = 4;
			break;
		case 0x71:		// cfg = 0x71:  trace L1 cache present, 16 KuOPs, 4 ways
James Cook's avatar
James Cook committed
			CPUInfo._Trace.bPresent = true;
			strcpy(CPUInfo._Trace.strSize, "16 K-micro-ops");	/* Flawfinder: ignore */
			CPUInfo._Trace.uiAssociativeWays = 4;
			break;
		case 0x72:		// cfg = 0x72:  trace L1 cache present, 32 KuOPs, 4 ways
James Cook's avatar
James Cook committed
			CPUInfo._Trace.bPresent = true;
			strcpy(CPUInfo._Trace.strSize, "32 K-micro-ops");	/* Flawfinder: ignore */
			CPUInfo._Trace.uiAssociativeWays = 4;
			break;
		case 0x79:		// cfg = 0x79:  code and data L2 cache present, 128 KB, 8 ways, 64 byte lines, sectored
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "128 KB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 8;
			CPUInfo._L2.uiLineSize = 64;
			CPUInfo._L2.bSectored = true;
			break;
		case 0x7A:		// cfg = 0x7A:  code and data L2 cache present, 256 KB, 8 ways, 64 byte lines, sectored
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "256 KB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 8;
			CPUInfo._L2.uiLineSize = 64;
			CPUInfo._L2.bSectored = true;
			break;
		case 0x7B:		// cfg = 0x7B:  code and data L2 cache present, 512 KB, 8 ways, 64 byte lines, sectored
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "512 KB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 8;
			CPUInfo._L2.uiLineSize = 64;
			CPUInfo._L2.bSectored = true;
			break;
		case 0x7C:		// cfg = 0x7C:  code and data L2 cache present, 1024 KB, 8 ways, 64 byte lines, sectored
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "1 MB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 8;
			CPUInfo._L2.uiLineSize = 64;
			CPUInfo._L2.bSectored = true;
			break;
		case 0x81:		// cfg = 0x81:  code and data L2 cache present, 128 KB, 8 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "128 KB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 8;
			CPUInfo._L2.uiLineSize = 32;
			break;
		case 0x82:		// cfg = 0x82:  code and data L2 cache present, 256 KB, 8 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "256 KB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 8;
			CPUInfo._L2.uiLineSize = 32;
			break;
		case 0x83:		// cfg = 0x83:  code and data L2 cache present, 512 KB, 8 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "512 KB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 8;
			CPUInfo._L2.uiLineSize = 32;
			break;
		case 0x84:		// cfg = 0x84:  code and data L2 cache present, 1024 KB, 8 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "1 MB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 8;
			CPUInfo._L2.uiLineSize = 32;
			break;
		case 0x85:		// cfg = 0x85:  code and data L2 cache present, 2048 KB, 8 ways, 32 byte lines
			CPUInfo._L2.bPresent = true;
			strcpy(CPUInfo._L2.strSize, "2 MB");	/* Flawfinder: ignore */
			CPUInfo._L2.uiAssociativeWays = 8;
			CPUInfo._L2.uiLineSize = 32;
			break;
	}
}

FORCEINLINE static char *TranslateAssociativeWays(unsigned int uiWays, char *buf)
{
	// We define 0xFFFFFFFF (= -1) as fully associative
    if (uiWays == ((unsigned int) -1))
		strcpy(buf, "fully associative");	/* Flawfinder: ignore */
	else
	{
		if (uiWays == 1)			// A one way associative cache is just direct mapped
			strcpy(buf, "direct mapped");	/* Flawfinder: ignore */
		else if (uiWays == 0)		// This should not happen...
			strcpy(buf, "unknown associative ways");	/* Flawfinder: ignore */
		else						// The x-way associative cache
			sprintf(buf, "%d ways associative", uiWays);	/* Flawfinder: ignore */
	}
	// To ease the function use we return the buffer
	return buf;
}
FORCEINLINE static void TranslateTLB(ProcessorTLB *tlb)
{
	char buf[64];	/* Flawfinder: ignore */

	// We just check if the TLB is present
	if (tlb->bPresent)
        snprintf(tlb->strTLB,sizeof(tlb->strTLB), "%s page size, %s, %d entries", tlb->strPageSize, TranslateAssociativeWays(tlb->uiAssociativeWays, buf), tlb->uiEntries);	/* Flawfinder: ignore */
	else
        strcpy(tlb->strTLB, "Not present");	/* Flawfinder: ignore */
}
FORCEINLINE static void TranslateCache(ProcessorCache *cache)
{
	char buf[64];	/* Flawfinder: ignore */

	// We just check if the cache is present
    if (cache->bPresent)
	{
		// If present we construct the string
		snprintf(cache->strCache, sizeof(cache->strCache), "%s cache size, %s, %d bytes line size", cache->strSize, TranslateAssociativeWays(cache->uiAssociativeWays, buf), cache->uiLineSize);	/* Flawfinder: ignore */
		if (cache->bSectored)
			strncat(cache->strCache, ", sectored", sizeof(cache->strCache)-strlen(cache->strCache)-1);	/* Flawfinder: ignore */
	}
	else
	{
		// Else we just say "Not present"
		strcpy(cache->strCache, "Not present");	/* Flawfinder: ignore */
	}
}

// void CProcessor::TranslateProcessorConfiguration()
// ==================================================
// Private class function to translate the processor configuration values
// to strings
/////////////////////////////////////////////////////////////////////////
void CProcessor::TranslateProcessorConfiguration()
{
	// We just call the small functions defined above
	TranslateTLB(&CPUInfo._Data);
	TranslateTLB(&CPUInfo._Instruction);

	TranslateCache(&CPUInfo._Trace);

	TranslateCache(&CPUInfo._L1.Instruction);
	TranslateCache(&CPUInfo._L1.Data);
	TranslateCache(&CPUInfo._L2);
	TranslateCache(&CPUInfo._L3);
}

// void CProcessor::GetStandardProcessorConfiguration()
// ====================================================
// Private class function to read the standard processor configuration
//////////////////////////////////////////////////////////////////////
void CProcessor::GetStandardProcessorConfiguration()
{
#if LL_WINDOWS
	unsigned long eaxreg, ebxreg, ecxreg, edxreg;

	// We check if the CPUID function is available
	if (!CheckCPUIDPresence())
		return;

	// First we check if the processor supports the standard
	// CPUID level 0x00000002
	if (CPUInfo.MaxSupportedLevel >= 2)
	{
		// Now we go read the std. CPUID level 0x00000002 the first time
		unsigned long count, num = 255;
		for (count = 0; count < num; count++)
		{
			__asm
			{
				mov eax, 2
				cpuid
				mov eaxreg, eax
				mov ebxreg, ebx
				mov ecxreg, ecx
				mov edxreg, edx
			}
			// We have to repeat this reading for 'num' times
			num = eaxreg & 0xFF;

			// Then we call the big decode switch function
			DecodeProcessorConfiguration(eaxreg >> 8);
			DecodeProcessorConfiguration(eaxreg >> 16);
			DecodeProcessorConfiguration(eaxreg >> 24);

			// If ebx contains additional data we also decode it
			if ((ebxreg & 0x80000000) == 0)
			{
				DecodeProcessorConfiguration(ebxreg);
				DecodeProcessorConfiguration(ebxreg >> 8);
				DecodeProcessorConfiguration(ebxreg >> 16);
				DecodeProcessorConfiguration(ebxreg >> 24);
			}
			// And also the ecx register
			if ((ecxreg & 0x80000000) == 0)
			{
				DecodeProcessorConfiguration(ecxreg);
				DecodeProcessorConfiguration(ecxreg >> 8);
				DecodeProcessorConfiguration(ecxreg >> 16);
				DecodeProcessorConfiguration(ecxreg >> 24);
			}
			// At last the edx processor register
			if ((edxreg & 0x80000000) == 0)
			{
				DecodeProcessorConfiguration(edxreg);
				DecodeProcessorConfiguration(edxreg >> 8);
				DecodeProcessorConfiguration(edxreg >> 16);
				DecodeProcessorConfiguration(edxreg >> 24);
			}
		}
	}
#endif
}

// void CProcessor::GetStandardProcessorExtensions()
// =================================================
// Private class function to read the standard processor extensions
///////////////////////////////////////////////////////////////////
void CProcessor::GetStandardProcessorExtensions()
{
#if LL_WINDOWS
	unsigned long ebxreg, edxreg;

	// We check if the CPUID command is available
	if (!CheckCPUIDPresence())
		return;
	// We just get the standard CPUID level 0x00000001 which should be
	// available on every x86 processor
	__asm
	{
		mov eax, 1
		cpuid
		mov ebxreg, ebx
		mov edxreg, edx
	}
    
	// Then we mask some bits
	CPUInfo._Ext.FPU_FloatingPointUnit							= CheckBit(edxreg, 0);
	CPUInfo._Ext.VME_Virtual8086ModeEnhancements				= CheckBit(edxreg, 1);
	CPUInfo._Ext.DE_DebuggingExtensions							= CheckBit(edxreg, 2);
	CPUInfo._Ext.PSE_PageSizeExtensions							= CheckBit(edxreg, 3);
	CPUInfo._Ext.TSC_TimeStampCounter							= CheckBit(edxreg, 4);
	CPUInfo._Ext.MSR_ModelSpecificRegisters						= CheckBit(edxreg, 5);
	CPUInfo._Ext.PAE_PhysicalAddressExtension					= CheckBit(edxreg, 6);
	CPUInfo._Ext.MCE_MachineCheckException						= CheckBit(edxreg, 7);
	CPUInfo._Ext.CX8_COMPXCHG8B_Instruction						= CheckBit(edxreg, 8);
	CPUInfo._Ext.APIC_AdvancedProgrammableInterruptController	= CheckBit(edxreg, 9);
	CPUInfo._Ext.APIC_ID = (ebxreg >> 24) & 0xFF;
	CPUInfo._Ext.SEP_FastSystemCall								= CheckBit(edxreg, 11);
	CPUInfo._Ext.MTRR_MemoryTypeRangeRegisters					= CheckBit(edxreg, 12);
	CPUInfo._Ext.PGE_PTE_GlobalFlag								= CheckBit(edxreg, 13);
	CPUInfo._Ext.MCA_MachineCheckArchitecture					= CheckBit(edxreg, 14);
	CPUInfo._Ext.CMOV_ConditionalMoveAndCompareInstructions		= CheckBit(edxreg, 15);
	CPUInfo._Ext.FGPAT_PageAttributeTable						= CheckBit(edxreg, 16);
	CPUInfo._Ext.PSE36_36bitPageSizeExtension					= CheckBit(edxreg, 17);
	CPUInfo._Ext.PN_ProcessorSerialNumber						= CheckBit(edxreg, 18);
	CPUInfo._Ext.CLFSH_CFLUSH_Instruction						= CheckBit(edxreg, 19);
	CPUInfo._Ext.CLFLUSH_InstructionCacheLineSize = (ebxreg >> 8) & 0xFF;
	CPUInfo._Ext.DS_DebugStore									= CheckBit(edxreg, 21);
	CPUInfo._Ext.ACPI_ThermalMonitorAndClockControl				= CheckBit(edxreg, 22);
	CPUInfo._Ext.MMX_MultimediaExtensions						= CheckBit(edxreg, 23);
	CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore	= CheckBit(edxreg, 24);
	CPUInfo._Ext.SSE_StreamingSIMD_Extensions					= CheckBit(edxreg, 25);
	CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions					= CheckBit(edxreg, 26);
	CPUInfo._Ext.Altivec_Extensions = false;
James Cook's avatar
James Cook committed
	CPUInfo._Ext.SS_SelfSnoop									= CheckBit(edxreg, 27);
	CPUInfo._Ext.HT_HyperThreading								= CheckBit(edxreg, 28);
	CPUInfo._Ext.HT_HyterThreadingSiblings = (ebxreg >> 16) & 0xFF;
	CPUInfo._Ext.TM_ThermalMonitor								= CheckBit(edxreg, 29);
	CPUInfo._Ext.IA64_Intel64BitArchitecture					= CheckBit(edxreg, 30);
#endif
}

// const ProcessorInfo *CProcessor::GetCPUInfo()
// =============================================
// Calls all the other detection function to create an detailed
// processor information
///////////////////////////////////////////////////////////////
const ProcessorInfo *CProcessor::GetCPUInfo()
{
#if LL_WINDOWS
	unsigned long eaxreg, ebxreg, ecxreg, edxreg;
 
	// First of all we check if the CPUID command is available
	if (!CheckCPUIDPresence())
		return NULL;

	// We read the standard CPUID level 0x00000000 which should
	// be available on every x86 processor
	__asm
	{
		mov eax, 0
		cpuid
		mov eaxreg, eax
		mov ebxreg, ebx
		mov edxreg, edx
		mov ecxreg, ecx
	}
	// Then we connect the single register values to the vendor string
	*((unsigned long *) CPUInfo.strVendor) = ebxreg;
	*((unsigned long *) (CPUInfo.strVendor+4)) = edxreg;
	*((unsigned long *) (CPUInfo.strVendor+8)) = ecxreg;
	// Null terminate for string comparisons below.
	CPUInfo.strVendor[12] = 0;
James Cook's avatar
James Cook committed

	// We can also read the max. supported standard CPUID level
	CPUInfo.MaxSupportedLevel = eaxreg & 0xFFFF;

	// Then we read the ext. CPUID level 0x80000000
	__asm
	{
        mov eax, 0x80000000
		cpuid
		mov eaxreg, eax
	}
	// ...to check the max. supportted extended CPUID level
	CPUInfo.MaxSupportedExtendedLevel = eaxreg;

	// Then we switch to the specific processor vendors
	// See http://www.sandpile.org/ia32/cpuid.htm
	if (!strcmp(CPUInfo.strVendor, "GenuineIntel"))
James Cook's avatar
James Cook committed
	{
		AnalyzeIntelProcessor();
	}
	else if (!strcmp(CPUInfo.strVendor, "AuthenticAMD"))
	{
		AnalyzeAMDProcessor();
	}
	else if (!strcmp(CPUInfo.strVendor, "UMC UMC UMC"))
	{
		AnalyzeUnknownProcessor();
	}
	else if (!strcmp(CPUInfo.strVendor, "CyrixInstead"))
	{
		AnalyzeUnknownProcessor();
	}
	else if (!strcmp(CPUInfo.strVendor, "NexGenDriven"))
	{
		AnalyzeUnknownProcessor();
	}
	else if (!strcmp(CPUInfo.strVendor, "CentaurHauls"))
	{
		AnalyzeUnknownProcessor();
	}
	else if (!strcmp(CPUInfo.strVendor, "RiseRiseRise"))
	{
		AnalyzeUnknownProcessor();
	}
	else if (!strcmp(CPUInfo.strVendor, "SiS SiS SiS"))
	{
		AnalyzeUnknownProcessor();
	}
	else if (!strcmp(CPUInfo.strVendor, "GenuineTMx86"))
	{
		// Transmeta
		AnalyzeUnknownProcessor();
	}
	else if (!strcmp(CPUInfo.strVendor, "Geode by NSC"))
	{
		AnalyzeUnknownProcessor();
	}
	else
	{
		AnalyzeUnknownProcessor();
James Cook's avatar
James Cook committed
	}
#endif
	// After all we return the class CPUInfo member var
	return (&CPUInfo);
}

#if defined(__i386)
#include <sys/auxv.h>
#endif

// ======================
// Class constructor:
/////////////////////////
CProcessor::CProcessor()
{
	uqwFrequency = 0;
	strCPUName[0] = 0;
	memset(&CPUInfo, 0, sizeof(CPUInfo));
}

// unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
// =========================================================================
// Function to query the current CPU frequency
////////////////////////////////////////////////////////////////////////////
F64 CProcessor::GetCPUFrequency(unsigned int /*uiMeasureMSecs*/)
{
	if(uqwFrequency == 0){
		GetCPUInfo();
	}

	return uqwFrequency;
}

// const ProcessorInfo *CProcessor::GetCPUInfo()
// =============================================
// Calls all the other detection function to create an detailed
// processor information
///////////////////////////////////////////////////////////////
const ProcessorInfo *CProcessor::GetCPUInfo()
{
					// In Solaris the CPU info is in the kstats
					// try "psrinfo" or "kstat cpu_info" to see all
					// that's available
	int ncpus=0, i; 
	kstat_ctl_t	*kc;
	kstat_t 	*ks;
	kstat_named_t   *ksinfo, *ksi;
	kstat_t 	*CPU_stats_list;

	kc = kstat_open();

	if((int)kc == -1){
		llwarns << "kstat_open(0 failed!" << llendl;
		return (&CPUInfo);
	}

	for (ks = kc->kc_chain; ks != NULL; ks = ks->ks_next) {
		if (strncmp(ks->ks_module, "cpu_info", 8) == 0 &&
			strncmp(ks->ks_name, "cpu_info", 8) == 0)
			ncpus++;
	}
	
	if(ncpus < 1){
		llwarns << "No cpus found in kstats!" << llendl;
		return (&CPUInfo);
	}

	for (ks = kc->kc_chain; ks; ks = ks->ks_next) {
		if (strncmp(ks->ks_module, "cpu_info", 8) == 0 
		&&  strncmp(ks->ks_name, "cpu_info", 8) == 0 
		&&  kstat_read(kc, ks, NULL) != -1){     
			CPU_stats_list = ks;	// only looking at the first CPU
			
			break;
		}
	}

	if(ncpus > 1)
        	snprintf(strCPUName, sizeof(strCPUName), "%d x ", ncpus); 

	kstat_read(kc, CPU_stats_list, NULL);
	ksinfo = (kstat_named_t *)CPU_stats_list->ks_data;
	for(i=0; i < (int)(CPU_stats_list->ks_ndata); ++i){ // Walk the kstats for this cpu gathering what we need
		ksi = ksinfo++;
		if(!strcmp(ksi->name, "brand")){
			strncat(strCPUName, (char *)KSTAT_NAMED_STR_PTR(ksi),
				sizeof(strCPUName)-strlen(strCPUName)-1);
			strncat(CPUInfo.strFamily, (char *)KSTAT_NAMED_STR_PTR(ksi),
				sizeof(CPUInfo.strFamily)-strlen(CPUInfo.strFamily)-1);
			strncpy(CPUInfo.strBrandID, strCPUName,sizeof(CPUInfo.strBrandID)-1);
			CPUInfo.strBrandID[sizeof(CPUInfo.strBrandID)-1]='\0';
			// DEBUG llinfos << "CPU brand: " << strCPUName << llendl;
			continue;
		}

		if(!strcmp(ksi->name, "clock_MHz")){
#if defined(__sparc)
			llinfos << "Raw kstat clock rate is: " << ksi->value.l << llendl;
			uqwFrequency = (F64)(ksi->value.l * 1000000);
#else
			uqwFrequency = (F64)(ksi->value.i64 * 1000000);
#endif
			//DEBUG llinfos << "CPU frequency: " << uqwFrequency << llendl;
			continue;
		}

#if defined(__i386)
		if(!strcmp(ksi->name, "vendor_id")){
			strncpy(CPUInfo.strVendor, (char *)KSTAT_NAMED_STR_PTR(ksi), sizeof(CPUInfo.strVendor)-1);
			// DEBUG llinfos << "CPU vendor: " << CPUInfo.strVendor << llendl;
			continue;
		}
#endif
	}

	kstat_close(kc);

#if defined(__sparc)		// SPARC does not define a vendor string in kstat
	strncpy(CPUInfo.strVendor, "Sun Microsystems, Inc.", sizeof(CPUInfo.strVendor)-1);
#endif

	// DEBUG llinfo << "The system has " << ncpus << " CPUs with a clock rate of " <<  uqwFrequency << "MHz." << llendl;
	
#if defined (__i386)  //  we really don't care about the CPU extensions on SPARC but on x86...

	// Now get cpu extensions

	uint_t ui;

	(void) getisax(&ui, 1);
	
	if(ui & AV_386_FPU)
		CPUInfo._Ext.FPU_FloatingPointUnit = true;
	if(ui & AV_386_CX8)
		CPUInfo._Ext.CX8_COMPXCHG8B_Instruction = true;
	if(ui & AV_386_MMX)
		CPUInfo._Ext.MMX_MultimediaExtensions = true;
	if(ui & AV_386_AMD_MMX)
		CPUInfo._Ext.MMX_MultimediaExtensions = true;
	if(ui & AV_386_FXSR)
		CPUInfo._Ext.FXSR_FastStreamingSIMD_ExtensionsSaveRestore = true;
	if(ui & AV_386_SSE)
		 CPUInfo._Ext.SSE_StreamingSIMD_Extensions = true;
	if(ui & AV_386_SSE2)
		CPUInfo._Ext.SSE2_StreamingSIMD2_Extensions = true;
/* Left these here since they may get used later
	if(ui & AV_386_SSE3)
		CPUInfo._Ext.... = true;
	if(ui & AV_386_AMD_3DNow)
		CPUInfo._Ext.... = true;
	if(ui & AV_386_AMD_3DNowx)
		CPUInfo._Ext.... = true;
*/
#endif
James Cook's avatar
James Cook committed
#else
// LL_DARWIN

#include <mach/machine.h>
#include <sys/sysctl.h>

static char *TranslateAssociativeWays(unsigned int uiWays, char *buf)
{
	// We define 0xFFFFFFFF (= -1) as fully associative
    if (uiWays == ((unsigned int) -1))
		strcpy(buf, "fully associative");	/* Flawfinder: ignore */
	else
	{
		if (uiWays == 1)			// A one way associative cache is just direct mapped
			strcpy(buf, "direct mapped");	/* Flawfinder: ignore */
		else if (uiWays == 0)		// This should not happen...
			strcpy(buf, "unknown associative ways");	/* Flawfinder: ignore */
		else						// The x-way associative cache
			sprintf(buf, "%d ways associative", uiWays);	/* Flawfinder: ignore */
	}
	// To ease the function use we return the buffer
	return buf;
}
static void TranslateTLB(ProcessorTLB *tlb)
{
	char buf[64];	/* Flawfinder: ignore */

	// We just check if the TLB is present
	if (tlb->bPresent)
        snprintf(tlb->strTLB, sizeof(tlb->strTLB), "%s page size, %s, %d entries", tlb->strPageSize, TranslateAssociativeWays(tlb->uiAssociativeWays, buf), tlb->uiEntries);	/* Flawfinder: ignore */
	else
        strcpy(tlb->strTLB, "Not present");	/* Flawfinder: ignore */
}
static void TranslateCache(ProcessorCache *cache)
{
	char buf[64];	/* Flawfinder: ignore */

	// We just check if the cache is present
    if (cache->bPresent)
	{
		// If present we construct the string
		snprintf(cache->strCache,sizeof(cache->strCache), "%s cache size, %s, %d bytes line size", cache->strSize, TranslateAssociativeWays(cache->uiAssociativeWays, buf), cache->uiLineSize);	/* Flawfinder: ignore */
		if (cache->bSectored)
			strncat(cache->strCache, ", sectored", sizeof(cache->strCache)-strlen(cache->strCache)-1);	/* Flawfinder: ignore */
	}
	else
	{
		// Else we just say "Not present"
		strcpy(cache->strCache, "Not present");	/* Flawfinder: ignore */
	}
}

// void CProcessor::TranslateProcessorConfiguration()
// ==================================================
// Private class function to translate the processor configuration values
// to strings
/////////////////////////////////////////////////////////////////////////
void CProcessor::TranslateProcessorConfiguration()
{
	// We just call the small functions defined above
	TranslateTLB(&CPUInfo._Data);
	TranslateTLB(&CPUInfo._Instruction);

	TranslateCache(&CPUInfo._Trace);

	TranslateCache(&CPUInfo._L1.Instruction);
	TranslateCache(&CPUInfo._L1.Data);
	TranslateCache(&CPUInfo._L2);
	TranslateCache(&CPUInfo._L3);
}

// CProcessor::CProcessor
// ======================
// Class constructor:
/////////////////////////
CProcessor::CProcessor()
{
	uqwFrequency = 0;
James Cook's avatar
James Cook committed
	memset(&CPUInfo, 0, sizeof(CPUInfo));
}

// unsigned __int64 CProcessor::GetCPUFrequency(unsigned int uiMeasureMSecs)
// =========================================================================
// Function to query the current CPU frequency
////////////////////////////////////////////////////////////////////////////
F64 CProcessor::GetCPUFrequency(unsigned int /*uiMeasureMSecs*/)
{
	U64 frequency = 0;
	size_t len = sizeof(frequency);
	
	if(sysctlbyname("hw.cpufrequency", &frequency, &len, NULL, 0) == 0)
	{
		uqwFrequency = (F64)frequency;
	}
	
	return uqwFrequency;
}

static bool hasFeature(const char *name)
{
	bool result = false;
	int val = 0;
	size_t len = sizeof(val);
	
	if(sysctlbyname(name, &val, &len, NULL, 0) == 0)
	{
		if(val != 0)
			result = true;
	}
	
	return result;
}

// const ProcessorInfo *CProcessor::GetCPUInfo()
// =============================================
// Calls all the other detection function to create an detailed
// processor information
///////////////////////////////////////////////////////////////
const ProcessorInfo *CProcessor::GetCPUInfo()
{
	int pagesize = 0;
	int cachelinesize = 0;
	int l1icachesize = 0;
	int l1dcachesize = 0;
	int l2settings = 0;
	int l2cachesize = 0;
	int l3settings = 0;
	int l3cachesize = 0;
	int ncpu = 0;
	int cpusubtype = 0;
	
	// sysctl knows all.
	int mib[2];
	size_t len;
	mib[0] = CTL_HW;
	
	mib[1] = HW_PAGESIZE;
	len = sizeof(pagesize);
	sysctl(mib, 2, &pagesize, &len, NULL, 0);

	mib[1] = HW_CACHELINE;
	len = sizeof(cachelinesize);
	sysctl(mib, 2, &cachelinesize, &len, NULL, 0);
	
	mib[1] = HW_L1ICACHESIZE;
	len = sizeof(l1icachesize);
	sysctl(mib, 2, &l1icachesize, &len, NULL, 0);
	
	mib[1] = HW_L1DCACHESIZE;
	len = sizeof(l1dcachesize);
	sysctl(mib, 2, &l1dcachesize, &len, NULL, 0);
	
	mib[1] = HW_L2SETTINGS;
	len = sizeof(l2settings);
	sysctl(mib, 2, &l2settings, &len, NULL, 0);
	
	mib[1] = HW_L2CACHESIZE;
	len = sizeof(l2cachesize);
	sysctl(mib, 2, &l2cachesize, &len, NULL, 0);
	
	mib[1] = HW_L3SETTINGS;
	len = sizeof(l3settings);
	sysctl(mib, 2, &l3settings, &len, NULL, 0);
	
	mib[1] = HW_L3CACHESIZE;
	len = sizeof(l3cachesize);
	sysctl(mib, 2, &l3cachesize, &len, NULL, 0);
	
	mib[1] = HW_NCPU;
	len = sizeof(ncpu);
	sysctl(mib, 2, &ncpu, &len, NULL, 0);
	
	sysctlbyname("hw.cpusubtype", &cpusubtype, &len, NULL, 0);
	
	strCPUName[0] = 0;
	
	if((ncpu == 0) || (ncpu == 1))
	{
		// Uhhh...
	}