PageRenderTime 62ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/CPUInfo.cpp

https://github.com/chadaustin/CPUInfo
C++ | 1232 lines | 912 code | 202 blank | 118 comment | 189 complexity | e9d9622acb0919f10ba315075f6749ac MD5 | raw file
  1. // Copyright (c) 2005 Chad Austin
  2. //
  3. // Permission is hereby granted, free of charge, to any person
  4. // obtaining a copy of this software and associated documentation
  5. // files (the "Software"), to deal in the Software without
  6. // restriction, including without limitation the rights to use, copy,
  7. // modify, merge, publish, distribute, sublicense, and/or sell copies
  8. // of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be
  12. // included in all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  18. // BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  19. // ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  20. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. // SOFTWARE.
  22. //
  23. //
  24. // Logic contained in this software gleaned from the following resources:
  25. //
  26. // Intel Software Development Manuals
  27. // http://www.intel.com/design/itanium/manuals/245319.pdf
  28. //
  29. // AMD Processor Detection
  30. // http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/20734.pdf
  31. // http://www.amd.com/us-en/assets/content_type/DownloadableAssets/dwamd_24593.pdf
  32. // http://www.amd.com/us-en/assets/content_type/DownloadableAssets/dwamd_24594.pdf
  33. //
  34. // AMD64 Instruction Set Reference
  35. //
  36. // sandpile.org
  37. // http://www.sandpile.org/post/msgs/20003246.htm
  38. // http://www.sandpile.org/ia32/cpuid.htm
  39. //
  40. // Iain Chesworth @ codeproject
  41. // http://www.codetools.com/system/camel.asp
  42. //
  43. // Rob Wyatt @ gamasutra:
  44. // http://www.gamasutra.com/features/wyatts_world/19990709/processor_detection_01.htm
  45. //
  46. // http://grafi.ii.pw.edu.pl/gbm/x86/cpuid.html
  47. #include <assert.h>
  48. #include <stdio.h>
  49. #include <string.h>
  50. #include "CPUInfo.h"
  51. #if defined(_MSC_VER) || defined(__CYGWIN__)
  52. #include <windows.h>
  53. typedef DWORD u32;
  54. typedef ULONGLONG u64;
  55. #else
  56. typedef unsigned long u32;
  57. typedef unsigned long long u64;
  58. #endif
  59. const char* CPUInfo::getVendorName() const {
  60. // Return the vendor ID.
  61. switch (identity.manufacturer) {
  62. case Intel: return "Intel Corporation";
  63. case AMD: return "Advanced Micro Devices";
  64. case NSC: return "National Semiconductor";
  65. case Cyrix: return "Cyrix Corp., VIA Inc.";
  66. case NexGen: return "NexGen Inc., Advanced Micro Devices";
  67. case IDT: return "IDT\\Centaur, Via Inc.";
  68. case UMC: return "United Microelectronics Corp.";
  69. case Rise: return "Rise";
  70. case Transmeta: return "Transmeta";
  71. default: return identity.vendor;
  72. }
  73. }
  74. std::string CPUInfo::getProcessorName() const {
  75. if (identity.hasExtendedName) {
  76. return identity.extendedName + identity.firstNonSpace;
  77. } else if (identity.brand != 0) {
  78. return getProcessorBrandName();
  79. } else {
  80. return getClassicalProcessorName();
  81. }
  82. }
  83. const char* CPUInfo::getProcessorTypeName() const {
  84. switch (identity.type) {
  85. case 0: return "Original OEM Processor";
  86. case 1: return "Intel OverDrive Processor";
  87. case 2: return "Dual Processor";
  88. case 3: return "Reserved";
  89. default: return "Other";
  90. }
  91. }
  92. std::string CPUInfo::getProcessorBrandName() const {
  93. // http://sandpile.org/ia32/cpuid.htm and IA-32 Manual 2A
  94. switch (identity.brand) {
  95. case 0x00: return "Not Supported";
  96. case 0x01: return "0.18 µm Intel Celeron";
  97. case 0x02: return "0.18 µm Intel Pentium III";
  98. case 0x03: return "0.18 µm Intel Pentium III Xeon"
  99. " -OR- "
  100. "0.13 µm Intel Celeron";
  101. case 0x04: return "0.13 µm Intel Pentium III";
  102. case 0x06: return "0.13 µm Intel Pentium III mobile";
  103. case 0x07: return "0.13 µm Intel Celeron mobile";
  104. case 0x08: return "0.18 µm Intel Pentium 4"
  105. " -OR- "
  106. "0.13 µm Intel Celeron 4 mobile (0F24h)";
  107. case 0x09: return "0.13 µm Intel Pentium 4";
  108. case 0x0A: return "0.18 µm Intel Celeron 4";
  109. case 0x0B: return "0.18 µm Intel Pentium 4 Xeon MP"
  110. " -OR- "
  111. "0.13 µm Intel Pentium 4 Xeon";
  112. case 0x0C: return "0.13 µm Intel Pentium 4 Xeon MP";
  113. case 0x0E: return "0.18 µm Intel Pentium 4 Xeon"
  114. " -OR- "
  115. "0.13 µm Intel Pentium 4 mobile (production)";
  116. case 0x0F: return "0.13 µm Intel Celeron 4 mobile (0F27h)"
  117. " -OR- "
  118. "0.13 µm Intel Pentium 4 mobile (samples)";
  119. case 0x11: return "Mobile Genuine Intel processor";
  120. case 0x12: return "0.13 µm Intel Celeron M"
  121. " -OR- "
  122. "0.09 µm Intel Celeron M";
  123. case 0x13: return "Mobile Intel Celeron processor";
  124. case 0x14: return "Intel Celeron processor";
  125. case 0x15: return "Mobile Genuine Intel processor";
  126. case 0x16: return "0.13 µm Intel Pentium M"
  127. " -OR- "
  128. "0.09 µm Intel Pentium M";
  129. case 0x17: return "Mobile Intel Celeron processor";
  130. }
  131. char buffer[100];
  132. int top3 = (identity.brand >> 5) & 7;
  133. int bottom5 = identity.brand & 31;
  134. switch (top3) {
  135. case 0: sprintf(buffer, "Engineering Sample %d", bottom5); return buffer;
  136. case 1: sprintf(buffer, "AMD Athlon 64 %d00+", 22 + bottom5); return buffer;
  137. case 2: sprintf(buffer, "AMD Athlon 64 %d00+ mobile", 22 + bottom5); return buffer;
  138. case 3: sprintf(buffer, "AMD Opteron UP 1%d", 38 + 2 * bottom5); return buffer;
  139. case 4: sprintf(buffer, "AMD Opteron DP 2%d", 38 + 2 * bottom5); return buffer;
  140. case 5: sprintf(buffer, "AMD Opteron MP 8%d", 38 + 2 * bottom5); return buffer;
  141. // n/a #6 AMD Athlon 64 FX-ZZ (ZZ=24+xxxxxb)
  142. // The upper 3 bits aren't sufficient to encode a value of 9=1001b.
  143. // Thus the Athlon 64 FX requires the 12-bit brand ID.
  144. }
  145. return "Unknown";
  146. }
  147. const char* CPUInfo::getClassicalProcessorName() const {
  148. switch (identity.manufacturer) {
  149. case Intel:
  150. switch (identity.family) {
  151. case 3: return "Newer i80386 family";
  152. case 4:
  153. switch (identity.model) {
  154. case 0: return "i80486DX-25/33";
  155. case 1: return "i80486DX-50";
  156. case 2: return "i80486SX";
  157. case 3: return "i80486DX2";
  158. case 4: return "i80486SL";
  159. case 5: return "i80486SX2";
  160. case 7: return "i80486DX2 WriteBack";
  161. case 8: return "i80486DX4";
  162. case 9: return "i80486DX4 WriteBack";
  163. default: return "Unknown 80486 family";
  164. }
  165. case 5:
  166. switch (identity.model) {
  167. case 0: return "P5 A-Step";
  168. case 1: return "P5";
  169. case 2: return "P54C";
  170. case 3: return "P24T OverDrive";
  171. case 4: return "P55C";
  172. case 7: return "P54C";
  173. case 8: return "P55C (0.25µm)";
  174. default: return "Unknown Pentium® family";
  175. }
  176. case 6:
  177. switch (identity.model) {
  178. case 0: return "P6 A-Step";
  179. case 1: return "P6";
  180. case 3: return "Pentium® II (0.28 µm)";
  181. case 5: return "Pentium® II (0.25 µm)";
  182. case 6: return "Pentium® II With On-Die L2 Cache";
  183. case 7: return "Pentium® III (0.25 µm)";
  184. case 8: return "Pentium® III (0.18 µm) With 256 KB On-Die L2 Cache";
  185. case 9: return "Pentium® M or Celeron M";
  186. case 0xa: return "Pentium® III (0.18 µm) With 1 Or 2 MB On-Die L2 Cache";
  187. case 0xb: return "Pentium® III (0.13 µm) With 256 Or 512 KB On-Die L2 Cache";
  188. case 0xd: return "Pentium M (90 nm)";
  189. case 0xe: return "Core Solo or Duo (65 nm)";
  190. case 0xf: return "Core 2 Solo or Duo (65 nm)";
  191. default: return "Unknown P6 family";
  192. }
  193. case 7: return "Intel Merced (IA-64)";
  194. case 15:
  195. switch (identity.model) {
  196. case 0: return "Pentium® IV (0.18 µm)";
  197. case 1: return "Pentium® IV (0.18 µm)";
  198. case 2: return "Pentium® IV (0.13 µm)";
  199. default: return "Unknown Pentium 4 family";
  200. }
  201. case 31: return "Intel McKinley (IA-64)";
  202. default: return "Unknown Intel family";
  203. }
  204. case AMD:
  205. switch (identity.family) {
  206. case 4:
  207. switch (identity.model) {
  208. case 3: return "80486DX2";
  209. case 7: return "80486DX2 WriteBack";
  210. case 8: return "80486DX4";
  211. case 9: return "80486DX4 WriteBack";
  212. case 0xe: return "5x86";
  213. case 0xf: return "5x86WB";
  214. default: return "Unknown 80486 family";
  215. }
  216. case 5:
  217. switch (identity.model) {
  218. case 0: return "SSA5 (PR75, PR90, PR100)";
  219. case 1: return "5k86 (PR120, PR133)";
  220. case 2: return "5k86 (PR166)";
  221. case 3: return "5k86 (PR200)";
  222. case 6: return "K6 (0.30 µm)";
  223. case 7: return "K6 (0.25 µm)";
  224. case 8: return "K6-2";
  225. case 9: return "K6-III";
  226. case 0xd: return "K6-2+ or K6-III+ (0.18 µm)";
  227. default: return "Unknown 80586 family";
  228. }
  229. case 6:
  230. switch (identity.model) {
  231. case 1: return "Athlon™ (0.25 µm)";
  232. case 2: return "Athlon™ (0.18 µm)";
  233. case 3: return "Duron™ (SF core)";
  234. case 4: return "Athlon™ (Thunderbird core)";
  235. case 6: return "Athlon™ (Palomino core)";
  236. case 7: return "Duron™ (Morgan core)";
  237. case 8: return (features.supportsMP ?
  238. "Athlon™ MP (Thoroughbred core)" :
  239. "Athlon™ XP (Thoroughbred core)");
  240. default: return "Unknown K7 family";
  241. }
  242. default:
  243. return "Unknown AMD family";
  244. }
  245. case Transmeta:
  246. switch (identity.family) {
  247. case 5:
  248. switch (identity.model) {
  249. case 4: return "Crusoe TM3x00 and TM5x00";
  250. default: return "Unknown Crusoe family";
  251. }
  252. default:
  253. return "Unknown Transmeta family";
  254. }
  255. case Rise:
  256. switch (identity.family) {
  257. case 5:
  258. switch (identity.model) {
  259. case 0: return "mP6 (0.25 µm)";
  260. case 2: return "mP6 (0.18 µm)";
  261. default: return "Unknown Rise family";
  262. }
  263. default:
  264. return "Unknown Rise family";
  265. }
  266. case UMC:
  267. switch (identity.family) {
  268. case 4:
  269. switch (identity.model) {
  270. case 1: return "U5D";
  271. case 2: return "U5S";
  272. default: return "Unknown UMC family";
  273. }
  274. default:
  275. return "Unknown UMC family";
  276. }
  277. case IDT:
  278. switch (identity.family) {
  279. case 5:
  280. switch (identity.model) {
  281. case 4: return "C6";
  282. case 8: return "C2";
  283. case 9: return "C3";
  284. default: return "Unknown IDT\\Centaur family";
  285. }
  286. break;
  287. case 6:
  288. switch (identity.model) {
  289. case 6: return "VIA Cyrix III - Samuel";
  290. default: return "Unknown IDT\\Centaur family";
  291. }
  292. default:
  293. return "Unknown IDT\\Centaur family";
  294. }
  295. case Cyrix:
  296. switch (identity.family) {
  297. case 4:
  298. switch (identity.model) {
  299. case 4: return "MediaGX GX, GXm";
  300. case 9: return "5x86";
  301. default: return "Unknown Cx5x86 family";
  302. }
  303. case 5:
  304. switch (identity.model) {
  305. case 2: return "Cx6x86";
  306. case 4: return "MediaGX GXm";
  307. default: return "Unknown Cx6x86 family";
  308. }
  309. case 6:
  310. switch (identity.model) {
  311. case 0: return "6x86MX";
  312. case 5: return "Cyrix M2 Core";
  313. case 6: return "WinChip C5A Core";
  314. case 7: return "WinChip C5B\\C5C Core";
  315. case 8: return "WinChip C5C-T Core";
  316. default: return "Unknown 6x86MX\\Cyrix III family";
  317. }
  318. default:
  319. return "Unknown Cyrix family";
  320. }
  321. case NexGen:
  322. switch (identity.family) {
  323. case 5:
  324. switch (identity.model) {
  325. case 0: return "Nx586 or Nx586FPU";
  326. default: return "Unknown NexGen family";
  327. }
  328. default:
  329. return "Unknown NexGen family";
  330. }
  331. case NSC:
  332. return "Cx486SLC \\ DLC \\ Cx486S A-Step";
  333. default:
  334. return "Unknown manufacturer.";
  335. }
  336. }
  337. #ifdef _MSC_VER // Use SEH on Win32.
  338. #if 0
  339. static bool getCPUIDSupport() {
  340. // The "right" way, which doesn't work under certain Windows versions.
  341. // Courtesy of Iain Chesworth.
  342. int present = 0;
  343. __asm {
  344. pushfd ; save EFLAGS to stack.
  345. pop eax ; store EFLAGS in eax.
  346. mov edx, eax ; save in edx for testing later.
  347. xor eax, 0200000h ; switch bit 21.
  348. push eax ; copy "changed" value to stack.
  349. popfd ; save "changed" eax to EFLAGS.
  350. pushfd
  351. pop eax
  352. xor eax, edx ; See if bit changeable.
  353. jz cpuid_not_present
  354. mov present, 1 ; CPUID present - enable its usage
  355. cpuid_not_present:
  356. }
  357. return present != 0;
  358. }
  359. #endif
  360. static bool getCPUIDSupport() {
  361. __try {
  362. __asm {
  363. mov eax, 0
  364. cpuid
  365. }
  366. }
  367. __except (EXCEPTION_EXECUTE_HANDLER) {
  368. return false;
  369. }
  370. return true;
  371. }
  372. static bool getSSEFPSupport() {
  373. __try {
  374. __asm {
  375. orps xmm0, xmm0
  376. }
  377. }
  378. __except (EXCEPTION_EXECUTE_HANDLER) {
  379. return false;
  380. }
  381. return true;
  382. }
  383. static void classicalTimingLoop(u32 loopLength) {
  384. __asm {
  385. mov eax, 0x80000000
  386. mov ebx, loopLength
  387. timingLoop:
  388. bsf ecx, eax
  389. dec ebx
  390. jnz timingLoop
  391. }
  392. }
  393. #else // Assume platform that raises signals for invalid instructions.
  394. #include <setjmp.h>
  395. #include <signal.h>
  396. #ifdef __CYGWIN__
  397. #define __thread __declspec(thread)
  398. #elif defined(__APPLE__)
  399. #define __thread
  400. #endif
  401. typedef void (*SignalHandler)(int);
  402. static __thread jmp_buf check_jmpbuf;
  403. static void handleSignal(int) {
  404. longjmp(check_jmpbuf, 1);
  405. }
  406. static bool getCPUIDSupport() {
  407. SignalHandler oldSIGILL = signal(SIGILL, handleSignal);
  408. SignalHandler oldSIGSEGV = signal(SIGSEGV, handleSignal);
  409. bool hasCPUID = false;
  410. if (setjmp(check_jmpbuf) == 0) {
  411. asm("movl $0, %eax\n"
  412. "cpuid\n");
  413. hasCPUID = true;
  414. }
  415. signal(SIGILL, oldSIGILL);
  416. signal(SIGSEGV, oldSIGSEGV);
  417. return hasCPUID;
  418. }
  419. static bool getSSEFPSupport() {
  420. SignalHandler oldSIGILL = signal(SIGILL, handleSignal);
  421. SignalHandler oldSIGSEGV = signal(SIGSEGV, handleSignal);
  422. bool hasSSEFP = false;
  423. if (setjmp(check_jmpbuf) == 0) {
  424. asm("orps %xmm0, %xmm0\n");
  425. hasSSEFP = true;
  426. }
  427. signal(SIGILL, oldSIGILL);
  428. signal(SIGSEGV, oldSIGSEGV);
  429. return hasSSEFP;
  430. }
  431. static void classicalTimingLoop(u32 loopLength) {
  432. asm("mov $0x80000000, %%eax\n"
  433. "timingLoop:\n"
  434. "bsf %%eax, %%ecx\n"
  435. "dec %%ebx\n"
  436. "jnz timingLoop\n"
  437. :
  438. : "b" (loopLength)
  439. : "%eax", "%ecx");
  440. }
  441. #endif
  442. #ifdef _MSC_VER
  443. static void CPUID(u32 level, u32* eax, u32* ebx, u32* ecx, u32* edx) {
  444. assert(getCPUIDSupport());
  445. u32 _eax, _ebx, _ecx, _edx;
  446. __asm {
  447. mov eax, level
  448. cpuid
  449. mov _eax, eax
  450. mov _ebx, ebx
  451. mov _ecx, ecx
  452. mov _edx, edx
  453. }
  454. if (eax) *eax = _eax;
  455. if (ebx) *ebx = _ebx;
  456. if (ecx) *ecx = _ecx;
  457. if (edx) *edx = _edx;
  458. }
  459. static u64 RDTSC() {
  460. u32 h, l;
  461. __asm {
  462. rdtsc
  463. mov h, edx
  464. mov l, eax
  465. }
  466. return (u64(h) << 32) + l;
  467. }
  468. #else
  469. static void CPUID(u32 level, u32* eax, u32* ebx, u32* ecx, u32* edx) {
  470. assert(getCPUIDSupport());
  471. u32 _eax, _ebx, _ecx, _edx;
  472. asm("cpuid"
  473. : "=a" (_eax), "=b" (_ebx), "=c" (_ecx), "=d" (_edx)
  474. : "a" (level));
  475. if (eax) *eax = _eax;
  476. if (ebx) *ebx = _ebx;
  477. if (ecx) *ecx = _ecx;
  478. if (edx) *edx = _edx;
  479. }
  480. static u64 RDTSC() {
  481. u32 eax, edx;
  482. asm("rdtsc"
  483. : "=a" (eax), "=d" (edx));
  484. return (u64(edx) << 32) + eax;
  485. }
  486. #endif
  487. #if defined(_MSC_VER) || defined(__CYGWIN__)
  488. static u64 getHPFrequency() {
  489. LARGE_INTEGER frequency;
  490. QueryPerformanceFrequency(&frequency);
  491. return frequency.QuadPart;
  492. }
  493. static u64 getHPCounter() {
  494. LARGE_INTEGER counter;
  495. QueryPerformanceCounter(&counter);
  496. return counter.QuadPart;
  497. }
  498. #else
  499. #include <sys/time.h>
  500. static u64 getHPFrequency() {
  501. return 1000000;
  502. }
  503. static u64 getHPCounter() {
  504. timeval tv;
  505. gettimeofday(&tv, 0);
  506. return tv.tv_sec * 1000000 + tv.tv_usec;
  507. }
  508. #endif
  509. static bool checkExtendedLevelSupport(const CPUInfo::Identity& id, u32 levelToCheck) {
  510. // The way everyone else checks is to see if the result of running with
  511. // input 0x80000000 is greater than or equal to 0x80000000. The Intel
  512. // docs indicate that this may not always be the case.
  513. // The extended CPUID is supported by various vendors starting with the
  514. // following CPU models:
  515. //
  516. // Manufacturer & Chip Name | Family Model Revision
  517. //
  518. // AMD K6, K6-2 | 5 6 x
  519. // Cyrix GXm, Cyrix III "Joshua" | 5 4 x
  520. // IDT C6-2 | 5 8 x
  521. // VIA Cyrix III | 6 5 x
  522. // Transmeta Crusoe | 5 x x
  523. // Intel Pentium 4 | f x x
  524. //
  525. // We check to see if a supported processor is present...
  526. if (id.manufacturer == CPUInfo::AMD) {
  527. if (id.family < 5) return false;
  528. if (id.family == 5 && id.model < 6) return false;
  529. } else if (id.manufacturer == CPUInfo::Cyrix) {
  530. if (id.family < 5) return false;
  531. if (id.family == 5 && id.model < 4) return false;
  532. if (id.family == 6 && id.model < 5) return false;
  533. } else if (id.manufacturer == CPUInfo::IDT) {
  534. if (id.family < 5) return false;
  535. if (id.family == 5 && id.model < 8) return false;
  536. } else if (id.manufacturer == CPUInfo::Transmeta) {
  537. if (id.family < 5) return false;
  538. } else if (id.manufacturer == CPUInfo::Intel) {
  539. if (id.family < 0xF) return false;
  540. }
  541. u32 maxExtendedLevel = 0;
  542. CPUID(0x80000000, &maxExtendedLevel, NULL, NULL, NULL);
  543. return maxExtendedLevel >= levelToCheck;
  544. }
  545. static void getIdentity(CPUInfo::Identity& id) {
  546. CPUID(0, NULL,
  547. (u32*)id.vendor,
  548. (u32*)(id.vendor + 8),
  549. (u32*)(id.vendor + 4));
  550. id.vendor[12] = 0;
  551. if (memcmp(id.vendor, "GenuineIntel", 12) == 0) id.manufacturer = CPUInfo::Intel; // Intel Corp.
  552. else if (memcmp(id.vendor, "UMC UMC UMC ", 12) == 0) id.manufacturer = CPUInfo::UMC; // United Microelectronics Corp.
  553. else if (memcmp(id.vendor, "AuthenticAMD", 12) == 0) id.manufacturer = CPUInfo::AMD; // Advanced Micro Devices
  554. else if (memcmp(id.vendor, "AMD ISBETTER", 12) == 0) id.manufacturer = CPUInfo::AMD; // Advanced Micro Devices (1994)
  555. else if (memcmp(id.vendor, "CyrixInstead", 12) == 0) id.manufacturer = CPUInfo::Cyrix; // Cyrix Corp., VIA Inc.
  556. else if (memcmp(id.vendor, "NexGenDriven", 12) == 0) id.manufacturer = CPUInfo::NexGen; // NexGen Inc. (now AMD)
  557. else if (memcmp(id.vendor, "CentaurHauls", 12) == 0) id.manufacturer = CPUInfo::IDT; // IDT/Centaur (now VIA)
  558. else if (memcmp(id.vendor, "RiseRiseRise", 12) == 0) id.manufacturer = CPUInfo::Rise; // Rise
  559. else if (memcmp(id.vendor, "GenuineTMx86", 12) == 0) id.manufacturer = CPUInfo::Transmeta; // Transmeta
  560. else if (memcmp(id.vendor, "TransmetaCPU", 12) == 0) id.manufacturer = CPUInfo::Transmeta; // Transmeta
  561. else if (memcmp(id.vendor, "Geode By NSC", 12) == 0) id.manufacturer = CPUInfo::NSC; // National Semiconductor
  562. else id.manufacturer = CPUInfo::UnknownManufacturer;
  563. u32 signature_eax, signature_ebx;
  564. CPUID(1, &signature_eax, &signature_ebx, NULL, NULL);
  565. unsigned family = (signature_eax >> 8) & 0xF;
  566. unsigned ex_family = (signature_eax >> 20) & 0xFF;
  567. unsigned model = (signature_eax >> 4) & 0xF;
  568. unsigned ex_model = (signature_eax >> 16) & 0xF;
  569. id.type = (signature_eax >> 12) & 0x3;
  570. id.family = (ex_family << 4) + family;
  571. id.model = (ex_model << 4) + model;
  572. id.stepping = signature_eax & 0xF;
  573. id.brand = signature_ebx & 0xFF;
  574. }
  575. static void getExtendedIdentity(CPUInfo::Identity& id) {
  576. id.hasExtendedName = false;
  577. // Make sure this check is supported.
  578. if (!checkExtendedLevelSupport(id, 0x80000002)) return;
  579. if (!checkExtendedLevelSupport(id, 0x80000003)) return;
  580. if (!checkExtendedLevelSupport(id, 0x80000004)) return;
  581. CPUID(0x80000002,
  582. (u32*)id.extendedName,
  583. (u32*)id.extendedName + 1,
  584. (u32*)id.extendedName + 2,
  585. (u32*)id.extendedName + 3);
  586. CPUID(0x80000003,
  587. (u32*)id.extendedName + 4,
  588. (u32*)id.extendedName + 5,
  589. (u32*)id.extendedName + 6,
  590. (u32*)id.extendedName + 7);
  591. CPUID(0x80000004,
  592. (u32*)id.extendedName + 8,
  593. (u32*)id.extendedName + 9,
  594. (u32*)id.extendedName + 10,
  595. (u32*)id.extendedName + 11);
  596. id.extendedName[48] = 0;
  597. // Trim leading whitespace.
  598. unsigned firstNonSpace = 0;
  599. while (firstNonSpace < 48 && isspace(id.extendedName[firstNonSpace])) {
  600. ++firstNonSpace;
  601. }
  602. id.hasExtendedName = true;
  603. id.firstNonSpace = firstNonSpace;
  604. }
  605. static bool isBitSet(unsigned word, int bit) {
  606. return (word & (1 << bit)) != 0;
  607. }
  608. static void getFeatures(CPUInfo::Features& features) {
  609. u32 features_ebx;
  610. u32 features_ecx;
  611. u32 features_edx;
  612. CPUID(1, NULL, &features_ebx, &features_ecx, &features_edx);
  613. #define F(name, bit) features.name = isBitSet(features_edx, (bit))
  614. F(fpu, 0);
  615. F(vme, 1);
  616. F(de, 2);
  617. F(pse, 3);
  618. F(tsc, 4);
  619. F(msr, 5);
  620. F(pae, 6);
  621. F(mce, 7);
  622. F(cx8, 8);
  623. F(apic, 9);
  624. F(sep, 11);
  625. F(mtrr, 12);
  626. F(pge, 13);
  627. F(mca, 14);
  628. F(cmov, 15);
  629. F(pat, 16);
  630. F(pse36, 17);
  631. F(serial, 18);
  632. F(clfsh, 19);
  633. F(ds, 21);
  634. F(acpi, 22);
  635. F(mmx, 23);
  636. F(fxsr, 24);
  637. F(sse, 25);
  638. F(sse2, 26);
  639. F(ss, 27);
  640. F(htt, 28);
  641. F(thermal, 29);
  642. F(ia64, 30);
  643. F(pbe, 31);
  644. #undef F
  645. // Verify that floating point SSE works.
  646. if (features.sse) {
  647. features.ssefp = getSSEFPSupport();
  648. } else {
  649. features.ssefp = false;
  650. }
  651. features.CLFLUSHCacheLineSize = (features_ebx >> 8) & 0xFF;
  652. features.APIC_ID = (features_ebx >> 24) & 0xFF;
  653. features.sse3 = isBitSet(features_ecx, 0);
  654. features.monitor = isBitSet(features_ecx, 3);
  655. features.ds_cpl = isBitSet(features_ecx, 4);
  656. features.est = isBitSet(features_ecx, 7);
  657. features.tm2 = isBitSet(features_ecx, 8);
  658. features.cnxt_id = isBitSet(features_ecx, 10);
  659. features.logicalProcessorsPerPhysical = (features.htt
  660. ? (features_ebx >> 16) & 0xFF
  661. : 1);
  662. }
  663. static void getExtendedFeatures(const CPUInfo::Identity& id, CPUInfo::Features& features) {
  664. if (checkExtendedLevelSupport(id, 0x80000001)) {
  665. u32 ex_signature;
  666. u32 ex_features;
  667. CPUID(0x80000001, &ex_signature, NULL, NULL, &ex_features);
  668. // Retrieve the extended features of CPU present.
  669. features._3dnow = isBitSet(ex_features, 31);
  670. features._3dnowPlus = isBitSet(ex_features, 30);
  671. features.ssemmx = isBitSet(ex_features, 22);
  672. features.supportsMP = isBitSet(ex_features, 19);
  673. // MMX+ is reported differently by manufacturers.
  674. if (id.manufacturer == CPUInfo::AMD) {
  675. features.mmxPlus = isBitSet(ex_features, 22);
  676. } else if (id.manufacturer == CPUInfo::Cyrix) {
  677. features.mmxPlus = isBitSet(ex_features, 24);
  678. } else {
  679. features.mmxPlus = false;
  680. }
  681. } else {
  682. features._3dnow = false;
  683. features._3dnowPlus = false;
  684. features.ssemmx = false;
  685. features.mmxPlus = false;
  686. features.supportsMP = false;
  687. }
  688. }
  689. static void getSerialNumber(CPUInfo& info) {
  690. // Verify that the processor has a serial number.
  691. assert(info.features.serial);
  692. unsigned char serialNumber[12];
  693. CPUID(3, NULL,
  694. (u32*)serialNumber,
  695. (u32*)serialNumber + 1,
  696. (u32*)serialNumber + 2);
  697. sprintf(info.features.serialNumber,
  698. "%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x-%.2x%.2x",
  699. serialNumber[0],
  700. serialNumber[1],
  701. serialNumber[2],
  702. serialNumber[3],
  703. serialNumber[4],
  704. serialNumber[5],
  705. serialNumber[6],
  706. serialNumber[7],
  707. serialNumber[8],
  708. serialNumber[9],
  709. serialNumber[10],
  710. serialNumber[11]);
  711. }
  712. static bool getCacheDetails(const CPUInfo::Identity& id, CPUInfo::Cache& cache) {
  713. if (checkExtendedLevelSupport(id, 0x80000005)) {
  714. u32 L1[4];
  715. CPUID(0x80000005, &L1[0], &L1[1], &L1[2], &L1[3]);
  716. cache.L1CacheSize = (L1[2] >> 24) & 0xFF;
  717. cache.L1CacheSize += (L1[3] >> 24) & 0xFF;
  718. } else {
  719. cache.L1CacheSize = -1;
  720. }
  721. if (checkExtendedLevelSupport(id, 0x80000006)) {
  722. u32 L2[4];
  723. CPUID(0x80000006, &L2[0], &L2[1], &L2[2], &L2[3]);
  724. cache.L2CacheSize = (L2[2] >> 16) & 0xFFFF;
  725. } else {
  726. cache.L2CacheSize = -1;
  727. }
  728. // No way to query L3.
  729. cache.L3CacheSize = -1;
  730. // Return failure if we cannot detect either cache with this method.
  731. return cache.L1CacheSize != -1 ||
  732. cache.L2CacheSize != -1;
  733. }
  734. static void getClassicalCacheDetails(CPUInfo::Cache& cache) {
  735. //int TLBCode = -1;
  736. //int TLBData = -1;
  737. int L1Code = -1;
  738. int L1Data = -1;
  739. int L1Trace = -1;
  740. int L2Unified = -1;
  741. int L3Unified = -1;
  742. int passTotal;
  743. int passCounter = 0;
  744. do {
  745. unsigned char cacheData[16];
  746. CPUID(2,
  747. (u32*)cacheData,
  748. (u32*)cacheData + 1,
  749. (u32*)cacheData + 2,
  750. (u32*)cacheData + 3);
  751. passTotal = cacheData[0];
  752. for (int counter = 1; counter < 16; ++counter) {
  753. // Process descriptors.
  754. switch (cacheData[counter]) {
  755. case 0x00: break;
  756. //case 0x01: TLBCode = 4; break;
  757. //case 0x02: TLBCode = 4096; break;
  758. //case 0x03: TLBData = 4; break;
  759. //case 0x04: TLBData = 4096; break;
  760. case 0x06: L1Code = 8; break;
  761. case 0x08: L1Code = 16; break;
  762. case 0x0a: L1Data = 8; break;
  763. case 0x0c: L1Data = 16; break;
  764. case 0x10: L1Data = 16; break; // <-- FIXME: IA-64 Only
  765. case 0x15: L1Code = 16; break; // <-- FIXME: IA-64 Only
  766. case 0x1a: L2Unified = 96; break; // <-- FIXME: IA-64 Only
  767. case 0x22: L3Unified = 512; break;
  768. case 0x23: L3Unified = 1024; break;
  769. case 0x25: L3Unified = 2048; break;
  770. case 0x29: L3Unified = 4096; break;
  771. case 0x39: L2Unified = 128; break;
  772. case 0x3c: L2Unified = 256; break;
  773. case 0x40: L2Unified = 0; break; // <-- FIXME: No integrated L2 cache (P6 core) or L3 cache (P4 core).
  774. case 0x41: L2Unified = 128; break;
  775. case 0x42: L2Unified = 256; break;
  776. case 0x43: L2Unified = 512; break;
  777. case 0x44: L2Unified = 1024; break;
  778. case 0x45: L2Unified = 2048; break;
  779. //case 0x50: TLBCode = 4096; break;
  780. //case 0x51: TLBCode = 4096; break;
  781. //case 0x52: TLBCode = 4096; break;
  782. //case 0x5b: TLBData = 4096; break;
  783. //case 0x5c: TLBData = 4096; break;
  784. //case 0x5d: TLBData = 4096; break;
  785. case 0x66: L1Data = 8; break;
  786. case 0x67: L1Data = 16; break;
  787. case 0x68: L1Data = 32; break;
  788. case 0x70: L1Trace = 12; break;
  789. case 0x71: L1Trace = 16; break;
  790. case 0x72: L1Trace = 32; break;
  791. case 0x77: L1Code = 16; break; // <-- FIXME: IA-64 Only
  792. case 0x79: L2Unified = 128; break;
  793. case 0x7a: L2Unified = 256; break;
  794. case 0x7b: L2Unified = 512; break;
  795. case 0x7c: L2Unified = 1024; break;
  796. case 0x7e: L2Unified = 256; break;
  797. case 0x81: L2Unified = 128; break;
  798. case 0x82: L2Unified = 256; break;
  799. case 0x83: L2Unified = 512; break;
  800. case 0x84: L2Unified = 1024; break;
  801. case 0x85: L2Unified = 2048; break;
  802. case 0x88: L3Unified = 2048; break; // <-- FIXME: IA-64 Only
  803. case 0x89: L3Unified = 4096; break; // <-- FIXME: IA-64 Only
  804. case 0x8a: L3Unified = 8192; break; // <-- FIXME: IA-64 Only
  805. case 0x8d: L3Unified = 3096; break; // <-- FIXME: IA-64 Only
  806. //case 0x90: TLBCode, 262144; break; // <-- FIXME: IA-64 Only
  807. //case 0x96: TLBCode, 262144; break; // <-- FIXME: IA-64 Only
  808. //case 0x9b: TLBCode, 262144; break; // <-- FIXME: IA-64 Only
  809. }
  810. }
  811. ++passCounter;
  812. } while (passCounter < passTotal);
  813. if (L1Code == -1 && L1Data == -1 && L1Trace == -1) {
  814. cache.L1CacheSize = -1;
  815. } else {
  816. cache.L1CacheSize = 0;
  817. if (L1Code != -1) cache.L1CacheSize += L1Code;
  818. if (L1Data != -1) cache.L1CacheSize += L1Data;
  819. if (L1Trace != -1) cache.L1CacheSize += L1Trace;
  820. }
  821. cache.L2CacheSize = L2Unified;
  822. cache.L3CacheSize = L3Unified;
  823. }
  824. static void getPowerManagement(const CPUInfo::Identity& id, CPUInfo::PowerManagement& pm) {
  825. if (checkExtendedLevelSupport(id, 0x80000007)) {
  826. u32 pmflags = 0;
  827. CPUID(0x80000007, NULL, NULL, NULL, &pmflags);
  828. pm.ts = isBitSet(pmflags, 0);
  829. pm.fid = isBitSet(pmflags, 1);
  830. pm.vid = isBitSet(pmflags, 2);
  831. pm.ttp = isBitSet(pmflags, 3);
  832. pm.tm = isBitSet(pmflags, 4);
  833. pm.stc = isBitSet(pmflags, 5);
  834. } else {
  835. pm.ts = false;
  836. pm.fid = false;
  837. pm.vid = false;
  838. pm.ttp = false;
  839. pm.tm = false;
  840. pm.stc = false;
  841. }
  842. }
  843. /// Duration in milliseconds.
  844. static int measureFrequency(unsigned duration) {
  845. // Run a high-performance timer with a known frequency against the
  846. // processor clock to calculate the processor's frequency.
  847. u64 frequencyPC = getHPFrequency();
  848. u64 ticks = duration * frequencyPC / 1000;
  849. u64 startPC = getHPCounter();
  850. u64 startTSC = RDTSC();
  851. u64 endPC;
  852. do {
  853. endPC = getHPCounter();
  854. } while ((endPC - startPC) < ticks);
  855. u64 endTSC = RDTSC();
  856. u64 elapsedPC = endPC - startPC;
  857. u64 elapsedTSC = endTSC - startTSC;
  858. return int(elapsedTSC * frequencyPC / elapsedPC / 1000000);
  859. }
  860. static int getFrequency() {
  861. return measureFrequency(50);
  862. }
  863. static int getClassicalFrequency(const CPUInfo& info) {
  864. static const u32 LOOP_LENGTH = 10000000;
  865. u64 start = getHPCounter();
  866. classicalTimingLoop(LOOP_LENGTH);
  867. u64 end = getHPCounter();
  868. u64 countsPerSecond = getHPFrequency();
  869. // Calculate loop running time in seconds.
  870. double duration = double(end - start) / countsPerSecond;
  871. // Calculate the clock speed.
  872. if (info.identity.family == 3) {
  873. // 80386 processors.... Loop time is 115 cycles!
  874. return int(((LOOP_LENGTH * 115.0) / duration) / 1000000);
  875. } else if (info.identity.family == 4) {
  876. // 80486 processors.... Loop time is 47 cycles!
  877. return int(((LOOP_LENGTH * 47.0) / duration) / 1000000);
  878. } else if (info.identity.family == 5) {
  879. // Pentium processors.... Loop time is 43 cycles!
  880. return int(((LOOP_LENGTH * 43.0) / duration) / 1000000);
  881. } else {
  882. // Unknown cycle count.
  883. return 0;
  884. }
  885. }
  886. static int getCPUFrequency(const CPUInfo& info) {
  887. if (info.features.tsc) {
  888. return getFrequency();
  889. } else {
  890. return getClassicalFrequency(info);
  891. }
  892. }
  893. void getCPUInfo(CPUInfo& info) {
  894. // CPUID support.
  895. info.supportsCPUID = getCPUIDSupport();
  896. if (info.supportsCPUID) {
  897. // Identity.
  898. getIdentity(info.identity);
  899. getExtendedIdentity(info.identity);
  900. // Features.
  901. getFeatures(info.features);
  902. getExtendedFeatures(info.identity, info.features);
  903. if (info.features.serial) {
  904. getSerialNumber(info);
  905. }
  906. // Cache.
  907. if (!getCacheDetails(info.identity, info.cache)) {
  908. getClassicalCacheDetails(info.cache);
  909. }
  910. // Power management.
  911. getPowerManagement(info.identity, info.powerManagement);
  912. info.frequency = getCPUFrequency(info);
  913. }
  914. }
  915. #if defined(_MSC_VER) || defined(__CYGWIN__)
  916. int getCPUCount() {
  917. SYSTEM_INFO info;
  918. GetSystemInfo(&info);
  919. return info.dwNumberOfProcessors;
  920. }
  921. static DWORD WINAPI retrieverThreadProc(LPVOID parameter) {
  922. CPUInfo* info = (CPUInfo*)parameter;
  923. getCPUInfo(*info);
  924. return 0;
  925. }
  926. int getMultipleCPUInfo(CPUInfo* array) {
  927. DWORD processAffinityMask;
  928. DWORD systemAffinityMask;
  929. GetProcessAffinityMask(
  930. GetCurrentProcess(),
  931. &processAffinityMask,
  932. &systemAffinityMask);
  933. int totalQueried = 0;
  934. int processorCount = getCPUCount();
  935. HANDLE* handles = new HANDLE[processorCount];
  936. for (int i = 0; i < processorCount; ++i) {
  937. // Skip if processor is disabled.
  938. int currentMask = (1 << i);
  939. if (!(processAffinityMask & currentMask)) {
  940. continue;
  941. }
  942. HANDLE& handle = handles[totalQueried];
  943. DWORD dummy;
  944. handle = CreateThread(
  945. NULL, 0, retrieverThreadProc, array + totalQueried,
  946. CREATE_SUSPENDED, &dummy);
  947. if (!handle) {
  948. continue;
  949. }
  950. if (0 == SetThreadAffinityMask(handle, currentMask)) {
  951. CloseHandle(handle);
  952. continue;
  953. }
  954. ResumeThread(handle);
  955. ++totalQueried;
  956. }
  957. WaitForMultipleObjects(totalQueried, handles, TRUE, INFINITE);
  958. for (int i = 0; i < totalQueried; ++i) {
  959. CloseHandle(handles[i]);
  960. }
  961. delete[] handles;
  962. return totalQueried;
  963. }
  964. #elif defined(__APPLE__)
  965. #include <sys/param.h>
  966. #include <sys/sysctl.h>
  967. // http://alienryderflex.com/processor_count.html
  968. int getCPUCount() {
  969. int count;
  970. size_t size = sizeof(count);
  971. if (sysctlbyname("hw.ncpu", &count, &size, NULL, 0)) {
  972. return 1;
  973. }
  974. return count;
  975. }
  976. int getMultipleCPUInfo(CPUInfo* array) {
  977. int cpuCount = getCPUCount();
  978. for (int i = 0; i < cpuCount; ++i) {
  979. // Do we need to yield to make sure we're actually going to run on that
  980. // processor? Not according to the online docs... affinity changes
  981. // should happen immediately.
  982. // sched_yield();
  983. getCPUInfo(array[i]);
  984. }
  985. return cpuCount;
  986. }
  987. #else // Linux
  988. #include <sched.h>
  989. int getCPUCount() {
  990. cpu_set_t mask;
  991. int res = sched_getaffinity(0, sizeof(mask), &mask);
  992. if (res == -1) {
  993. return 0;
  994. }
  995. int cpuCount = 0;
  996. for (int i = 0; i < CPU_SETSIZE; ++i) {
  997. if (CPU_ISSET(i, &mask)) {
  998. ++cpuCount;
  999. }
  1000. }
  1001. return cpuCount;
  1002. }
  1003. int getMultipleCPUInfo(CPUInfo* array) {
  1004. cpu_set_t oldMask;
  1005. int res = sched_getaffinity(0, sizeof(oldMask), &oldMask);
  1006. if (res == -1) {
  1007. return 0;
  1008. }
  1009. int cpuCount = getCPUCount();
  1010. int totalQueried = 0;
  1011. for (int i = 0; i < cpuCount; ++i) {
  1012. cpu_set_t mask;
  1013. CPU_ZERO(&mask);
  1014. CPU_SET(i, &mask);
  1015. res = sched_setaffinity(0, sizeof(mask), &mask);
  1016. if (res == -1) {
  1017. continue;
  1018. }
  1019. // Do we need to yield to make sure we're actually going to run on that
  1020. // processor? Not according to the online docs... affinity changes
  1021. // should happen immediately.
  1022. // sched_yield();
  1023. getCPUInfo(array[totalQueried]);
  1024. ++totalQueried;
  1025. }
  1026. // Make sure we restore the original affinity.
  1027. sched_setaffinity(0, sizeof(oldMask), &oldMask);
  1028. return totalQueried;
  1029. }
  1030. #endif