diff options
-rw-r--r-- | arch/x86_64/boot/compressed/head.S | 19 | ||||
-rw-r--r-- | arch/x86_64/boot/setup.S | 65 | ||||
-rw-r--r-- | arch/x86_64/kernel/acpi/wakeup.S | 30 | ||||
-rw-r--r-- | arch/x86_64/kernel/trampoline.S | 51 | ||||
-rw-r--r-- | arch/x86_64/kernel/verify_cpu.S | 110 |
5 files changed, 152 insertions, 123 deletions
diff --git a/arch/x86_64/boot/compressed/head.S b/arch/x86_64/boot/compressed/head.S index c353a9266ea4..f9d5692a0106 100644 --- a/arch/x86_64/boot/compressed/head.S +++ b/arch/x86_64/boot/compressed/head.S | |||
@@ -54,6 +54,15 @@ startup_32: | |||
54 | 1: popl %ebp | 54 | 1: popl %ebp |
55 | subl $1b, %ebp | 55 | subl $1b, %ebp |
56 | 56 | ||
57 | /* setup a stack and make sure cpu supports long mode. */ | ||
58 | movl $user_stack_end, %eax | ||
59 | addl %ebp, %eax | ||
60 | movl %eax, %esp | ||
61 | |||
62 | call verify_cpu | ||
63 | testl %eax, %eax | ||
64 | jnz no_longmode | ||
65 | |||
57 | /* Compute the delta between where we were compiled to run at | 66 | /* Compute the delta between where we were compiled to run at |
58 | * and where the code will actually run at. | 67 | * and where the code will actually run at. |
59 | */ | 68 | */ |
@@ -159,13 +168,21 @@ startup_32: | |||
159 | /* Jump from 32bit compatibility mode into 64bit mode. */ | 168 | /* Jump from 32bit compatibility mode into 64bit mode. */ |
160 | lret | 169 | lret |
161 | 170 | ||
171 | no_longmode: | ||
172 | /* This isn't an x86-64 CPU so hang */ | ||
173 | 1: | ||
174 | hlt | ||
175 | jmp 1b | ||
176 | |||
177 | #include "../../kernel/verify_cpu.S" | ||
178 | |||
162 | /* Be careful here startup_64 needs to be at a predictable | 179 | /* Be careful here startup_64 needs to be at a predictable |
163 | * address so I can export it in an ELF header. Bootloaders | 180 | * address so I can export it in an ELF header. Bootloaders |
164 | * should look at the ELF header to find this address, as | 181 | * should look at the ELF header to find this address, as |
165 | * it may change in the future. | 182 | * it may change in the future. |
166 | */ | 183 | */ |
167 | .code64 | 184 | .code64 |
168 | .org 0x100 | 185 | .org 0x200 |
169 | ENTRY(startup_64) | 186 | ENTRY(startup_64) |
170 | /* We come here either from startup_32 or directly from a | 187 | /* We come here either from startup_32 or directly from a |
171 | * 64bit bootloader. If we come here from a bootloader we depend on | 188 | * 64bit bootloader. If we come here from a bootloader we depend on |
diff --git a/arch/x86_64/boot/setup.S b/arch/x86_64/boot/setup.S index deb3573c7aec..816d04faa2be 100644 --- a/arch/x86_64/boot/setup.S +++ b/arch/x86_64/boot/setup.S | |||
@@ -299,64 +299,10 @@ loader_ok: | |||
299 | movw %cs,%ax | 299 | movw %cs,%ax |
300 | movw %ax,%ds | 300 | movw %ax,%ds |
301 | 301 | ||
302 | /* minimum CPUID flags for x86-64 */ | 302 | call verify_cpu |
303 | /* see http://www.x86-64.org/lists/discuss/msg02971.html */ | 303 | testl %eax,%eax |
304 | #define SSE_MASK ((1<<25)|(1<<26)) | 304 | jz sse_ok |
305 | #define REQUIRED_MASK1 ((1<<0)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<8)|\ | 305 | |
306 | (1<<13)|(1<<15)|(1<<24)) | ||
307 | #define REQUIRED_MASK2 (1<<29) | ||
308 | |||
309 | pushfl /* standard way to check for cpuid */ | ||
310 | popl %eax | ||
311 | movl %eax,%ebx | ||
312 | xorl $0x200000,%eax | ||
313 | pushl %eax | ||
314 | popfl | ||
315 | pushfl | ||
316 | popl %eax | ||
317 | cmpl %eax,%ebx | ||
318 | jz no_longmode /* cpu has no cpuid */ | ||
319 | movl $0x0,%eax | ||
320 | cpuid | ||
321 | cmpl $0x1,%eax | ||
322 | jb no_longmode /* no cpuid 1 */ | ||
323 | xor %di,%di | ||
324 | cmpl $0x68747541,%ebx /* AuthenticAMD */ | ||
325 | jnz noamd | ||
326 | cmpl $0x69746e65,%edx | ||
327 | jnz noamd | ||
328 | cmpl $0x444d4163,%ecx | ||
329 | jnz noamd | ||
330 | mov $1,%di /* cpu is from AMD */ | ||
331 | noamd: | ||
332 | movl $0x1,%eax | ||
333 | cpuid | ||
334 | andl $REQUIRED_MASK1,%edx | ||
335 | xorl $REQUIRED_MASK1,%edx | ||
336 | jnz no_longmode | ||
337 | movl $0x80000000,%eax | ||
338 | cpuid | ||
339 | cmpl $0x80000001,%eax | ||
340 | jb no_longmode /* no extended cpuid */ | ||
341 | movl $0x80000001,%eax | ||
342 | cpuid | ||
343 | andl $REQUIRED_MASK2,%edx | ||
344 | xorl $REQUIRED_MASK2,%edx | ||
345 | jnz no_longmode | ||
346 | sse_test: | ||
347 | movl $1,%eax | ||
348 | cpuid | ||
349 | andl $SSE_MASK,%edx | ||
350 | cmpl $SSE_MASK,%edx | ||
351 | je sse_ok | ||
352 | test %di,%di | ||
353 | jz no_longmode /* only try to force SSE on AMD */ | ||
354 | movl $0xc0010015,%ecx /* HWCR */ | ||
355 | rdmsr | ||
356 | btr $15,%eax /* enable SSE */ | ||
357 | wrmsr | ||
358 | xor %di,%di /* don't loop */ | ||
359 | jmp sse_test /* try again */ | ||
360 | no_longmode: | 306 | no_longmode: |
361 | call beep | 307 | call beep |
362 | lea long_mode_panic,%si | 308 | lea long_mode_panic,%si |
@@ -366,7 +312,8 @@ no_longmode_loop: | |||
366 | long_mode_panic: | 312 | long_mode_panic: |
367 | .string "Your CPU does not support long mode. Use a 32bit distribution." | 313 | .string "Your CPU does not support long mode. Use a 32bit distribution." |
368 | .byte 0 | 314 | .byte 0 |
369 | 315 | ||
316 | #include "../kernel/verify_cpu.S" | ||
370 | sse_ok: | 317 | sse_ok: |
371 | popw %ds | 318 | popw %ds |
372 | 319 | ||
diff --git a/arch/x86_64/kernel/acpi/wakeup.S b/arch/x86_64/kernel/acpi/wakeup.S index 766cfbcac1db..8550a6ffa275 100644 --- a/arch/x86_64/kernel/acpi/wakeup.S +++ b/arch/x86_64/kernel/acpi/wakeup.S | |||
@@ -43,6 +43,11 @@ wakeup_code: | |||
43 | cmpl $0x12345678, %eax | 43 | cmpl $0x12345678, %eax |
44 | jne bogus_real_magic | 44 | jne bogus_real_magic |
45 | 45 | ||
46 | call verify_cpu # Verify the cpu supports long | ||
47 | # mode | ||
48 | testl %eax, %eax | ||
49 | jnz no_longmode | ||
50 | |||
46 | testl $1, video_flags - wakeup_code | 51 | testl $1, video_flags - wakeup_code |
47 | jz 1f | 52 | jz 1f |
48 | lcall $0xc000,$3 | 53 | lcall $0xc000,$3 |
@@ -92,18 +97,6 @@ wakeup_32: | |||
92 | # Running in this code, but at low address; paging is not yet turned on. | 97 | # Running in this code, but at low address; paging is not yet turned on. |
93 | movb $0xa5, %al ; outb %al, $0x80 | 98 | movb $0xa5, %al ; outb %al, $0x80 |
94 | 99 | ||
95 | /* Check if extended functions are implemented */ | ||
96 | movl $0x80000000, %eax | ||
97 | cpuid | ||
98 | cmpl $0x80000000, %eax | ||
99 | jbe bogus_cpu | ||
100 | wbinvd | ||
101 | mov $0x80000001, %eax | ||
102 | cpuid | ||
103 | btl $29, %edx | ||
104 | jnc bogus_cpu | ||
105 | movl %edx,%edi | ||
106 | |||
107 | movl $__KERNEL_DS, %eax | 100 | movl $__KERNEL_DS, %eax |
108 | movl %eax, %ds | 101 | movl %eax, %ds |
109 | 102 | ||
@@ -123,6 +116,11 @@ wakeup_32: | |||
123 | leal (wakeup_level4_pgt - wakeup_code)(%esi), %eax | 116 | leal (wakeup_level4_pgt - wakeup_code)(%esi), %eax |
124 | movl %eax, %cr3 | 117 | movl %eax, %cr3 |
125 | 118 | ||
119 | /* Check if nx is implemented */ | ||
120 | movl $0x80000001, %eax | ||
121 | cpuid | ||
122 | movl %edx,%edi | ||
123 | |||
126 | /* Enable Long Mode */ | 124 | /* Enable Long Mode */ |
127 | xorl %eax, %eax | 125 | xorl %eax, %eax |
128 | btsl $_EFER_LME, %eax | 126 | btsl $_EFER_LME, %eax |
@@ -244,10 +242,12 @@ bogus_64_magic: | |||
244 | movb $0xb3,%al ; outb %al,$0x80 | 242 | movb $0xb3,%al ; outb %al,$0x80 |
245 | jmp bogus_64_magic | 243 | jmp bogus_64_magic |
246 | 244 | ||
247 | bogus_cpu: | 245 | .code16 |
248 | movb $0xbc,%al ; outb %al,$0x80 | 246 | no_longmode: |
249 | jmp bogus_cpu | 247 | movb $0xbc,%al ; outb %al,$0x80 |
248 | jmp no_longmode | ||
250 | 249 | ||
250 | #include "../verify_cpu.S" | ||
251 | 251 | ||
252 | /* This code uses an extended set of video mode numbers. These include: | 252 | /* This code uses an extended set of video mode numbers. These include: |
253 | * Aliases for standard modes | 253 | * Aliases for standard modes |
diff --git a/arch/x86_64/kernel/trampoline.S b/arch/x86_64/kernel/trampoline.S index 13eee63c7bb5..e7e2764c461b 100644 --- a/arch/x86_64/kernel/trampoline.S +++ b/arch/x86_64/kernel/trampoline.S | |||
@@ -54,6 +54,8 @@ r_base = . | |||
54 | movw $(trampoline_stack_end - r_base), %sp | 54 | movw $(trampoline_stack_end - r_base), %sp |
55 | 55 | ||
56 | call verify_cpu # Verify the cpu supports long mode | 56 | call verify_cpu # Verify the cpu supports long mode |
57 | testl %eax, %eax # Check for return code | ||
58 | jnz no_longmode | ||
57 | 59 | ||
58 | mov %cs, %ax | 60 | mov %cs, %ax |
59 | movzx %ax, %esi # Find the 32bit trampoline location | 61 | movzx %ax, %esi # Find the 32bit trampoline location |
@@ -121,57 +123,10 @@ startup_64: | |||
121 | jmp *%rax | 123 | jmp *%rax |
122 | 124 | ||
123 | .code16 | 125 | .code16 |
124 | verify_cpu: | ||
125 | pushl $0 # Kill any dangerous flags | ||
126 | popfl | ||
127 | |||
128 | /* minimum CPUID flags for x86-64 */ | ||
129 | /* see http://www.x86-64.org/lists/discuss/msg02971.html */ | ||
130 | #define REQUIRED_MASK1 ((1<<0)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<8)|\ | ||
131 | (1<<13)|(1<<15)|(1<<24)|(1<<25)|(1<<26)) | ||
132 | #define REQUIRED_MASK2 (1<<29) | ||
133 | |||
134 | pushfl # check for cpuid | ||
135 | popl %eax | ||
136 | movl %eax, %ebx | ||
137 | xorl $0x200000,%eax | ||
138 | pushl %eax | ||
139 | popfl | ||
140 | pushfl | ||
141 | popl %eax | ||
142 | pushl %ebx | ||
143 | popfl | ||
144 | cmpl %eax, %ebx | ||
145 | jz no_longmode | ||
146 | |||
147 | xorl %eax, %eax # See if cpuid 1 is implemented | ||
148 | cpuid | ||
149 | cmpl $0x1, %eax | ||
150 | jb no_longmode | ||
151 | |||
152 | movl $0x01, %eax # Does the cpu have what it takes? | ||
153 | cpuid | ||
154 | andl $REQUIRED_MASK1, %edx | ||
155 | xorl $REQUIRED_MASK1, %edx | ||
156 | jnz no_longmode | ||
157 | |||
158 | movl $0x80000000, %eax # See if extended cpuid is implemented | ||
159 | cpuid | ||
160 | cmpl $0x80000001, %eax | ||
161 | jb no_longmode | ||
162 | |||
163 | movl $0x80000001, %eax # Does the cpu have what it takes? | ||
164 | cpuid | ||
165 | andl $REQUIRED_MASK2, %edx | ||
166 | xorl $REQUIRED_MASK2, %edx | ||
167 | jnz no_longmode | ||
168 | |||
169 | ret # The cpu supports long mode | ||
170 | |||
171 | no_longmode: | 126 | no_longmode: |
172 | hlt | 127 | hlt |
173 | jmp no_longmode | 128 | jmp no_longmode |
174 | 129 | #include "verify_cpu.S" | |
175 | 130 | ||
176 | # Careful these need to be in the same 64K segment as the above; | 131 | # Careful these need to be in the same 64K segment as the above; |
177 | tidt: | 132 | tidt: |
diff --git a/arch/x86_64/kernel/verify_cpu.S b/arch/x86_64/kernel/verify_cpu.S new file mode 100644 index 000000000000..72edabd2ef9a --- /dev/null +++ b/arch/x86_64/kernel/verify_cpu.S | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * | ||
3 | * verify_cpu.S - Code for cpu long mode and SSE verification. This | ||
4 | * code has been borrowed from boot/setup.S and was introduced by | ||
5 | * Andi Kleen. | ||
6 | * | ||
7 | * Copyright (c) 2007 Andi Kleen (ak@suse.de) | ||
8 | * Copyright (c) 2007 Eric Biederman (ebiederm@xmission.com) | ||
9 | * Copyright (c) 2007 Vivek Goyal (vgoyal@in.ibm.com) | ||
10 | * | ||
11 | * This source code is licensed under the GNU General Public License, | ||
12 | * Version 2. See the file COPYING for more details. | ||
13 | * | ||
14 | * This is a common code for verification whether CPU supports | ||
15 | * long mode and SSE or not. It is not called directly instead this | ||
16 | * file is included at various places and compiled in that context. | ||
17 | * Following are the current usage. | ||
18 | * | ||
19 | * This file is included by both 16bit and 32bit code. | ||
20 | * | ||
21 | * arch/x86_64/boot/setup.S : Boot cpu verification (16bit) | ||
22 | * arch/x86_64/boot/compressed/head.S: Boot cpu verification (32bit) | ||
23 | * arch/x86_64/kernel/trampoline.S: secondary processor verfication (16bit) | ||
24 | * arch/x86_64/kernel/acpi/wakeup.S:Verfication at resume (16bit) | ||
25 | * | ||
26 | * verify_cpu, returns the status of cpu check in register %eax. | ||
27 | * 0: Success 1: Failure | ||
28 | * | ||
29 | * The caller needs to check for the error code and take the action | ||
30 | * appropriately. Either display a message or halt. | ||
31 | */ | ||
32 | |||
33 | verify_cpu: | ||
34 | |||
35 | pushfl # Save caller passed flags | ||
36 | pushl $0 # Kill any dangerous flags | ||
37 | popfl | ||
38 | |||
39 | /* minimum CPUID flags for x86-64 */ | ||
40 | /* see http://www.x86-64.org/lists/discuss/msg02971.html */ | ||
41 | #define SSE_MASK ((1<<25)|(1<<26)) | ||
42 | #define REQUIRED_MASK1 ((1<<0)|(1<<3)|(1<<4)|(1<<5)|(1<<6)|(1<<8)|\ | ||
43 | (1<<13)|(1<<15)|(1<<24)) | ||
44 | #define REQUIRED_MASK2 (1<<29) | ||
45 | pushfl # standard way to check for cpuid | ||
46 | popl %eax | ||
47 | movl %eax,%ebx | ||
48 | xorl $0x200000,%eax | ||
49 | pushl %eax | ||
50 | popfl | ||
51 | pushfl | ||
52 | popl %eax | ||
53 | cmpl %eax,%ebx | ||
54 | jz verify_cpu_no_longmode # cpu has no cpuid | ||
55 | |||
56 | movl $0x0,%eax # See if cpuid 1 is implemented | ||
57 | cpuid | ||
58 | cmpl $0x1,%eax | ||
59 | jb verify_cpu_no_longmode # no cpuid 1 | ||
60 | |||
61 | xor %di,%di | ||
62 | cmpl $0x68747541,%ebx # AuthenticAMD | ||
63 | jnz verify_cpu_noamd | ||
64 | cmpl $0x69746e65,%edx | ||
65 | jnz verify_cpu_noamd | ||
66 | cmpl $0x444d4163,%ecx | ||
67 | jnz verify_cpu_noamd | ||
68 | mov $1,%di # cpu is from AMD | ||
69 | |||
70 | verify_cpu_noamd: | ||
71 | movl $0x1,%eax # Does the cpu have what it takes | ||
72 | cpuid | ||
73 | andl $REQUIRED_MASK1,%edx | ||
74 | xorl $REQUIRED_MASK1,%edx | ||
75 | jnz verify_cpu_no_longmode | ||
76 | |||
77 | movl $0x80000000,%eax # See if extended cpuid is implemented | ||
78 | cpuid | ||
79 | cmpl $0x80000001,%eax | ||
80 | jb verify_cpu_no_longmode # no extended cpuid | ||
81 | |||
82 | movl $0x80000001,%eax # Does the cpu have what it takes | ||
83 | cpuid | ||
84 | andl $REQUIRED_MASK2,%edx | ||
85 | xorl $REQUIRED_MASK2,%edx | ||
86 | jnz verify_cpu_no_longmode | ||
87 | |||
88 | verify_cpu_sse_test: | ||
89 | movl $1,%eax | ||
90 | cpuid | ||
91 | andl $SSE_MASK,%edx | ||
92 | cmpl $SSE_MASK,%edx | ||
93 | je verify_cpu_sse_ok | ||
94 | test %di,%di | ||
95 | jz verify_cpu_no_longmode # only try to force SSE on AMD | ||
96 | movl $0xc0010015,%ecx # HWCR | ||
97 | rdmsr | ||
98 | btr $15,%eax # enable SSE | ||
99 | wrmsr | ||
100 | xor %di,%di # don't loop | ||
101 | jmp verify_cpu_sse_test # try again | ||
102 | |||
103 | verify_cpu_no_longmode: | ||
104 | popfl # Restore caller passed flags | ||
105 | movl $1,%eax | ||
106 | ret | ||
107 | verify_cpu_sse_ok: | ||
108 | popfl # Restore caller passed flags | ||
109 | xorl %eax, %eax | ||
110 | ret | ||