1/* 2 * Copyright 2004-2013, Axel D��rfler, axeld@pinc-software.de. 3 * Copyright 2013, Pawe�� Dziepak, pdziepak@quarnos.org. 4 * Distributed under the terms of the MIT License. 5 */ 6 7/* Taken from the Pulse application, and extended. 8 * It's used by Pulse, AboutHaiku, and sysinfo. 9 */ 10 11#include <stdlib.h> 12#include <stdio.h> 13#include <strings.h> 14 15#include <OS.h> 16 17 18#ifdef __cplusplus 19extern "C" { 20#endif 21 22static const char* get_cpu_vendor_string(enum cpu_vendor cpuVendor); 23static const char* get_cpu_model_string(enum cpu_platform platform, 24 enum cpu_vendor cpuVendor, uint32 cpuModel); 25void get_cpu_type(char *vendorBuffer, size_t vendorSize, 26 char *modelBuffer, size_t modelSize); 27int32 get_rounded_cpu_speed(void); 28 29#ifdef __cplusplus 30} 31#endif 32 33 34#if defined(__i386__) || defined(__x86_64__) 35/*! Tries to parse an Intel CPU ID string to match our usual naming scheme. 36 Note, this function is not thread safe, and must only be called once 37 at a time. 38*/ 39static const char* 40parse_intel(const char* name) 41{ 42 static char buffer[49]; 43 44 // ignore initial spaces 45 int index = 0; 46 for (; name[index] != '\0'; index++) { 47 if (name[index] != ' ') 48 break; 49 } 50 51 52 // parse model 53 int outIndex = 0; 54 for (; name[index] != '\0'; index++) { 55 // ignore vendor 56 if (strncmp(&name[index], "Intel", 5) == 0) { 57 for (; name[index] != '\0'; index++) { 58 if (name[index] == ' ') { 59 index++; 60 break; 61 } 62 } 63 } 64 if (!strncmp(&name[index], "(R)", 3)) { 65 outIndex += strlcpy(&buffer[outIndex], "��", 66 sizeof(buffer) - outIndex); 67 index += 2; 68 } else if (!strncmp(&name[index], "(TM)", 4)) { 69 outIndex += strlcpy(&buffer[outIndex], "���", 70 sizeof(buffer) - outIndex); 71 index += 3; 72 } else if (!strncmp(&name[index], " CPU", 4)) { 73 // Cut out the CPU string 74 index += 3; 75 } else if (!strncmp(&name[index], " @", 2)) { 76 // Cut off the remainder 77 break; 78 } else 79 buffer[outIndex++] = name[index]; 80 } 81 82 buffer[outIndex] = '\0'; 83 return buffer; 84} 85 86 87static const char* 88parse_amd(const char* name) 89{ 90 static char buffer[49]; 91 92 // ignore initial spaces 93 int index = 0; 94 for (; name[index] != '\0'; index++) { 95 if (name[index] != ' ') 96 break; 97 } 98 99 // Keep an initial "mobile" 100 int outIndex = 0; 101 bool spaceWritten = false; 102 if (!strncasecmp(&name[index], "Mobile ", 7)) { 103 strcpy(buffer, "Mobile "); 104 spaceWritten = true; 105 outIndex += 7; 106 index += 7; 107 } 108 109 // parse model 110 for (; name[index] != '\0'; index++) { 111 if (!strncasecmp(&name[index], "(r)", 3)) { 112 outIndex += strlcpy(&buffer[outIndex], "��", 113 sizeof(buffer) - outIndex); 114 index += 2; 115 } else if (!strncasecmp(&name[index], "(tm)", 4)) { 116 outIndex += strlcpy(&buffer[outIndex], "���", 117 sizeof(buffer) - outIndex); 118 index += 3; 119 } else if (!strncmp(&name[index], "with ", 5) 120 || !strncmp(&name[index], "/w", 2)) { 121 // Cut off the rest 122 break; 123 } else if (name[index] == '-') { 124 if (!spaceWritten) 125 buffer[outIndex++] = ' '; 126 spaceWritten = true; 127 } else { 128 const char* kWords[] = { 129 "Eight-core", "6-core", "Six-core", "Quad-core", "Dual-core", 130 "Dual core", "Processor", "APU", "AMD", "Intel", "Integrated", 131 "CyrixInstead", "Advanced Micro Devices", "Comb", "DualCore", 132 "Technology", "Mobile", "Triple-Core" 133 }; 134 bool removed = false; 135 for (size_t i = 0; i < sizeof(kWords) / sizeof(kWords[0]); i++) { 136 size_t length = strlen(kWords[i]); 137 if (!strncasecmp(&name[index], kWords[i], length)) { 138 index += length - 1; 139 removed = true; 140 break; 141 } 142 } 143 if (removed) 144 continue; 145 146 if (name[index] == ' ') { 147 if (spaceWritten) 148 continue; 149 spaceWritten = true; 150 } else 151 spaceWritten = false; 152 buffer[outIndex++] = name[index]; 153 } 154 } 155 156 // cut off trailing spaces 157 while (outIndex > 1 && buffer[outIndex - 1] == ' ') 158 outIndex--; 159 160 buffer[outIndex] = '\0'; 161 162 // skip new initial spaces 163 for (outIndex = 0; buffer[outIndex] != '\0'; outIndex++) { 164 if (buffer[outIndex] != ' ') 165 break; 166 } 167 return buffer + outIndex; 168} 169#endif 170 171 172static const char* 173get_cpu_vendor_string(enum cpu_vendor cpuVendor) 174{ 175 // Should match vendors in OS.h 176 static const char* vendorStrings[] = { 177 NULL, "AMD", "Cyrix", "IDT", "Intel", "National Semiconductor", "Rise", 178 "Transmeta", "VIA", "IBM", "Motorola", "NEC", "Hygon" 179 }; 180 181 if ((size_t)cpuVendor >= sizeof(vendorStrings) / sizeof(const char*)) 182 return NULL; 183 return vendorStrings[cpuVendor]; 184} 185 186 187#if defined(__i386__) || defined(__x86_64__) 188/*! Parameter 'name' needs to point to an allocated array of 49 characters. */ 189void 190get_cpuid_model_string(char *name) 191{ 192 /* References: 193 * 194 * http://grafi.ii.pw.edu.pl/gbm/x86/cpuid.html 195 * http://www.sandpile.org/ia32/cpuid.htm 196 * http://www.amd.com/us-en/assets/content_type/ 197 * white_papers_and_tech_docs/TN13.pdf (Duron erratum) 198 */ 199 200 cpuid_info baseInfo; 201 cpuid_info cpuInfo; 202 int32 maxStandardFunction, maxExtendedFunction = 0; 203 204 memset(name, 0, 49 * sizeof(char)); 205 206 if (get_cpuid(&baseInfo, 0, 0) != B_OK) { 207 /* This CPU doesn't support cpuid. */ 208 return; 209 } 210 211 maxStandardFunction = baseInfo.eax_0.max_eax; 212 if (maxStandardFunction >= 500) { 213 maxStandardFunction = 0; 214 /* Old Pentium sample chips have the CPU signature here. */ 215 } 216 217 /* Extended cpuid */ 218 219 get_cpuid(&cpuInfo, 0x80000000, 0); 220 /* hardcoded to CPU 0 */ 221 222 /* Extended cpuid is only supported if max_eax is greater than the */ 223 /* service id. */ 224 if (cpuInfo.eax_0.max_eax > 0x80000000) 225 maxExtendedFunction = cpuInfo.eax_0.max_eax & 0xff; 226 227 if (maxExtendedFunction >= 4) { 228 int32 i; 229 230 for (i = 0; i < 3; i++) { 231 cpuid_info nameInfo; 232 get_cpuid(&nameInfo, 0x80000002 + i, 0); 233 234 memcpy(name, &nameInfo.regs.eax, 4); 235 memcpy(name + 4, &nameInfo.regs.ebx, 4); 236 memcpy(name + 8, &nameInfo.regs.ecx, 4); 237 memcpy(name + 12, &nameInfo.regs.edx, 4); 238 name += 16; 239 } 240 } 241} 242#endif /* __i386__ || __x86_64__ */ 243 244 245static const char* 246get_cpu_model_string(enum cpu_platform platform, enum cpu_vendor cpuVendor, 247 uint32 cpuModel) 248{ 249#if defined(__i386__) || defined(__x86_64__) 250 char cpuidName[49]; 251#endif 252 253 (void)cpuVendor; 254 (void)cpuModel; 255 256#if defined(__i386__) || defined(__x86_64__) 257 if (platform != B_CPU_x86 && platform != B_CPU_x86_64) 258 return NULL; 259 260 // XXX: This *really* isn't accurate. There is differing math 261 // based on the CPU vendor.. Don't use these numbers anywhere 262 // except "fast and dumb" identification of processor names. 263 // 264 // see cpuidtool.c to decode cpuid signatures (sysinfo) into a 265 // value for this function. 266 // 267 // sysinfo has code in it which obtains the proper fam/mod/step ids 268 269 uint16 family = ((cpuModel >> 8) & 0xf) | ((cpuModel >> 16) & 0xff0); 270 uint16 model = ((cpuModel >> 4) & 0xf) | ((cpuModel >> 12) & 0xf0); 271 uint8 stepping = cpuModel & 0xf; 272 273 if (cpuVendor == B_CPU_VENDOR_AMD) { 274 if (family == 5) { 275 if (model <= 3) 276 return "K5"; 277 if (model <= 7) 278 return "K6"; 279 if (model == 8) 280 return "K6-2"; 281 if (model == 9 || model == 0xd) 282 return "K6-III"; 283 if (model == 0xa) 284 return "Geode LX"; 285 } else if (family == 6) { 286 if (model <= 2 || model == 4) 287 return "Athlon"; 288 if (model == 3) 289 return "Duron"; 290 if (model <= 8 || model == 0xa) 291 return "Athlon XP"; 292 } else if (family == 0xf) { 293 if (model <= 4 || model == 7 || model == 8 294 || (model >= 0xb && model <= 0xf) || model == 0x14 295 || model == 0x18 || model == 0x1b || model == 0x1f 296 || model == 0x23 || model == 0x2b 297 || ((model & 0xf) == 0xf && model >= 0x2f && model <= 0x7e)) { 298 return "Athlon 64"; 299 } 300 if (model == 5 || model == 0x15 || model == 0x21 || model == 0x25 301 || model == 0x27) { 302 return "Opteron"; 303 } 304 if (model == 0x1c || model == 0x2c || model == 0x7f) 305 return "Sempron 64"; 306 if (model == 0x24 || model == 0x4c || model == 0x68) 307 return "Turion 64"; 308 } else if (family == 0x1f) { 309 if (model == 2) 310 return "Phenom"; 311 if ((model >= 4 && model <= 6) || model == 0xa) { 312 get_cpuid_model_string(cpuidName); 313 if (strcasestr(cpuidName, "Athlon") != NULL) 314 return "Athlon II"; 315 return "Phenom II"; 316 } 317 } else if (family == 0x3f) 318 return "A-Series"; 319 else if (family == 0x5f) { 320 if (model == 1) 321 return "C-Series"; 322 if (model == 2) 323 return "E-Series"; 324 } else if (family == 0x6f) { 325 if (model == 1 || model == 2) 326 return "FX-Series"; 327 if (model == 0x10 || model == 0x13) 328 return "A-Series"; 329 } 330 331 // Fallback to manual parsing of the model string 332 get_cpuid_model_string(cpuidName); 333 return parse_amd(cpuidName); 334 } 335 336 if (cpuVendor == B_CPU_VENDOR_CYRIX) { 337 if (family == 5 && model == 4) 338 return "GXm"; 339 if (family == 6) 340 return "6x86MX"; 341 return NULL; 342 } 343 344 if (cpuVendor == B_CPU_VENDOR_INTEL) { 345 if (family == 5) { 346 if (model == 1 || model == 2) 347 return "Pentium"; 348 if (model == 3 || model == 9) 349 return "Pentium OD"; 350 if (model == 4 || model == 8) 351 return "Pentium MMX"; 352 } else if (family == 6) { 353 if (model == 1) 354 return "Pentium Pro"; 355 if (model == 3 || model == 5) 356 return "Pentium II"; 357 if (model == 6) 358 return "Celeron"; 359 if (model == 7 || model == 8 || model == 0xa || model == 0xb) 360 return "Pentium III"; 361 if (model == 9 || model == 0xd) { 362 get_cpuid_model_string(cpuidName); 363 if (strcasestr(cpuidName, "Celeron") != NULL) 364 return "Pentium M Celeron"; 365 return "Pentium M"; 366 } 367 if (model == 0x1c || model == 0x26 || model == 0x36) 368 return "Atom"; 369 if (model == 0xe) { 370 get_cpuid_model_string(cpuidName); 371 if (strcasestr(cpuidName, "Celeron") != NULL) 372 return "Core Celeron"; 373 return "Core"; 374 } 375 if (model == 0xf || model == 0x17) { 376 get_cpuid_model_string(cpuidName); 377 if (strcasestr(cpuidName, "Celeron") != NULL) 378 return "Core 2 Celeron"; 379 if (strcasestr(cpuidName, "Xeon") != NULL) 380 return "Core 2 Xeon"; 381 if (strcasestr(cpuidName, "Pentium") != NULL) 382 return "Pentium"; 383 if (strcasestr(cpuidName, "Extreme") != NULL) 384 return "Core 2 Extreme"; 385 return "Core 2"; 386 } 387 if (model == 0x25) { 388 get_cpuid_model_string(cpuidName); 389 if (strcasestr(cpuidName, "i3") != NULL) 390 return "Core i3"; 391 return "Core i5"; 392 } 393 if (model == 0x1a || model == 0x1e) { 394 get_cpuid_model_string(cpuidName); 395 if (strcasestr(cpuidName, "Xeon") != NULL) 396 return "Core i7 Xeon"; 397 return "Core i7"; 398 } 399 } else if (family == 0xf) { 400 if (model <= 4) { 401 get_cpuid_model_string(cpuidName); 402 if (strcasestr(cpuidName, "Celeron") != NULL) 403 return "Pentium 4 Celeron"; 404 if (strcasestr(cpuidName, "Xeon") != NULL) 405 return "Pentium 4 Xeon"; 406 return "Pentium 4"; 407 } 408 } 409 410 // Fallback to manual parsing of the model string 411 get_cpuid_model_string(cpuidName); 412 return parse_intel(cpuidName); 413 } 414 415 if (cpuVendor == B_CPU_VENDOR_NATIONAL_SEMICONDUCTOR) { 416 if (family == 5) { 417 if (model == 4) 418 return "Geode GX1"; 419 if (model == 5) 420 return "Geode GX2"; 421 return NULL; 422 } 423 } 424 425 if (cpuVendor == B_CPU_VENDOR_RISE) { 426 if (family == 5) 427 return "mP6"; 428 return NULL; 429 } 430 431 if (cpuVendor == B_CPU_VENDOR_TRANSMETA) { 432 if (family == 5 && model == 4) 433 return "Crusoe"; 434 if (family == 0xf && (model == 2 || model == 3)) 435 return "Efficeon"; 436 return NULL; 437 } 438 439 if (cpuVendor == B_CPU_VENDOR_VIA) { 440 if (family == 5) { 441 if (model == 4) 442 return "WinChip C6"; 443 if (model == 8) 444 return "WinChip 2"; 445 if (model == 9) 446 return "WinChip 3"; 447 return NULL; 448 } else if (family == 6) { 449 if (model == 6) 450 return "C3 Samuel"; 451 if (model == 7) { 452 if (stepping < 8) 453 return "C3 Eden/Samuel 2"; 454 return "C3 Ezra"; 455 } 456 if (model == 8) 457 return "C3 Ezra-T"; 458 if (model == 9) { 459 if (stepping < 8) 460 return "C3 Nehemiah"; 461 return "C3 Ezra-N"; 462 } 463 if (model == 0xa || model == 0xd) 464 return "C7"; 465 if (model == 0xf) 466 return "Nano"; 467 return NULL; 468 } 469 } 470 471#endif 472 473 return NULL; 474} 475 476 477void 478get_cpu_type(char *vendorBuffer, size_t vendorSize, char *modelBuffer, 479 size_t modelSize) 480{ 481 const char *vendor, *model; 482 483 uint32 topologyNodeCount = 0; 484 cpu_topology_node_info* topology = NULL; 485 get_cpu_topology_info(NULL, &topologyNodeCount); 486 if (topologyNodeCount != 0) 487 topology = (cpu_topology_node_info*)calloc(topologyNodeCount, sizeof(cpu_topology_node_info)); 488 get_cpu_topology_info(topology, &topologyNodeCount); 489 490 enum cpu_platform platform = B_CPU_UNKNOWN; 491 enum cpu_vendor cpuVendor = B_CPU_VENDOR_UNKNOWN; 492 uint32 cpuModel = 0; 493 for (uint32 i = 0; i < topologyNodeCount; i++) { 494 switch (topology[i].type) { 495 case B_TOPOLOGY_ROOT: 496 platform = topology[i].data.root.platform; 497 break; 498 499 case B_TOPOLOGY_PACKAGE: 500 cpuVendor = topology[i].data.package.vendor; 501 break; 502 503 case B_TOPOLOGY_CORE: 504 cpuModel = topology[i].data.core.model; 505 break; 506 507 default: 508 break; 509 } 510 } 511 free(topology); 512 513 vendor = get_cpu_vendor_string(cpuVendor); 514 if (vendor == NULL) 515 vendor = "Unknown"; 516 517 model = get_cpu_model_string(platform, cpuVendor, cpuModel); 518 if (model == NULL) 519 model = "Unknown"; 520 521 strlcpy(vendorBuffer, vendor, vendorSize); 522 strlcpy(modelBuffer, model, modelSize); 523} 524 525 526int32 527get_rounded_cpu_speed(void) 528{ 529 uint32 topologyNodeCount = 0; 530 cpu_topology_node_info* topology = NULL; 531 get_cpu_topology_info(NULL, &topologyNodeCount); 532 if (topologyNodeCount != 0) 533 topology = (cpu_topology_node_info*)calloc(topologyNodeCount, sizeof(cpu_topology_node_info)); 534 get_cpu_topology_info(topology, &topologyNodeCount); 535 536 uint64 cpuFrequency = 0; 537 for (uint32 i = 0; i < topologyNodeCount; i++) { 538 if (topology[i].type == B_TOPOLOGY_CORE) { 539 cpuFrequency = topology[i].data.core.default_frequency; 540 break; 541 } 542 } 543 free(topology); 544 545 int target, frac, delta; 546 int freqs[] = { 100, 50, 25, 75, 33, 67, 20, 40, 60, 80, 10, 30, 70, 90 }; 547 uint x; 548 549 target = cpuFrequency / 1000000; 550 frac = target % 100; 551 delta = -frac; 552 553 for (x = 0; x < sizeof(freqs) / sizeof(freqs[0]); x++) { 554 int ndelta = freqs[x] - frac; 555 if (abs(ndelta) < abs(delta)) 556 delta = ndelta; 557 } 558 return target + delta; 559} 560 561