1 /*  Cpudetection lib
2  *  Copyright (C) 2002-2003  Pcsx2 Team
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #ifdef __x86_64__
20 
21 #if defined (_WIN32)
22 #include <windows.h>
23 #endif
24 
25 #include <string.h>
26 #include <stdio.h>
27 
28 #include "ix86-64.h"
29 
30 #if defined (_MSC_VER) && _MSC_VER >= 1400
31 
32    void __cpuid(int* CPUInfo, int InfoType);
33    unsigned __int64 __rdtsc();
34 
35    #pragma intrinsic(__cpuid)
36    #pragma intrinsic(__rdtsc)
37 
38 #endif
39 
40 CAPABILITIES cpucaps;
41 CPUINFO cpuinfo;
42 
43 #define cpuid(cmd,a,b,c,d) \
44   __asm__ __volatile__("cpuid" \
45 		: "=a" (a), "=b" (b), "=c" (c), "=d" (d)  : "0" (cmd))
46 
iCpuId(u32 cmd,u32 * regs)47 static s32 iCpuId( u32 cmd, u32 *regs )
48 {
49    int flag=1;
50 
51 #if defined (_MSC_VER) && _MSC_VER >= 1400
52 
53    __cpuid( regs, cmd );
54 
55    return 0;
56 
57 #elif defined (_MSC_VER)
58 
59 #ifdef __x86_64__
60    assert(0);
61 #else // __x86_64__
62    __asm
63    {
64       push ebx;
65       push edi;
66 
67       pushfd;
68       pop eax;
69       mov edx, eax;
70       xor eax, 1 << 21;
71       push eax;
72       popfd;
73       pushfd;
74       pop eax;
75       xor eax, edx;
76       mov flag, eax;
77    }
78    if ( ! flag )
79    {
80       return -1;
81    }
82 
83    __asm
84    {
85       mov eax, cmd;
86       cpuid;
87       mov edi, [regs]
88       mov [edi], eax;
89       mov [edi+4], ebx;
90       mov [edi+8], ecx;
91       mov [edi+12], edx;
92 
93       pop edi;
94       pop ebx;
95    }
96 #endif // __x86_64__
97    return 0;
98 
99 
100 #else
101 
102 #ifndef __x86_64__
103    // see if we can use cpuid
104    __asm__ __volatile__ (
105       "sub $0x18, %%esp\n"
106       "pushf\n"
107       "pop %%eax\n"
108       "mov %%eax, %%edx\n"
109       "xor $0x200000, %%eax\n"
110       "push %%eax\n"
111       "popf\n"
112       "pushf\n"
113       "pop %%eax\n"
114       "xor %%edx, %%eax\n"
115       "mov %%eax, %0\n"
116 	  "add $0x18, %%esp\n"
117       : "=r"(flag) :
118    );
119 #endif
120 
121    if ( !flag )
122        return -1;
123 
124    cpuid(cmd, regs[0], regs[1], regs[2], regs[3]);
125    return 0;
126 #endif // _MSC_VER
127 }
128 
GetCPUTick(void)129 u64 GetCPUTick( void )
130 {
131 #if defined (_MSC_VER) && _MSC_VER >= 1400
132 
133    return __rdtsc();
134 
135 #elif defined(__MSCW32__) && !defined(__x86_64__)
136 
137    __asm rdtsc;
138 
139 #else
140 
141    u32 _a, _d;
142 	__asm__ __volatile__ ("rdtsc" : "=a"(_a), "=d"(_d));
143 	return (u64)_a | ((u64)_d << 32);
144 
145 #endif
146 }
147 
148 #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
149 
150 #include <sys/time.h>
151 #include <errno.h>
152 //*
timeGetTime2()153 unsigned long timeGetTime2()
154 {
155  struct timeval tv;
156  gettimeofday(&tv, 0);                                 // well, maybe there are better ways
157  return (unsigned long)tv.tv_sec * 1000 + tv.tv_usec/1000;            // to do that, but at least it works
158 }
159 //*/
160 #endif
161 
CPUSpeedHz(unsigned int time)162 s64 CPUSpeedHz( unsigned int time )
163 {
164    s64 timeStart,
165             timeStop;
166    s64 startTick,
167             endTick;
168    s64 overhead;
169 
170    if( ! cpucaps.hasTimeStampCounter )
171    {
172       return 0; //check if function is supported
173    }
174 
175 	overhead = GetCPUTick() - GetCPUTick();
176 
177 	timeStart = timeGetTime2( );
178 	while( timeGetTime2( ) == timeStart )
179    {
180       timeStart = timeGetTime2( );
181    }
182 	for(;;)
183 	{
184 		timeStop = timeGetTime2( );
185 		if ( ( timeStop - timeStart ) > 1 )
186 		{
187 			startTick = GetCPUTick( );
188 			break;
189 		}
190 	}
191 
192 	timeStart = timeStop;
193 	for(;;)
194 	{
195 		timeStop = timeGetTime2( );
196 		if ( ( timeStop - timeStart ) > time )
197 		{
198 			endTick = GetCPUTick( );
199 			break;
200 		}
201 	}
202 
203 	return (s64)( ( endTick - startTick ) + ( overhead ) );
204 }
205 
206 ////////////////////////////////////////////////////
cpudetectInit(void)207 void cpudetectInit( void )
208 {
209    u32 regs[ 4 ];
210    u32 cmds;
211    u32 AMDspeed;
212    s8 AMDspeedString[10];
213    int cputype=0;            // Cpu type
214    //AMD 64 STUFF
215    u32 x86_64_8BITBRANDID = 0;
216    u32 x86_64_12BITBRANDID = 0;
217    memset( cpuinfo.x86ID, 0, sizeof( cpuinfo.x86ID ) );
218    cpuinfo.x86Family = 0;
219    cpuinfo.x86Model  = 0;
220    cpuinfo.x86PType  = 0;
221    cpuinfo.x86StepID = 0;
222    cpuinfo.x86Flags  = 0;
223    cpuinfo.x86EFlags = 0;
224 
225    if ( iCpuId( 0, regs ) == -1 ) return;
226 
227    cmds = regs[ 0 ];
228    ((u32*)cpuinfo.x86ID)[ 0 ] = regs[ 1 ];
229    ((u32*)cpuinfo.x86ID)[ 1 ] = regs[ 3 ];
230    ((u32*)cpuinfo.x86ID)[ 2 ] = regs[ 2 ];
231    if ( cmds >= 0x00000001 )
232    {
233       if ( iCpuId( 0x00000001, regs ) != -1 )
234       {
235          cpuinfo.x86StepID =  regs[ 0 ]        & 0xf;
236          cpuinfo.x86Model  = (regs[ 0 ] >>  4) & 0xf;
237          cpuinfo.x86Family = (regs[ 0 ] >>  8) & 0xf;
238          cpuinfo.x86PType  = (regs[ 0 ] >> 12) & 0x3;
239          x86_64_8BITBRANDID = regs[1] & 0xff;
240          cpuinfo.x86Flags  =  regs[ 3 ];
241       }
242    }
243    if ( iCpuId( 0x80000000, regs ) != -1 )
244    {
245       cmds = regs[ 0 ];
246       if ( cmds >= 0x80000001 )
247       {
248 		 if ( iCpuId( 0x80000001, regs ) != -1 )
249          {
250 			x86_64_12BITBRANDID = regs[1] & 0xfff;
251             cpuinfo.x86EFlags = regs[ 3 ];
252 
253          }
254       }
255    }
256    switch(cpuinfo.x86PType)
257    {
258       case 0:
259          strcpy( cpuinfo.x86Type, "Standard OEM");
260          break;
261       case 1:
262          strcpy( cpuinfo.x86Type, "Overdrive");
263          break;
264       case 2:
265          strcpy( cpuinfo.x86Type, "Dual");
266          break;
267       case 3:
268          strcpy( cpuinfo.x86Type, "Reserved");
269          break;
270       default:
271          strcpy( cpuinfo.x86Type, "Unknown");
272          break;
273    }
274    if ( cpuinfo.x86ID[ 0 ] == 'G' ){ cputype=0;}//trick lines but if you know a way better ;p
275    if ( cpuinfo.x86ID[ 0 ] == 'A' ){ cputype=1;}
276 
277    if ( cputype == 0 ) //intel cpu
278    {
279       if( ( cpuinfo.x86Family >= 7 ) && ( cpuinfo.x86Family < 15 ) )
280       {
281          strcpy( cpuinfo.x86Fam, "Intel P6 family (Not PIV and Higher then PPro" );
282       }
283       else
284       {
285          switch( cpuinfo.x86Family )
286          {
287             // Start at 486 because if it's below 486 there is no cpuid instruction
288             case 4:
289                strcpy( cpuinfo.x86Fam, "Intel 486" );
290                break;
291             case 5:
292                switch( cpuinfo.x86Model )
293                {
294                case 4:
295                case 8:     // 0.25 �m
296                   strcpy( cpuinfo.x86Fam, "Intel Pentium (MMX)");
297                   break;
298                default:
299                   strcpy( cpuinfo.x86Fam, "Intel Pentium" );
300                }
301                break;
302             case 6:
303                switch( cpuinfo.x86Model )
304                {
305                case 0:     // Pentium pro (P6 A-Step)
306                case 1:     // Pentium pro
307                   strcpy( cpuinfo.x86Fam, "Intel Pentium Pro" );
308                   break;
309 
310                case 2:     // 66 MHz FSB
311                case 5:     // Xeon/Celeron (0.25 �m)
312                case 6:     // Internal L2 cache
313                   strcpy( cpuinfo.x86Fam, "Intel Pentium II" );
314                   break;
315 
316                case 7:     // Xeon external L2 cache
317                case 8:     // Xeon/Celeron with 256 KB on-die L2 cache
318                case 10:    // Xeon/Celeron with 1 or 2 MB on-die L2 cache
319                case 11:    // Xeon/Celeron with Tualatin core, on-die cache
320                   strcpy( cpuinfo.x86Fam, "Intel Pentium III" );
321                   break;
322 			   case 15:    // Core 2 Duo Allendale/Conroe
323 				  strcpy( cpuinfo.x86Fam, "Intel Core 2 Duo" );
324 				  break;
325 
326                default:
327                   strcpy( cpuinfo.x86Fam, "Intel Pentium Pro (Unknown)" );
328                }
329                break;
330             case 15:
331                switch( cpuinfo.x86Model )
332                {
333                case 0:     // Willamette (A-Step)
334                case 1:     // Willamette
335                   strcpy( cpuinfo.x86Fam, "Willamette Intel Pentium IV" );
336                   break;
337                case 2:     // Northwood
338                   strcpy( cpuinfo.x86Fam, "Northwood Intel Pentium IV" );
339                   break;
340 
341                default:
342                   strcpy( cpuinfo.x86Fam, "Intel Pentium IV (Unknown)" );
343                   break;
344                }
345                break;
346             default:
347                strcpy( cpuinfo.x86Fam, "Unknown Intel CPU" );
348          }
349       }
350    }
351    else if ( cputype == 1 ) //AMD cpu
352    {
353       if( cpuinfo.x86Family >= 7 )
354       {
355 		  if((x86_64_12BITBRANDID !=0) || (x86_64_8BITBRANDID !=0))
356 		  {
357 		    if(x86_64_8BITBRANDID == 0 )
358 		    {
359                switch((x86_64_12BITBRANDID >>6)& 0x3f)
360 			   {
361 			    case 4:
362 				 strcpy(cpuinfo.x86Fam,"AMD Athlon(tm) 64 Processor");
363                  AMDspeed = 22 + (x86_64_12BITBRANDID & 0x1f);
364 				 //AMDspeedString = strtol(AMDspeed, (char**)NULL,10);
365 				 sprintf(AMDspeedString," %d",AMDspeed);
366 				 strcat(AMDspeedString,"00+");
367 				 strcat(cpuinfo.x86Fam,AMDspeedString);
368 				 break;
369 			    case 12:
370 				 strcpy(cpuinfo.x86Fam,"AMD Opteron(tm) Processor");
371 				 break;
372 			    case 5:
373 				  strcpy( cpuinfo.x86Fam, "AMD Athlon X2 Processor" );
374 				  AMDspeed = 22 + (x86_64_12BITBRANDID & 0x1f);
375 				 //AMDspeedString = strtol(AMDspeed, (char**)NULL,10);
376 				 sprintf(AMDspeedString," %d",AMDspeed);
377 				 strcat(AMDspeedString,"00+");
378 				 strcat(cpuinfo.x86Fam,AMDspeedString);
379                   break;
380 			   case 44:
381 				   strcpy( cpuinfo.x86Fam, "AMD Opteron(tm) Dual Core Processor" );
382                   break;
383 			    default:
384 				   strcpy(cpuinfo.x86Fam,"Unknown AMD 64 proccesor");
385 
386 			    }
387 		     }
388 		     else //8bit brand id is non zero
389 		     {
390                 strcpy(cpuinfo.x86Fam,"Unsupported yet AMD64 cpu");
391 		     }
392 		  }
393 		  else
394 		  {
395 			  strcpy( cpuinfo.x86Fam, "AMD K7+ Processor" );
396 		  }
397       }
398       else
399       {
400          switch ( cpuinfo.x86Family )
401          {
402             case 4:
403                switch( cpuinfo.x86Model )
404                {
405                case 14:
406                case 15:       // Write-back enhanced
407                   strcpy( cpuinfo.x86Fam, "AMD 5x86 Processor" );
408                   break;
409 
410                case 3:        // DX2
411                case 7:        // Write-back enhanced DX2
412                case 8:        // DX4
413                case 9:        // Write-back enhanced DX4
414                   strcpy( cpuinfo.x86Fam, "AMD 486 Processor" );
415                   break;
416 
417 
418                default:
419                   strcpy( cpuinfo.x86Fam, "AMD Unknown Processor" );
420 
421                }
422                break;
423 
424             case 5:
425                switch( cpuinfo.x86Model)
426                {
427                case 0:     // SSA 5 (75, 90 and 100 Mhz)
428                case 1:     // 5k86 (PR 120 and 133 MHz)
429                case 2:     // 5k86 (PR 166 MHz)
430                case 3:     // K5 5k86 (PR 200 MHz)
431                   strcpy( cpuinfo.x86Fam, "AMD K5 Processor" );
432                   break;
433 
434                case 6:
435                case 7:     // (0.25 �m)
436                case 8:     // K6-2
437                case 9:     // K6-III
438                case 14:    // K6-2+ / K6-III+
439                   strcpy( cpuinfo.x86Fam, "AMD K6 Series Processor" );
440                   break;
441 
442                default:
443                   strcpy( cpuinfo.x86Fam, "AMD Unknown Processor" );
444                }
445                break;
446             case 6:
447                strcpy( cpuinfo.x86Fam, "AMD Athlon XP Processor" );
448                break;
449             default:
450                strcpy( cpuinfo.x86Fam, "Unknown AMD CPU" );
451          }
452       }
453    }
454    //capabilities
455    cpucaps.hasFloatingPointUnit                         = ( cpuinfo.x86Flags >>  0 ) & 1;
456    cpucaps.hasVirtual8086ModeEnhancements               = ( cpuinfo.x86Flags >>  1 ) & 1;
457    cpucaps.hasDebuggingExtensions                       = ( cpuinfo.x86Flags >>  2 ) & 1;
458    cpucaps.hasPageSizeExtensions                        = ( cpuinfo.x86Flags >>  3 ) & 1;
459    cpucaps.hasTimeStampCounter                          = ( cpuinfo.x86Flags >>  4 ) & 1;
460    cpucaps.hasModelSpecificRegisters                    = ( cpuinfo.x86Flags >>  5 ) & 1;
461    cpucaps.hasPhysicalAddressExtension                  = ( cpuinfo.x86Flags >>  6 ) & 1;
462    cpucaps.hasMachineCheckArchitecture                  = ( cpuinfo.x86Flags >>  7 ) & 1;
463    cpucaps.hasCOMPXCHG8BInstruction                     = ( cpuinfo.x86Flags >>  8 ) & 1;
464    cpucaps.hasAdvancedProgrammableInterruptController   = ( cpuinfo.x86Flags >>  9 ) & 1;
465    cpucaps.hasSEPFastSystemCall                         = ( cpuinfo.x86Flags >> 11 ) & 1;
466    cpucaps.hasMemoryTypeRangeRegisters                  = ( cpuinfo.x86Flags >> 12 ) & 1;
467    cpucaps.hasPTEGlobalFlag                             = ( cpuinfo.x86Flags >> 13 ) & 1;
468    cpucaps.hasMachineCheckArchitecture                  = ( cpuinfo.x86Flags >> 14 ) & 1;
469    cpucaps.hasConditionalMoveAndCompareInstructions     = ( cpuinfo.x86Flags >> 15 ) & 1;
470    cpucaps.hasFGPageAttributeTable                      = ( cpuinfo.x86Flags >> 16 ) & 1;
471    cpucaps.has36bitPageSizeExtension                    = ( cpuinfo.x86Flags >> 17 ) & 1;
472    cpucaps.hasProcessorSerialNumber                     = ( cpuinfo.x86Flags >> 18 ) & 1;
473    cpucaps.hasCFLUSHInstruction                         = ( cpuinfo.x86Flags >> 19 ) & 1;
474    cpucaps.hasDebugStore                                = ( cpuinfo.x86Flags >> 21 ) & 1;
475    cpucaps.hasACPIThermalMonitorAndClockControl         = ( cpuinfo.x86Flags >> 22 ) & 1;
476    cpucaps.hasMultimediaExtensions                      = ( cpuinfo.x86Flags >> 23 ) & 1; //mmx
477    cpucaps.hasFastStreamingSIMDExtensionsSaveRestore    = ( cpuinfo.x86Flags >> 24 ) & 1;
478    cpucaps.hasStreamingSIMDExtensions                   = ( cpuinfo.x86Flags >> 25 ) & 1; //sse
479    cpucaps.hasStreamingSIMD2Extensions                  = ( cpuinfo.x86Flags >> 26 ) & 1; //sse2
480    cpucaps.hasSelfSnoop                                 = ( cpuinfo.x86Flags >> 27 ) & 1;
481    cpucaps.hasHyperThreading                            = ( cpuinfo.x86Flags >> 28 ) & 1;
482    cpucaps.hasThermalMonitor                            = ( cpuinfo.x86Flags >> 29 ) & 1;
483    cpucaps.hasIntel64BitArchitecture                    = ( cpuinfo.x86Flags >> 30 ) & 1;
484     //that is only for AMDs
485    cpucaps.hasMultimediaExtensionsExt                   = ( cpuinfo.x86EFlags >> 22 ) & 1; //mmx2
486    cpucaps.hasAMD64BitArchitecture                      = ( cpuinfo.x86EFlags >> 29 ) & 1; //64bit cpu
487    cpucaps.has3DNOWInstructionExtensionsExt             = ( cpuinfo.x86EFlags >> 30 ) & 1; //3dnow+
488    cpucaps.has3DNOWInstructionExtensions                = ( cpuinfo.x86EFlags >> 31 ) & 1; //3dnow
489    cpuinfo.cpuspeed = (u32 )(CPUSpeedHz( 1000 ) / 1000000);
490 }
491 
492 #endif
493