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