cpuid.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /*
  2. * Copyright (c) 2013 Ahmed Samy <f.fallen45@gmail.com>
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies 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 included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. * THE SOFTWARE.
  21. *
  22. * This file has been written with some help from wikipedia:
  23. * http://en.wikipedia.org/wiki/CPUID
  24. */
  25. /* Only compile this file if we're on a x86 machine. */
  26. #if defined(__i386__) || defined(__i386) || defined(__x86_64) \
  27. || defined(_M_AMD64) || defined(__M_X64)
  28. #include "cpuid.h"
  29. #include <string.h>
  30. enum {
  31. CPU_PROC_BRAND_STRING_INTERNAL0 = 0x80000003,
  32. CPU_PROC_BRAND_STRING_INTERNAL1 = 0x80000004
  33. };
  34. #ifndef _MSC_VER
  35. static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
  36. {
  37. __asm__(
  38. "xchg %%ebx, %%edi\n\t" /* 32bit PIC: Don't clobber ebx. */
  39. "cpuid\n\t"
  40. "xchg %%ebx, %%edi\n\t"
  41. : "=a"(*eax), "=D"(*ebx), "=c"(*ecx), "=d"(*edx)
  42. : "0" (info)
  43. );
  44. }
  45. #else
  46. #include <intrin.h>
  47. static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx)
  48. {
  49. uint32_t registers[4];
  50. __cpuid(registers, info);
  51. *eax = registers[0];
  52. *ebx = registers[1];
  53. *ecx = registers[2];
  54. *edx = registers[3];
  55. }
  56. #endif
  57. static struct {
  58. uint32_t feature;
  59. uint32_t mask;
  60. bool use_edx; /* ecx will be used if false. */
  61. } features[] = {
  62. { CF_MMX, 1 << 23, true },
  63. { CF_SSE, 1 << 25, true },
  64. { CF_SSE2, 1 << 26, true },
  65. { CF_SSE3, 1 << 9, false },
  66. { CF_FPU, 1 << 0, true },
  67. { CF_TSC, 1 << 4, true },
  68. { CF_MSR, 1 << 5, true },
  69. { CF_SSSE3, 1 << 9, false },
  70. { CF_AVX, 1 << 28, false },
  71. /* Extended ones. */
  72. { CEF_x64, 1 << 30, true },
  73. { CEF_FPU, 1 << 0, true },
  74. { CEF_DE, 1 << 2, true },
  75. { CEF_SYSCALLRET, 1 << 11, true },
  76. { CEF_CMOV, 1 << 15, true },
  77. { CEF_SSE4a, 1 << 6, false },
  78. { CEF_FMA4, 1 << 16, false },
  79. { CEF_XOP, 1 << 11, false }
  80. };
  81. bool cpuid_is_supported(void)
  82. {
  83. int ret = 0;
  84. #if defined(__GNUC__) || defined(__clang__)
  85. /* The following assembly code uses EAX as the return value,
  86. * but we store the value of EAX into ret since GCC uses EAX
  87. * as the return register for every C function. That's a double
  88. * operation, but there's no other way to do this unless doing this
  89. * function entirely in assembly.
  90. *
  91. * The following assembly code has been shamelessly stolen from:
  92. * http://wiki.osdev.org/CPUID
  93. * and converted to work with AT&T syntax.
  94. *
  95. * This check is to make sure that the compiler is actually compiling
  96. * for 64-bit.
  97. *
  98. * The compiler can be 32-bit and the system 64-bit so the
  99. * following would be true:
  100. * #if defined(__x86_64) ...
  101. */
  102. #if UINTPTR_MAX == 0xffffffffffffffff
  103. #define ASM_PUSHF "pushfq\n\t"
  104. #define ASM_POPF "popfq\n\t"
  105. #define ASM_PUSHEAX "pushq %%rax\n\t"
  106. #define ASM_POPEAX "popq %%rax\n\t"
  107. #define ASM_PUSHECX "pushq %%rcx\n\t"
  108. #elif UINTPTR_MAX == 0xffffffff
  109. #define ASM_PUSHF "pushfl\n\t"
  110. #define ASM_POPF "popfl\n\t"
  111. #define ASM_PUSHEAX "pushl %%eax\n\t"
  112. #define ASM_POPEAX "popl %%eax\n\t"
  113. #define ASM_PUSHECX "pushl %%ecx\n\t"
  114. #endif
  115. asm volatile(
  116. ASM_PUSHF
  117. ASM_POPEAX
  118. "movl %%eax, %%ecx\n\t"
  119. "xorl $0x200000, %%eax\n\t"
  120. ASM_PUSHEAX
  121. ASM_POPF
  122. ASM_PUSHF
  123. ASM_POPEAX
  124. "xorl %%ecx, %%eax\n\t"
  125. "shrl $21, %%eax\n\t"
  126. "andl $1, %%eax\n\t"
  127. ASM_PUSHECX
  128. ASM_POPF
  129. : "=a" (ret)
  130. );
  131. #undef ASM_PUSHF
  132. #undef ASM_POPF
  133. #undef ASM_PUSHEAX
  134. #undef ASM_POPEAX
  135. #undef ASM_PUSHECX
  136. #elif defined _MSC_VER
  137. __asm {
  138. pushfd
  139. pop eax
  140. mov ecx, eax
  141. xor eax, 0x200000
  142. push eax
  143. popfd
  144. pushfd
  145. pop eax
  146. xor eax, ecx
  147. shr eax, 0x21
  148. and eax, 0x1
  149. push ecx
  150. popfd
  151. mov eax, ret
  152. };
  153. #endif
  154. return !!ret;
  155. }
  156. bool cpuid_test_feature(cpuid_t feature)
  157. {
  158. if (feature > CPU_VIRT_PHYS_ADDR_SIZES || feature < CPU_EXTENDED_PROC_INFO_FEATURE_BITS)
  159. return false;
  160. return (feature <= cpuid_highest_ext_func_supported());
  161. }
  162. bool cpuid_has_feature(int feature, bool extended)
  163. {
  164. uint32_t eax, ebx, ecx, edx, i;
  165. if (!extended)
  166. ___cpuid(CPU_PROCINFO_AND_FEATUREBITS, &eax, &ebx, &ecx, &edx);
  167. else
  168. ___cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, &eax, &ebx, &ecx, &edx);
  169. for (i = 0; i < sizeof(features) / sizeof(features[0]); ++i) {
  170. if (features[i].feature == feature) {
  171. if (features[i].use_edx)
  172. return (edx & features[i].mask);
  173. else
  174. return (ecx & features[i].mask);
  175. }
  176. }
  177. return false;
  178. }
  179. static const char *const cpuids[] = {
  180. "Nooooooooone",
  181. "AMDisbetter!",
  182. "AuthenticAMD",
  183. "CentaurHauls",
  184. "CyrixInstead",
  185. "GenuineIntel",
  186. "TransmetaCPU",
  187. "GeniuneTMx86",
  188. "Geode by NSC",
  189. "NexGenDriven",
  190. "RiseRiseRise",
  191. "SiS SiS SiS ",
  192. "UMC UMC UMC ",
  193. "VIA VIA VIA ",
  194. "Vortex86 SoC",
  195. "KVMKVMKVMKVM"
  196. };
  197. cputype_t cpuid_get_cpu_type(void)
  198. {
  199. static cputype_t cputype;
  200. if (cputype == CT_NONE) {
  201. union {
  202. char buf[12];
  203. uint32_t bufu32[3];
  204. } u;
  205. uint32_t i;
  206. ___cpuid(CPU_VENDORID, &i, &u.bufu32[0], &u.bufu32[2], &u.bufu32[1]);
  207. for (i = 0; i < sizeof(cpuids) / sizeof(cpuids[0]); ++i) {
  208. if (strncmp(cpuids[i], u.buf, 12) == 0) {
  209. cputype = (cputype_t)i;
  210. break;
  211. }
  212. }
  213. }
  214. return cputype;
  215. }
  216. bool cpuid_sprintf_cputype(const cputype_t cputype, char *buf)
  217. {
  218. if (cputype == CT_NONE)
  219. return false;
  220. memcpy(buf, cpuids[(int)cputype], 12);
  221. buf[12] = '\0';
  222. return true;
  223. }
  224. uint32_t cpuid_highest_ext_func_supported(void)
  225. {
  226. static uint32_t highest;
  227. if (!highest) {
  228. #if defined(__GNUC__) || defined(__clang__)
  229. asm volatile(
  230. "cpuid\n\t"
  231. : "=a" (highest)
  232. : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED)
  233. );
  234. #elif defined _MSC_VER
  235. __asm {
  236. mov eax, CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
  237. cpuid
  238. mov highest, eax
  239. };
  240. #endif
  241. }
  242. return highest;
  243. }
  244. void cpuid(cpuid_t info, uint32_t *buf)
  245. {
  246. /* Sanity checks, make sure we're not trying to do something
  247. * invalid or we are trying to get information that isn't supported
  248. * by the CPU. */
  249. if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED
  250. && !cpuid_test_feature(info)))
  251. return;
  252. if (info == CPU_PROC_BRAND_STRING) {
  253. static char cached[48] = { 0 };
  254. if (cached[0] == '\0') {
  255. ___cpuid(CPU_PROC_BRAND_STRING, &buf[0], &buf[1], &buf[2], &buf[3]);
  256. ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &buf[4], &buf[5], &buf[6], &buf[7]);
  257. ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &buf[8], &buf[9], &buf[10], &buf[11]);
  258. memcpy(cached, buf, sizeof cached);
  259. } else
  260. buf = (uint32_t *)cached;
  261. return;
  262. } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) {
  263. *buf = cpuid_highest_ext_func_supported();
  264. return;
  265. }
  266. uint32_t eax, ebx, ecx, edx;
  267. ___cpuid(info, &eax, &ebx, &ecx, &edx);
  268. switch (info) {
  269. case CPU_VENDORID:
  270. buf[0] = ebx;
  271. buf[1] = edx;
  272. buf[2] = ecx;
  273. break;
  274. case CPU_PROCINFO_AND_FEATUREBITS:
  275. buf[0] = eax; /* The so called "signature" of the CPU. */
  276. buf[1] = edx; /* Feature flags #1. */
  277. buf[2] = ecx; /* Feature flags #2. */
  278. buf[3] = ebx; /* Additional feature information. */
  279. break;
  280. case CPU_CACHE_AND_TLBD_INFO:
  281. buf[0] = eax;
  282. buf[1] = ebx;
  283. buf[2] = ecx;
  284. buf[3] = edx;
  285. break;
  286. case CPU_EXTENDED_PROC_INFO_FEATURE_BITS:
  287. buf[0] = edx;
  288. buf[1] = ecx;
  289. break;
  290. case CPU_L1_CACHE_AND_TLB_IDS:
  291. buf[0] = eax;
  292. buf[1] = ebx;
  293. buf[2] = ecx;
  294. buf[3] = edx;
  295. break;
  296. case CPU_EXTENDED_L2_CACHE_FEATURES:
  297. buf[0] = ecx & 0xFF; /* Line size. */
  298. buf[1] = (ecx >> 12) & 0xFF; /* Associativity. */
  299. buf[2] = ecx >> 16; /* Cache size. */
  300. break;
  301. case CPU_ADV_POWER_MGT_INFO:
  302. *buf = edx;
  303. break;
  304. case CPU_VIRT_PHYS_ADDR_SIZES:
  305. buf[0] = eax & 0xFF; /* physical. */
  306. buf[1] = (eax >> 8) & 0xFF; /* virtual. */
  307. break;
  308. default:
  309. *buf = 0xbaadf00d;
  310. break;
  311. }
  312. }
  313. #endif