1/* 2 * Copyright 2004-2012, Axel D��rfler, axeld@pinc-software.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6/* Taken from the Pulse application, and extended. 7 * It's used by Pulse, AboutHaiku, and sysinfo. 8 */ 9 10#include <stdlib.h> 11#include <stdio.h> 12#include <string.h> 13 14#include <OS.h> 15 16 17#ifdef __cplusplus 18extern "C" { 19#endif 20 21const char *get_cpu_vendor_string(enum cpu_types type); 22const char *get_cpu_model_string(system_info *info); 23void get_cpu_type(char *vendorBuffer, size_t vendorSize, 24 char *modelBuffer, size_t modelSize); 25int32 get_rounded_cpu_speed(void); 26 27#ifdef __cplusplus 28} 29#endif 30 31 32#if defined(__INTEL__) || defined(__x86_64__) 33/*! Tries to parse an Intel CPU ID string to match our usual naming scheme. 34 Note, this function is not thread safe, and must only be called once 35 at a time. 36*/ 37static const char* 38parse_intel(const char* name) 39{ 40 static char buffer[49]; 41 42 // ignore initial spaces 43 int index = 0; 44 for (; name[index] != '\0'; index++) { 45 if (name[index] != ' ') 46 break; 47 } 48 49 // ignore vendor 50 for (; name[index] != '\0'; index++) { 51 if (name[index] == ' ') { 52 index++; 53 break; 54 } 55 } 56 57 // parse model 58 int outIndex = 0; 59 for (; name[index] != '\0'; index++) { 60 if (!strncmp(&name[index], "(R)", 3)) { 61 outIndex += strlcpy(&buffer[outIndex], "��", 62 sizeof(buffer) - outIndex); 63 index += 2; 64 } else if (!strncmp(&name[index], "(TM)", 4)) { 65 outIndex += strlcpy(&buffer[outIndex], "���", 66 sizeof(buffer) - outIndex); 67 index += 3; 68 } else if (!strncmp(&name[index], " CPU", 4)) { 69 // Cut out the CPU string 70 index += 3; 71 } else if (!strncmp(&name[index], " @", 2)) { 72 // Cut off the remainder 73 break; 74 } else 75 buffer[outIndex++] = name[index]; 76 } 77 78 buffer[outIndex] = '\0'; 79 return buffer; 80} 81#endif 82 83 84const char * 85get_cpu_vendor_string(enum cpu_types type) 86{ 87#if __POWERPC__ 88 /* We're not that nice here. */ 89 return "IBM/Motorola"; 90#endif 91#if defined(__INTEL__) || defined(__x86_64__) 92 /* Determine x86 vendor name */ 93 switch (type & B_CPU_x86_VENDOR_MASK) { 94 case B_CPU_INTEL_x86: 95 return "Intel"; 96 case B_CPU_AMD_x86: 97 return "AMD"; 98 case B_CPU_CYRIX_x86: 99 return "Cyrix"; 100 case B_CPU_IDT_x86: 101 /* IDT was bought by VIA */ 102 if (((type >> 8) & 0xf) >= 6) 103 return "VIA"; 104 return "IDT"; 105 case B_CPU_RISE_x86: 106 return "Rise"; 107 case B_CPU_TRANSMETA_x86: 108 return "Transmeta"; 109 110 default: 111 return NULL; 112 } 113#endif 114} 115 116 117#if defined(__INTEL__) || defined(__x86_64__) 118/*! Parameter 'name' needs to point to an allocated array of 49 characters. */ 119void 120get_cpuid_model_string(char *name) 121{ 122 /* References: 123 * 124 * http://grafi.ii.pw.edu.pl/gbm/x86/cpuid.html 125 * http://www.sandpile.org/ia32/cpuid.htm 126 * http://www.amd.com/us-en/assets/content_type/ 127 * white_papers_and_tech_docs/TN13.pdf (Duron erratum) 128 */ 129 130 cpuid_info baseInfo; 131 cpuid_info cpuInfo; 132 int32 maxStandardFunction, maxExtendedFunction = 0; 133 134 memset(name, 0, 49 * sizeof(char)); 135 136 if (get_cpuid(&baseInfo, 0, 0) != B_OK) { 137 /* This CPU doesn't support cpuid. */ 138 return; 139 } 140 141 maxStandardFunction = baseInfo.eax_0.max_eax; 142 if (maxStandardFunction >= 500) { 143 maxStandardFunction = 0; 144 /* Old Pentium sample chips have the CPU signature here. */ 145 } 146 147 /* Extended cpuid */ 148 149 get_cpuid(&cpuInfo, 0x80000000, 0); 150 /* hardcoded to CPU 0 */ 151 152 /* Extended cpuid is only supported if max_eax is greater than the */ 153 /* service id. */ 154 if (cpuInfo.eax_0.max_eax > 0x80000000) 155 maxExtendedFunction = cpuInfo.eax_0.max_eax & 0xff; 156 157 if (maxExtendedFunction >= 4) { 158 int32 i; 159 160 for (i = 0; i < 3; i++) { 161 cpuid_info nameInfo; 162 get_cpuid(&nameInfo, 0x80000002 + i, 0); 163 164 memcpy(name, &nameInfo.regs.eax, 4); 165 memcpy(name + 4, &nameInfo.regs.ebx, 4); 166 memcpy(name + 8, &nameInfo.regs.ecx, 4); 167 memcpy(name + 12, &nameInfo.regs.edx, 4); 168 name += 16; 169 } 170 } 171} 172#endif /* __INTEL__ || __x86_64__ */ 173 174 175const char * 176get_cpu_model_string(system_info *info) 177{ 178#if defined(__INTEL__) || defined(__x86_64__) 179 char cpuidName[49]; 180 /* for use with get_cpuid_model_string() */ 181#endif /* __INTEL__ || __x86_64__ */ 182 183 /* Determine CPU type */ 184 switch (info->cpu_type) { 185#if __POWERPC__ 186 case B_CPU_PPC_603: 187 return "603"; 188 case B_CPU_PPC_603e: 189 return "603e"; 190 case B_CPU_PPC_750: 191 return "750"; 192 case B_CPU_PPC_604: 193 return "604"; 194 case B_CPU_PPC_604e: 195 return "604e"; 196 default: 197 return NULL; 198#endif /* __POWERPC__ */ 199#if defined(__INTEL__) || defined(__x86_64__) 200 case B_CPU_x86: 201 return "Unknown x86"; 202 203 /* Intel */ 204 case B_CPU_INTEL_PENTIUM: 205 case B_CPU_INTEL_PENTIUM75: 206 return "Pentium"; 207 case B_CPU_INTEL_PENTIUM_486_OVERDRIVE: 208 case B_CPU_INTEL_PENTIUM75_486_OVERDRIVE: 209 return "Pentium OD"; 210 case B_CPU_INTEL_PENTIUM_MMX: 211 case B_CPU_INTEL_PENTIUM_MMX_MODEL_8: 212 return "Pentium MMX"; 213 case B_CPU_INTEL_PENTIUM_PRO: 214 return "Pentium Pro"; 215 case B_CPU_INTEL_PENTIUM_II_MODEL_3: 216 case B_CPU_INTEL_PENTIUM_II_MODEL_5: 217 return "Pentium II"; 218 case B_CPU_INTEL_CELERON: 219 case B_CPU_INTEL_CELERON_MODEL_22: 220 return "Celeron"; 221 case B_CPU_INTEL_PENTIUM_III: 222 case B_CPU_INTEL_PENTIUM_III_MODEL_8: 223 case B_CPU_INTEL_PENTIUM_III_MODEL_11: 224 case B_CPU_INTEL_PENTIUM_III_XEON: 225 return "Pentium III"; 226 case B_CPU_INTEL_PENTIUM_M: 227 case B_CPU_INTEL_PENTIUM_M_MODEL_13: 228 get_cpuid_model_string(cpuidName); 229 if (strcasestr(cpuidName, "Celeron") != NULL) 230 return "Pentium M Celeron"; 231 return "Pentium M"; 232 case B_CPU_INTEL_ATOM: 233 return "Atom"; 234 case B_CPU_INTEL_PENTIUM_CORE: 235 get_cpuid_model_string(cpuidName); 236 if (strcasestr(cpuidName, "Celeron") != NULL) 237 return "Core Celeron"; 238 return "Core"; 239 case B_CPU_INTEL_PENTIUM_CORE_2: 240 get_cpuid_model_string(cpuidName); 241 if (strcasestr(cpuidName, "Celeron") != NULL) 242 return "Core 2 Celeron"; 243 if (strcasestr(cpuidName, "Xeon") != NULL) 244 return "Core 2 Xeon"; 245 return "Core 2"; 246 case B_CPU_INTEL_PENTIUM_CORE_2_45_NM: 247 get_cpuid_model_string(cpuidName); 248 if (strcasestr(cpuidName, "Celeron") != NULL) 249 return "Core 2 Celeron"; 250 if (strcasestr(cpuidName, "Xeon") != NULL) 251 return "Core 2 Xeon"; 252 if (strcasestr(cpuidName, "Pentium") != NULL) 253 return "Pentium"; 254 if (strcasestr(cpuidName, "Extreme") != NULL) 255 return "Core 2 Extreme"; 256 return "Core 2"; 257 case B_CPU_INTEL_PENTIUM_CORE_I5_M430: 258 return "Core i5"; 259 case B_CPU_INTEL_PENTIUM_CORE_I7: 260 case B_CPU_INTEL_PENTIUM_CORE_I7_Q720: 261 get_cpuid_model_string(cpuidName); 262 if (strcasestr(cpuidName, "Xeon") != NULL) 263 return "Core i7 Xeon"; 264 return "Core i7"; 265 case B_CPU_INTEL_PENTIUM_IV: 266 case B_CPU_INTEL_PENTIUM_IV_MODEL_1: 267 case B_CPU_INTEL_PENTIUM_IV_MODEL_2: 268 case B_CPU_INTEL_PENTIUM_IV_MODEL_3: 269 case B_CPU_INTEL_PENTIUM_IV_MODEL_4: 270 get_cpuid_model_string(cpuidName); 271 if (strcasestr(cpuidName, "Celeron") != NULL) 272 return "Pentium 4 Celeron"; 273 if (strcasestr(cpuidName, "Xeon") != NULL) 274 return "Pentium 4 Xeon"; 275 return "Pentium 4"; 276 277 /* AMD */ 278 case B_CPU_AMD_K5_MODEL_0: 279 case B_CPU_AMD_K5_MODEL_1: 280 case B_CPU_AMD_K5_MODEL_2: 281 case B_CPU_AMD_K5_MODEL_3: 282 return "K5"; 283 case B_CPU_AMD_K6_MODEL_6: 284 case B_CPU_AMD_K6_MODEL_7: 285 return "K6"; 286 case B_CPU_AMD_K6_2: 287 return "K6-2"; 288 case B_CPU_AMD_K6_III: 289 case B_CPU_AMD_K6_III_MODEL_13: 290 return "K6-III"; 291 case B_CPU_AMD_GEODE_LX: 292 return "Geode LX"; 293 case B_CPU_AMD_ATHLON_MODEL_1: 294 case B_CPU_AMD_ATHLON_MODEL_2: 295 case B_CPU_AMD_ATHLON_THUNDERBIRD: 296 return "Athlon"; 297 case B_CPU_AMD_ATHLON_XP_MODEL_6: 298 case B_CPU_AMD_ATHLON_XP_MODEL_7: 299 case B_CPU_AMD_ATHLON_XP_MODEL_8: 300 case B_CPU_AMD_ATHLON_XP_MODEL_10: 301 return "Athlon XP"; 302 case B_CPU_AMD_DURON: 303 return "Duron"; 304 case B_CPU_AMD_ATHLON_64_MODEL_3: 305 case B_CPU_AMD_ATHLON_64_MODEL_4: 306 case B_CPU_AMD_ATHLON_64_MODEL_7: 307 case B_CPU_AMD_ATHLON_64_MODEL_8: 308 case B_CPU_AMD_ATHLON_64_MODEL_11: 309 case B_CPU_AMD_ATHLON_64_MODEL_12: 310 case B_CPU_AMD_ATHLON_64_MODEL_14: 311 case B_CPU_AMD_ATHLON_64_MODEL_15: 312 case B_CPU_AMD_ATHLON_64_MODEL_20: 313 case B_CPU_AMD_ATHLON_64_MODEL_23: 314 case B_CPU_AMD_ATHLON_64_MODEL_24: 315 case B_CPU_AMD_ATHLON_64_MODEL_27: 316 case B_CPU_AMD_ATHLON_64_MODEL_28: 317 case B_CPU_AMD_ATHLON_64_MODEL_31: 318 case B_CPU_AMD_ATHLON_64_MODEL_35: 319 case B_CPU_AMD_ATHLON_64_MODEL_43: 320 case B_CPU_AMD_ATHLON_64_MODEL_44: 321 case B_CPU_AMD_ATHLON_64_MODEL_47: 322 case B_CPU_AMD_ATHLON_64_MODEL_63: 323 case B_CPU_AMD_ATHLON_64_MODEL_79: 324 case B_CPU_AMD_ATHLON_64_MODEL_95: 325 case B_CPU_AMD_ATHLON_64_MODEL_127: 326 return "Athlon 64"; 327 case B_CPU_AMD_OPTERON_MODEL_5: 328 case B_CPU_AMD_OPTERON_MODEL_21: 329 case B_CPU_AMD_OPTERON_MODEL_33: 330 case B_CPU_AMD_OPTERON_MODEL_37: 331 case B_CPU_AMD_OPTERON_MODEL_39: 332 return "Opteron"; 333 case B_CPU_AMD_TURION_64_MODEL_36: 334 case B_CPU_AMD_TURION_64_MODEL_76: 335 case B_CPU_AMD_TURION_64_MODEL_104: 336 return "Turion 64"; 337 case B_CPU_AMD_PHENOM_MODEL_2: 338 return "Phenom"; 339 case B_CPU_AMD_PHENOM_II_MODEL_4: 340 case B_CPU_AMD_PHENOM_II_MODEL_5: 341 case B_CPU_AMD_PHENOM_II_MODEL_6: 342 case B_CPU_AMD_PHENOM_II_MODEL_10: 343 get_cpuid_model_string(cpuidName); 344 if (strcasestr(cpuidName, "Athlon") != NULL) 345 return "Athlon II"; 346 return "Phenom II"; 347 case B_CPU_AMD_A_SERIES: 348 return "A-Series"; 349 case B_CPU_AMD_C_SERIES: 350 return "C-Series"; 351 case B_CPU_AMD_E_SERIES: 352 return "E-Series"; 353 case B_CPU_AMD_FX_SERIES: 354 return "FX-Series"; 355 356 /* Transmeta */ 357 case B_CPU_TRANSMETA_CRUSOE: 358 return "Crusoe"; 359 case B_CPU_TRANSMETA_EFFICEON: 360 case B_CPU_TRANSMETA_EFFICEON_2: 361 return "Efficeon"; 362 363 /* IDT/VIA */ 364 case B_CPU_IDT_WINCHIP_C6: 365 return "WinChip C6"; 366 case B_CPU_IDT_WINCHIP_2: 367 return "WinChip 2"; 368 case B_CPU_VIA_C3_SAMUEL: 369 return "C3 Samuel"; 370 case B_CPU_VIA_C3_SAMUEL_2: 371 /* stepping identified the model */ 372 if ((info->cpu_revision & 0xf) < 8) 373 return "C3 Eden/Samuel 2"; 374 return "C3 Ezra"; 375 case B_CPU_VIA_C3_EZRA_T: 376 return "C3 Ezra-T"; 377 case B_CPU_VIA_C3_NEHEMIAH: 378 /* stepping identified the model */ 379 if ((info->cpu_revision & 0xf) < 8) 380 return "C3 Nehemiah"; 381 return "C3 Eden-N"; 382 case B_CPU_VIA_C7_ESTHER: 383 case B_CPU_VIA_C7_ESTHER_2: 384 return "C7"; 385 case B_CPU_VIA_NANO_ISAIAH: 386 return "Nano"; 387 388 /* Cyrix/VIA */ 389 case B_CPU_CYRIX_GXm: 390 return "GXm"; 391 case B_CPU_CYRIX_6x86MX: 392 return "6x86MX"; 393 394 /* Rise */ 395 case B_CPU_RISE_mP6: 396 return "mP6"; 397 398 /* National Semiconductor */ 399 case B_CPU_NATIONAL_GEODE_GX1: 400 return "Geode GX1"; 401 402 default: 403 if ((info->cpu_type & B_CPU_x86_VENDOR_MASK) == B_CPU_INTEL_x86) { 404 // Fallback to manual parsing of the model string 405 get_cpuid_model_string(cpuidName); 406 return parse_intel(cpuidName); 407 } 408 return NULL; 409#endif /* __INTEL__ || __x86_64__ */ 410 } 411} 412 413 414void 415get_cpu_type(char *vendorBuffer, size_t vendorSize, char *modelBuffer, 416 size_t modelSize) 417{ 418 const char *vendor, *model; 419 system_info info; 420 421 get_system_info(&info); 422 423 vendor = get_cpu_vendor_string(info.cpu_type); 424 if (vendor == NULL) 425 vendor = "Unknown"; 426 427 model = get_cpu_model_string(&info); 428 if (model == NULL) 429 model = "Unknown"; 430 431#ifdef R5_COMPATIBLE 432 strncpy(vendorBuffer, vendor, vendorSize - 1); 433 vendorBuffer[vendorSize - 1] = '\0'; 434 strncpy(modelBuffer, model, modelSize - 1); 435 modelBuffer[modelSize - 1] = '\0'; 436#else 437 strlcpy(vendorBuffer, vendor, vendorSize); 438 strlcpy(modelBuffer, model, modelSize); 439#endif 440} 441 442 443int32 444get_rounded_cpu_speed(void) 445{ 446 system_info info; 447 448 int target, frac, delta; 449 int freqs[] = { 100, 50, 25, 75, 33, 67, 20, 40, 60, 80, 10, 30, 70, 90 }; 450 uint x; 451 452 get_system_info(&info); 453 target = info.cpu_clock_speed / 1000000; 454 frac = target % 100; 455 delta = -frac; 456 457 for (x = 0; x < sizeof(freqs) / sizeof(freqs[0]); x++) { 458 int ndelta = freqs[x] - frac; 459 if (abs(ndelta) < abs(delta)) 460 delta = ndelta; 461 } 462 return target + delta; 463} 464 465