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