📄 cpuident.c
字号:
/* ***** BEGIN LICENSE BLOCK *****
* Version: RCSL 1.0/RPSL 1.0
*
* Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved.
*
* The contents of this file, and the files included with this file, are
* subject to the current version of the RealNetworks Public Source License
* Version 1.0 (the "RPSL") available at
* http://www.helixcommunity.org/content/rpsl unless you have licensed
* the file under the RealNetworks Community Source License Version 1.0
* (the "RCSL") available at http://www.helixcommunity.org/content/rcsl,
* in which case the RCSL will apply. You may also obtain the license terms
* directly from RealNetworks. You may not use this file except in
* compliance with the RPSL or, if you have a valid RCSL with RealNetworks
* applicable to this file, the RCSL. Please see the applicable RPSL or
* RCSL for the rights, obligations and limitations governing use of the
* contents of the file.
*
* This file is part of the Helix DNA Technology. RealNetworks is the
* developer of the Original Code and owns the copyrights in the portions
* it created.
*
* This file, and the files included with this file, is distributed and made
* available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
*
* Technology Compatibility Kit Test Suite(s) Location:
* http://www.helixcommunity.org/content/tck
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
#include "hlxclib/string.h" // memset
#include "cpuident.h"
/*
* check if this cpu supports the cpuid instruction.
* returns: 1 if support, 0 else
*/
static int hasCPUID(void)
{
int has_cpuid = 0 ;
#if defined(_M_IX86) && defined(_MSC_VER)
/* we are compiling under MSVC, on x86 */
__asm {
pushfd // Get original EFLAGS
pop eax // Get original EFLAGS
mov ecx, eax // save original EFLAGS
xor eax, 0x200000 // Flip ID bit in EFLAGS
push eax // Save new EFLAGS value on stack
popfd // Replace current EFLAGS value
pushfd // Get new EFLAGS
pop eax // Get new EFLAGS
xor eax, ecx // did state toggle?
and eax, 0x200000
mov has_cpuid, eax
}
#endif
#if defined(__i386) && defined(__GNUC__)
/* we are compiling under gcc */
__asm__ __volatile (
"pushfl;"
"popl %%eax;"
"movl %%eax, %%ecx;"
"xorl $0x200000, %%eax;"
"pushl %%eax;"
"popfl;"
"pushfl;"
"popl %%eax;"
"xorl %%ecx, %%eax;"
"andl $0x200000, %%eax;"
"movl %%eax,%0;"
:"=m" (has_cpuid)
: /*no input*/
: "eax","ecx","cc"
);
#endif
return (has_cpuid != 0) ;
}
/*
* Issue a cpuid instruction
*/
#if defined(_M_IX86) || defined(__i386)
static void cpuid(long cmd, long* a, long *b, long *c, long *d)
{
long la=0,lb=0,lc=0,ld=0 ;
#if defined(_MSC_VER)
/* we are compiling under MSVC, on x86 */
__asm {
mov eax,cmd
push ebx /* save ebx */
__emit 0x0F /* cpuid was not supported by MSVC5, thus emit it */
__emit 0xA2
mov ebx,esi
pop ebx /* restore ebx */
mov la,eax
mov lb,esi
mov lc,ecx
mov ld,edx
}
#endif /* MSVC */
#if defined(__GNUC__)
/* we are compiling under gcc */
__asm__ __volatile (
"movl %4, %%eax;"
"push %%ebx;"
"cpuid;"
"movl %%ebx,%%esi;"
"popl %%ebx;"
"movl %%eax, %0;"
"movl %%esi, %1;"
"movl %%ecx, %2;"
"movl %%edx, %3;"
: "=m" (la), "=m" (lb), "=m" (lc), "=m" (ld)
: "m" (cmd)
: "eax","ecx","edx","esi","cc"
);
#endif /* GNUC */
*a = la ; *b = lb ; *c = lc ; *d = ld ;
}
#endif /* x86 architecture */
#if defined(_ARM)
#if defined(__linux)
#define has_cpuid() 0 /* we can't currently cpuid under ARM/linux */
#else
#define has_cpuid() 1
#endif
#if defined (__GNUC__)
static int cpu_id(void)
{
int ret ;
#if defined(__MARM_THUMB__)
ret = 0x4180720; // XXXNH: until we fix this assembly
#else
__asm__ ("mrc p15, 0, %0, c0, c0, 0" : "=r" (ret)) ;
#endif
return ret ;
}
#endif /* GNUC */
#if defined(_MSC_VER)
/* cpu_id is defined in an assembly file */
extern int cpu_id(void);
#endif
#endif /* ARM */
/*
* routines to check for operating system support for the SSE floating point
* instructions.
* Intel recommends to check for OS support by executing an instruction which
* will fault (SIGILL) and throw an exception in the absence of OS support.
* Catching the exception signals lack of support.
*
* An alternative method would be to check CR4.OSFXSR, but alas, that can only
* be done if you run in ring 0. Duh.
*/
#if defined __i386 && defined __linux
/* to catch the exception caused by SSE instructions on an OS that does not support
saving the additional SSE CPU state, we need a signal handler here. If called
as a result of SIGILL, it will modify eax, and return to the code that caused
the trap, jumping past the offending instruction.
*/
#include <signal.h>
static void myHandler(int signal, struct sigcontext sc)
{
// signal "no SSE support" by modifying eax (it is 1 on entry)
sc.eax = 0 ;
// we get here because orps xmm0,xmm0 has raised an exception. To avoid
// repeating this instruction, we increment the PC by three before
// returning.
sc.eip += 3 ;
}
#endif
static int bOSHasSSEsupport(void)
{
#if defined(_WIN32) && defined(_M_IX86)
int bSSE = 1 ;
// this is a little tricky. For Windows, we use __try/__except.
// Stack unwinding does not need to be enabled because we
// catch all exceptions; you can ignore the compiler warning.
#pragma warning(disable:4530)
__try {
// Use opcode bytes for the instruction because MSVC and gcc
// compilers can't compile it yet.
// __asm orps xmm0,xmm0
__asm {
_emit 0x0f
_emit 0x56
_emit 0xc0
}
}
__except ( 1 /* EXCEPTION_EXECUTE_HANDLER */ )
{
// if an exception occurred, no OS support for xmm register save
// and restore so don't use SSE nor SSE2 instructions
bSSE = 0 ;
}
#pragma warning(default:4530)
return bSSE ;
#elif defined(__i386) && defined(__linux)
// On linux, we use an exception handler
struct sigaction savedSIGILLHandler ;
struct sigaction newSIGILLHandler ;
int bSSE ;
memset (&newSIGILLHandler, 0, sizeof newSIGILLHandler);
sigemptyset (&newSIGILLHandler.sa_mask);
newSIGILLHandler.sa_handler = (void (*)(int))myHandler;
// install new signal handler
sigaction(SIGILL, &newSIGILLHandler, &savedSIGILLHandler) ;
// execute an MME instruction. If it fails, the exception handler
// will be called, which will modify eax (bSSE)
__asm__ __volatile ("mov $1,%0\n\t"
".byte 0x0f,0x56,0xc0\n\t" /* xorps xmm0,xmm0 */
: "=a" (bSSE)) ;
// restore signal handler
sigaction(SIGILL,&savedSIGILLHandler,NULL) ;
return bSSE ;
#else
return 0 ; // no OS SSE support detectable
#endif
}
enum
{
ulBitMM = 1<<23,
ulBitEM = 1<<2, /* emulation enabled / disabled */
ulBitMSR = 1<<5,
ulBitTSC = 1<<4,
ulBitXMM = 1<<25,/* Streaming SIMD Extensions-enabled? */
ulBitSSE2 = 1<<26
} ;
void
CPUIdentify(CPUInformation* pInfo)
{
int id;
memset(pInfo, 0, sizeof(*pInfo)) ;
pInfo->architecture = ulArchitectureUNKNOWN ;
#if defined(_M_IX86) || defined(__i386)
/* we assume we are running at least on 386 or compatible */
pInfo->architecture = ulArchitectureIntel ;
pInfo->specific.m_x86.family = ulFamily80386 ;
if (hasCPUID())
{
long a,b,c,d ;
/* if we have cpuid, we are at least 486 */
pInfo->specific.m_x86.family = ulFamily80486 ;
/* First, invoke cpuid(0, ....). eax (returned in a) tells us
the maximum p which we can call cpuid(p, ...) with. Pentium
and later will have p >= 1. */
cpuid (0,&a,&b,&c,&d) ;
/* cpuid(1,...) checks for additional processor features. */
if (a >= 1)
{
cpuid (1,&a,&b,&c,&d) ;
pInfo->specific.m_x86.family = (a & 0xf00) >> 8 ;
pInfo->specific.m_x86.model = (a & 0x0f0) >> 4 ;
pInfo->specific.m_x86.stepping = (a & 0x00f) ;
pInfo->specific.m_x86.hasPMC = (pInfo->specific.m_x86.family == 6) ;
pInfo->specific.m_x86.hasMSR = (d & ulBitMSR) ;
pInfo->specific.m_x86.hasTSC = (d & ulBitTSC) ;
if (d & ulBitMM)
{
pInfo->specific.m_x86.hasPMC = 1 ;
pInfo->specific.m_x86.hasMMX = 1 ;
pInfo->specific.m_x86.hasSSE = (d & ulBitXMM) ;
pInfo->specific.m_x86.hasSSE2 = (d & ulBitSSE2) ;
}
}
}
if (pInfo->specific.m_x86.hasSSE || pInfo->specific.m_x86.hasSSE2)
{
// SSE / SSE2 floating point instructions require OS FXSAVE
// and FXRSTOR support.
pInfo->specific.m_x86.hasSSE_OSSupport = bOSHasSSEsupport() ;
}
#endif // if defined i386
#ifdef _ARM
if (!has_cpuid())
return ;
id = cpu_id() ;
switch (id & 0xf000)
{
case 0x0<<12: // pre-ARM7
pInfo->specific.m_arm.family = ulFamily_PREARM7 ;
pInfo->specific.m_arm.implementor = ARM_IMPL_ARM ;
switch (id >> 4) {
case 0x4156030: pInfo->architecture = ulArchitectureARM_ARCH2 ; break ;
case 0x4156060:
case 0x4156061:
case 0x4156062:
pInfo->architecture = ulArchitectureARM_ARCH3 ; break ;
default:
pInfo->architecture = ulArchitectureUNKNOWN ; break ;
}
break ;
case 0x7<<12: // ARM7
pInfo->specific.m_arm.family = ulFamily_ARM7 ;
pInfo->architecture = ((id >> 23) & 1) ? ulArchitectureARM_ARCH4T : ulArchitectureARM_ARCH3 ;
pInfo->specific.m_arm.implementor = id >> 24 ;
pInfo->specific.m_arm.variant = (id >> 16) & 0x7f ;
pInfo->specific.m_arm.primaryPartNo = (id >> 4) & 0xfff ;
pInfo->specific.m_arm.revision = id & 0xf ;
default: // post-ARM7
pInfo->specific.m_arm.family = ulFamily_POSTARM7 ;
pInfo->specific.m_arm.implementor = id >> 24 ;
pInfo->specific.m_arm.variant = (id >> 20) & 0xf ;
pInfo->architecture = ((id >> 16) & 0xf) - 1 + ulArchitectureARM_ARCH4 ;
pInfo->specific.m_arm.primaryPartNo = (id >> 4) & 0xfff ;
pInfo->specific.m_arm.revision = id & 0xf ;
if (pInfo->specific.m_arm.implementor == ARM_IMPL_Intel &&
pInfo->architecture == ulArchitectureARM_ARCH5TE &&
(id >> 13) & 7 == 1)
{
// XScale
pInfo->specific.m_arm.specific.XScale.coreGeneration = (id >> 13) & 7 ; // "1"
pInfo->specific.m_arm.specific.XScale.coreRevision = (id >> 10) & 7 ;
pInfo->specific.m_arm.specific.XScale.productNumber = (id >> 4) & 63 ;
}
break ;
}
#endif /* _ARM */
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -