xref: /openbsd/sys/arch/i386/stand/libsa/cpuprobe.c (revision 76d0caae)
1 /*	$OpenBSD: cpuprobe.c,v 1.2 2014/03/29 18:09:29 guenther Exp $	*/
2 
3 /*
4  * Copyright (c) 2004 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <machine/psl.h>
20 #include <machine/specialreg.h>
21 
22 #include "libsa.h"
23 
24 int amd64_supported;
25 static int cpu_family, cpu_model, cpu_stepping;
26 static int psl_check;
27 static u_int32_t feature_ecx, feature_edx, feature_amd;
28 static char cpu_brandstr[48];		/* Includes term NUL byte */
29 static char cpu_vendor[13];		/* 12 chars plus NUL term */
30 
31 /*
32  * cpuid instruction.  request in eax, result in eax, ebx, ecx, edx.
33  * requires caller to provide u_int32_t regs[4] array.
34  */
35 u_int32_t
36 cpuid(u_int32_t eax, u_int32_t *regs)
37 {
38 	__asm volatile(
39 	    "cpuid\n\t"
40 	    "movl	%%eax, 0(%2)\n\t"
41 	    "movl	%%ebx, 4(%2)\n\t"
42 	    "movl	%%ecx, 8(%2)\n\t"
43 	    "movl	%%edx, 12(%2)\n\t"
44 	    : "=a" (eax)
45 	    : "0" (eax), "S" (regs)
46 	    : "bx", "cx", "dx");
47 
48 	return eax;
49 }
50 
51 void
52 cpuprobe(void)
53 {
54 	u_int32_t cpuid_max, extended_max;
55 	u_int32_t regs[4];
56 
57 	/*
58 	 * The following is a simple check to see if cpuid is supported.
59 	 * We try to toggle bit 21 (PSL_ID) in eflags.  If it works, then
60 	 * cpuid is supported.  If not, there's no cpuid, and we don't
61 	 * try it (don't want /boot to get an invalid opcode exception).
62 	 *
63 	 * XXX The NexGen Nx586 does not support this bit, so this is not
64 	 *     a good method to detect the presence of cpuid on this
65 	 *     processor.  That's fine: the purpose here is to detect the
66 	 *     absence of cpuid.  We don't mind if the instruction's not
67 	 *     there - this is not intended to determine exactly what
68 	 *     processor is there, just whether it's i386 or amd64.
69 	 *
70 	 *     The only thing that would cause us grief is a processor which
71 	 *     does not support cpuid but which does allow the PSL_ID bit
72 	 *     in eflags to be toggled.
73 	 */
74 	__asm volatile(
75 	    "pushfl\n\t"
76 	    "popl	%2\n\t"
77 	    "xorl	%2, %0\n\t"
78 	    "pushl	%0\n\t"
79 	    "popfl\n\t"
80 	    "pushfl\n\t"
81 	    "popl	%0\n\t"
82 	    "xorl	%2, %0\n\t"		/* If %2 == %0, no cpuid */
83 	    : "=r" (psl_check)
84 	    : "0" (PSL_ID), "r" (0)
85 	    : "cc");
86 
87 	if (psl_check == PSL_ID) {			/* cpuid supported */
88 		cpuid_max = cpuid(0, regs);		/* Highest std call */
89 
90 		bcopy(&regs[1], cpu_vendor, sizeof(regs[1]));
91 		bcopy(&regs[3], cpu_vendor + 4, sizeof(regs[3]));
92 		bcopy(&regs[2], cpu_vendor + 8, sizeof(regs[2]));
93 		cpu_vendor[sizeof(cpu_vendor) - 1] = '\0';
94 
95 		if (cpuid_max >= 1) {
96 			u_int32_t id;
97 
98 			id = cpuid(1, regs);		/* Get basic info */
99 			cpu_stepping = id & 0x000000f;
100 			cpu_model = (id >> 4) & 0x0000000f;
101 			cpu_family = (id >> 8) & 0x0000000f;
102 
103 			feature_ecx = regs[2];
104 			feature_edx = regs[3];
105 		}
106 
107 		extended_max = cpuid(0x80000000, regs);	/* Highest ext  */
108 
109 		if (extended_max >= 0x80000001) {
110 			cpuid(0x80000001, regs);
111 			feature_amd = regs[3];
112 			if (feature_amd & CPUID_LONG)
113 				amd64_supported = 1;
114 		}
115 
116 		cpu_brandstr[0] = '\0';
117 		if (extended_max >= 0x80000004) {
118 			u_int32_t brand_ints[12];
119 
120 			cpuid(0x80000002, brand_ints);
121 			cpuid(0x80000003, brand_ints + 4);
122 			cpuid(0x80000004, brand_ints + 8);
123 
124 			bcopy(brand_ints, cpu_brandstr,
125 			    sizeof(cpu_brandstr) - 1);
126 
127 			cpu_brandstr[sizeof(cpu_brandstr) - 1] = '\0';
128 		}
129 	}
130 
131 	printf("%s", amd64_supported ? " amd64" : " i386");
132 }
133 
134 void
135 dump_cpuinfo(void)
136 {
137 	printf("\"%s\", family %d, model %d, step %d\n",
138 	    cpu_vendor, cpu_family, cpu_model, cpu_stepping);
139 
140 	if (*cpu_brandstr)
141 		printf("%s\n", cpu_brandstr);
142 
143 	printf("features: ecx 0x%x, edx 0x%x, amd 0x%x\n",
144 	    feature_ecx, feature_edx, feature_amd);
145 
146 	printf("psl_check: 0x%x\n", psl_check);
147 }
148