xref: /haiku/headers/private/shared/cpu_type.h (revision 2710b4f5d4251c5cf88c82b0114ea99b0ef46d22)
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