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