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
19 extern "C" {
20 #endif
21
22 static const char* get_cpu_vendor_string(enum cpu_vendor cpuVendor);
23 static const char* get_cpu_model_string(enum cpu_platform platform,
24 enum cpu_vendor cpuVendor, uint32 cpuModel);
25 void get_cpu_type(char *vendorBuffer, size_t vendorSize,
26 char *modelBuffer, size_t modelSize);
27 int32 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 */
39 static const char*
parse_intel(const char * name)40 parse_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
87 static const char*
parse_amd(const char * name)88 parse_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
172 static const char*
get_cpu_vendor_string(enum cpu_vendor cpuVendor)173 get_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. */
189 void
get_cpuid_model_string(char * name)190 get_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
245 static const char*
get_cpu_model_string(enum cpu_platform platform,enum cpu_vendor cpuVendor,uint32 cpuModel)246 get_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
477 void
get_cpu_type(char * vendorBuffer,size_t vendorSize,char * modelBuffer,size_t modelSize)478 get_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
526 int32
get_rounded_cpu_speed(void)527 get_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