aboutsummaryrefslogtreecommitdiffstats
path: root/arch/i386
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2007-07-11 15:18:47 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-12 13:55:55 -0400
commit31b54f40e12e4d04941762be6615edaf3c6ed811 (patch)
tree137c160c216f35a589b4c2fabe255a14a1343d91 /arch/i386
parent0008ea39bd03ee1f29e361e6f6e1b8a6289e5234 (diff)
CPU features verification for the new x86 setup code
Verify that the CPU has enough features to run the kernel. This may entail enabling features on some CPUs. By doing this in the setup code we can be guaranteed to still be able to write to the console through the BIOS. Signed-off-by: H. Peter Anvin <hpa@zytor.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/i386')
-rw-r--r--arch/i386/boot/cpu.c69
-rw-r--r--arch/i386/boot/cpucheck.c267
2 files changed, 336 insertions, 0 deletions
diff --git a/arch/i386/boot/cpu.c b/arch/i386/boot/cpu.c
new file mode 100644
index 000000000000..2a5c32da5852
--- /dev/null
+++ b/arch/i386/boot/cpu.c
@@ -0,0 +1,69 @@
1/* -*- linux-c -*- ------------------------------------------------------- *
2 *
3 * Copyright (C) 1991, 1992 Linus Torvalds
4 * Copyright 2007 rPath, Inc. - All Rights Reserved
5 *
6 * This file is part of the Linux kernel, and is made available under
7 * the terms of the GNU General Public License version 2.
8 *
9 * ----------------------------------------------------------------------- */
10
11/*
12 * arch/i386/boot/cpu.c
13 *
14 * Check for obligatory CPU features and abort if the features are not
15 * present.
16 */
17
18#include "boot.h"
19#include "bitops.h"
20#include <asm/cpufeature.h>
21
22static char *cpu_name(int level)
23{
24 static char buf[6];
25
26 if (level == 64) {
27 return "x86-64";
28 } else {
29 sprintf(buf, "i%d86", level);
30 return buf;
31 }
32}
33
34int validate_cpu(void)
35{
36 u32 *err_flags;
37 int cpu_level, req_level;
38
39 check_cpu(&cpu_level, &req_level, &err_flags);
40
41 if (cpu_level < req_level) {
42 printf("This kernel requires an %s CPU, ",
43 cpu_name(req_level));
44 printf("but only detected an %s CPU.\n",
45 cpu_name(cpu_level));
46 return -1;
47 }
48
49 if (err_flags) {
50 int i, j;
51 puts("This kernel requires the following features "
52 "not present on the CPU:\n");
53
54 for (i = 0; i < NCAPINTS; i++) {
55 u32 e = err_flags[i];
56
57 for (j = 0; j < 32; j++) {
58 if (e & 1)
59 printf("%d:%d ", i, j);
60
61 e >>= 1;
62 }
63 }
64 putchar('\n');
65 return -1;
66 } else {
67 return 0;
68 }
69}
diff --git a/arch/i386/boot/cpucheck.c b/arch/i386/boot/cpucheck.c
new file mode 100644
index 000000000000..8b0f4473b083
--- /dev/null
+++ b/arch/i386/boot/cpucheck.c
@@ -0,0 +1,267 @@
1/* -*- linux-c -*- ------------------------------------------------------- *
2 *
3 * Copyright (C) 1991, 1992 Linus Torvalds
4 * Copyright 2007 rPath, Inc. - All Rights Reserved
5 *
6 * This file is part of the Linux kernel, and is made available under
7 * the terms of the GNU General Public License version 2.
8 *
9 * ----------------------------------------------------------------------- */
10
11/*
12 * arch/i386/boot/cpucheck.c
13 *
14 * Check for obligatory CPU features and abort if the features are not
15 * present. This code should be compilable as 16-, 32- or 64-bit
16 * code, so be very careful with types and inline assembly.
17 *
18 * This code should not contain any messages; that requires an
19 * additional wrapper.
20 *
21 * As written, this code is not safe for inclusion into the kernel
22 * proper (after FPU initialization, in particular).
23 */
24
25#ifdef _SETUP
26# include "boot.h"
27# include "bitops.h"
28#endif
29#include <linux/types.h>
30#include <asm/cpufeature.h>
31#include <asm/processor-flags.h>
32#include <asm/required-features.h>
33#include <asm/msr-index.h>
34
35struct cpu_features {
36 int level; /* Family, or 64 for x86-64 */
37 int model;
38 u32 flags[NCAPINTS];
39};
40
41static struct cpu_features cpu;
42static u32 cpu_vendor[3];
43static u32 err_flags[NCAPINTS];
44
45#ifdef CONFIG_X86_64
46static const int req_level = 64;
47#elif defined(CONFIG_X86_MINIMUM_CPU_FAMILY)
48static const int req_level = CONFIG_X86_MINIMUM_CPU_FAMILY;
49#else
50static const int req_level = 3;
51#endif
52
53static const u32 req_flags[NCAPINTS] =
54{
55 REQUIRED_MASK0,
56 REQUIRED_MASK1,
57 REQUIRED_MASK2,
58 REQUIRED_MASK3,
59 REQUIRED_MASK4,
60 REQUIRED_MASK5,
61 REQUIRED_MASK6,
62 REQUIRED_MASK7,
63};
64
65#define A32(a,b,c,d) (((d) << 24)+((c) << 16)+((b) << 8)+(a))
66
67static int is_amd(void)
68{
69 return cpu_vendor[0] == A32('A','u','t','h') &&
70 cpu_vendor[1] == A32('e','n','t','i') &&
71 cpu_vendor[2] == A32('c','A','M','D');
72}
73
74static int is_centaur(void)
75{
76 return cpu_vendor[0] == A32('C','e','n','t') &&
77 cpu_vendor[1] == A32('a','u','r','H') &&
78 cpu_vendor[2] == A32('a','u','l','s');
79}
80
81static int is_transmeta(void)
82{
83 return cpu_vendor[0] == A32('G','e','n','u') &&
84 cpu_vendor[1] == A32('i','n','e','T') &&
85 cpu_vendor[2] == A32('M','x','8','6');
86}
87
88static int has_fpu(void)
89{
90 u16 fcw = -1, fsw = -1;
91 u32 cr0;
92
93 asm("movl %%cr0,%0" : "=r" (cr0));
94 if (cr0 & (X86_CR0_EM|X86_CR0_TS)) {
95 cr0 &= ~(X86_CR0_EM|X86_CR0_TS);
96 asm volatile("movl %0,%%cr0" : : "r" (cr0));
97 }
98
99 asm("fninit ; fnstsw %0 ; fnstcw %1" : "+m" (fsw), "+m" (fcw));
100
101 return fsw == 0 && (fcw & 0x103f) == 0x003f;
102}
103
104static int has_eflag(u32 mask)
105{
106 u32 f0, f1;
107
108 asm("pushfl ; "
109 "pushfl ; "
110 "popl %0 ; "
111 "movl %0,%1 ; "
112 "xorl %2,%1 ; "
113 "pushl %1 ; "
114 "popfl ; "
115 "pushfl ; "
116 "popl %1 ; "
117 "popfl"
118 : "=r" (f0), "=r" (f1)
119 : "g" (mask));
120
121 return !!((f0^f1) & mask);
122}
123
124static void get_flags(void)
125{
126 u32 max_intel_level, max_amd_level;
127 u32 tfms;
128
129 if (has_fpu())
130 set_bit(X86_FEATURE_FPU, cpu.flags);
131
132 if (has_eflag(X86_EFLAGS_ID)) {
133 asm("cpuid"
134 : "=a" (max_intel_level),
135 "=b" (cpu_vendor[0]),
136 "=d" (cpu_vendor[1]),
137 "=c" (cpu_vendor[2])
138 : "a" (0));
139
140 if (max_intel_level >= 0x00000001 &&
141 max_intel_level <= 0x0000ffff) {
142 asm("cpuid"
143 : "=a" (tfms),
144 "=c" (cpu.flags[4]),
145 "=d" (cpu.flags[0])
146 : "a" (0x00000001)
147 : "ebx");
148 cpu.level = (tfms >> 8) & 15;
149 cpu.model = (tfms >> 4) & 15;
150 if (cpu.level >= 6)
151 cpu.model += ((tfms >> 16) & 0xf) << 4;
152 }
153
154 asm("cpuid"
155 : "=a" (max_amd_level)
156 : "a" (0x80000000)
157 : "ebx", "ecx", "edx");
158
159 if (max_amd_level >= 0x80000001 &&
160 max_amd_level <= 0x8000ffff) {
161 u32 eax = 0x80000001;
162 asm("cpuid"
163 : "+a" (eax),
164 "=c" (cpu.flags[6]),
165 "=d" (cpu.flags[1])
166 : : "ebx");
167 }
168 }
169}
170
171/* Returns a bitmask of which words we have error bits in */
172static int check_flags(void)
173{
174 u32 err;
175 int i;
176
177 err = 0;
178 for (i = 0; i < NCAPINTS; i++) {
179 err_flags[i] = req_flags[i] & ~cpu.flags[i];
180 if (err_flags[i])
181 err |= 1 << i;
182 }
183
184 return err;
185}
186
187/*
188 * Returns -1 on error.
189 *
190 * *cpu_level is set to the current CPU level; *req_level to the required
191 * level. x86-64 is considered level 64 for this purpose.
192 *
193 * *err_flags_ptr is set to the flags error array if there are flags missing.
194 */
195int check_cpu(int *cpu_level_ptr, int *req_level_ptr, u32 **err_flags_ptr)
196{
197 int err;
198
199 memset(&cpu.flags, 0, sizeof cpu.flags);
200 cpu.level = 3;
201
202 if (has_eflag(X86_EFLAGS_AC))
203 cpu.level = 4;
204
205 get_flags();
206 err = check_flags();
207
208 if (test_bit(X86_FEATURE_LM, cpu.flags))
209 cpu.level = 64;
210
211 if (err == 0x01 &&
212 !(err_flags[0] &
213 ~((1 << X86_FEATURE_XMM)|(1 << X86_FEATURE_XMM2))) &&
214 is_amd()) {
215 /* If this is an AMD and we're only missing SSE+SSE2, try to
216 turn them on */
217
218 u32 ecx = MSR_K7_HWCR;
219 u32 eax, edx;
220
221 asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx));
222 eax &= ~(1 << 15);
223 asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx));
224
225 get_flags(); /* Make sure it really did something */
226 err = check_flags();
227 } else if (err == 0x01 &&
228 !(err_flags[0] & ~(1 << X86_FEATURE_CX8)) &&
229 is_centaur() && cpu.model >= 6) {
230 /* If this is a VIA C3, we might have to enable CX8
231 explicitly */
232
233 u32 ecx = MSR_VIA_FCR;
234 u32 eax, edx;
235
236 asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx));
237 eax |= (1<<1)|(1<<7);
238 asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx));
239
240 set_bit(X86_FEATURE_CX8, cpu.flags);
241 err = check_flags();
242 } else if (err == 0x01 && is_transmeta()) {
243 /* Transmeta might have masked feature bits in word 0 */
244
245 u32 ecx = 0x80860004;
246 u32 eax, edx;
247 u32 level = 1;
248
249 asm("rdmsr" : "=a" (eax), "=d" (edx) : "c" (ecx));
250 asm("wrmsr" : : "a" (~0), "d" (edx), "c" (ecx));
251 asm("cpuid"
252 : "+a" (level), "=d" (cpu.flags[0])
253 : : "ecx", "ebx");
254 asm("wrmsr" : : "a" (eax), "d" (edx), "c" (ecx));
255
256 err = check_flags();
257 }
258
259 if (err_flags_ptr)
260 *err_flags_ptr = err ? err_flags : NULL;
261 if (cpu_level_ptr)
262 *cpu_level_ptr = cpu.level;
263 if (req_level_ptr)
264 *req_level_ptr = req_level;
265
266 return (cpu.level < req_level || err) ? -1 : 0;
267}