hc
2025-02-14 bbb9540dc49f70f6b703d1c8d1b85fa5f602d86e
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/* SPDX-License-Identifier: GPL-2.0-only */
/*
 *
 *    verify_cpu.S - Code for cpu long mode and SSE verification. This
 *    code has been borrowed from boot/setup.S and was introduced by
 *     Andi Kleen.
 *
 *    Copyright (c) 2007  Andi Kleen (ak@suse.de)
 *    Copyright (c) 2007  Eric Biederman (ebiederm@xmission.com)
 *    Copyright (c) 2007  Vivek Goyal (vgoyal@in.ibm.com)
 *    Copyright (c) 2010  Kees Cook (kees.cook@canonical.com)
 *
 *    This is a common code for verification whether CPU supports
 *     long mode and SSE or not. It is not called directly instead this
 *    file is included at various places and compiled in that context.
 *    This file is expected to run in 32bit code.  Currently:
 *
 *    arch/x86/boot/compressed/head_64.S: Boot cpu verification
 *    arch/x86/kernel/trampoline_64.S: secondary processor verification
 *    arch/x86/kernel/head_32.S: processor startup
 *
 *    verify_cpu, returns the status of longmode and SSE in register %eax.
 *        0: Success    1: Failure
 *
 *    On Intel, the XD_DISABLE flag will be cleared as a side-effect.
 *
 *     The caller needs to check for the error code and take the action
 *     appropriately. Either display a message or halt.
 */
 
#include <asm/cpufeatures.h>
#include <asm/msr-index.h>
 
SYM_FUNC_START_LOCAL(verify_cpu)
   pushf                # Save caller passed flags
   push    $0            # Kill any dangerous flags
   popf
 
#ifndef __x86_64__
   pushfl                # standard way to check for cpuid
   popl    %eax
   movl    %eax,%ebx
   xorl    $0x200000,%eax
   pushl    %eax
   popfl
   pushfl
   popl    %eax
   cmpl    %eax,%ebx
   jz    .Lverify_cpu_no_longmode    # cpu has no cpuid
#endif
 
   movl    $0x0,%eax        # See if cpuid 1 is implemented
   cpuid
   cmpl    $0x1,%eax
   jb    .Lverify_cpu_no_longmode    # no cpuid 1
 
   xor    %di,%di
   cmpl    $0x68747541,%ebx    # AuthenticAMD
   jnz    .Lverify_cpu_noamd
   cmpl    $0x69746e65,%edx
   jnz    .Lverify_cpu_noamd
   cmpl    $0x444d4163,%ecx
   jnz    .Lverify_cpu_noamd
   mov    $1,%di            # cpu is from AMD
   jmp    .Lverify_cpu_check
 
.Lverify_cpu_noamd:
   cmpl    $0x756e6547,%ebx        # GenuineIntel?
   jnz    .Lverify_cpu_check
   cmpl    $0x49656e69,%edx
   jnz    .Lverify_cpu_check
   cmpl    $0x6c65746e,%ecx
   jnz    .Lverify_cpu_check
 
   # only call IA32_MISC_ENABLE when:
   # family > 6 || (family == 6 && model >= 0xd)
   movl    $0x1, %eax        # check CPU family and model
   cpuid
   movl    %eax, %ecx
 
   andl    $0x0ff00f00, %eax    # mask family and extended family
   shrl    $8, %eax
   cmpl    $6, %eax
   ja    .Lverify_cpu_clear_xd    # family > 6, ok
   jb    .Lverify_cpu_check    # family < 6, skip
 
   andl    $0x000f00f0, %ecx    # mask model and extended model
   shrl    $4, %ecx
   cmpl    $0xd, %ecx
   jb    .Lverify_cpu_check    # family == 6, model < 0xd, skip
 
.Lverify_cpu_clear_xd:
   movl    $MSR_IA32_MISC_ENABLE, %ecx
   rdmsr
   btrl    $2, %edx        # clear MSR_IA32_MISC_ENABLE_XD_DISABLE
   jnc    .Lverify_cpu_check    # only write MSR if bit was changed
   wrmsr
 
.Lverify_cpu_check:
   movl    $0x1,%eax        # Does the cpu have what it takes
   cpuid
   andl    $REQUIRED_MASK0,%edx
   xorl    $REQUIRED_MASK0,%edx
   jnz    .Lverify_cpu_no_longmode
 
   movl    $0x80000000,%eax    # See if extended cpuid is implemented
   cpuid
   cmpl    $0x80000001,%eax
   jb      .Lverify_cpu_no_longmode    # no extended cpuid
 
   movl    $0x80000001,%eax    # Does the cpu have what it takes
   cpuid
   andl    $REQUIRED_MASK1,%edx
   xorl    $REQUIRED_MASK1,%edx
   jnz     .Lverify_cpu_no_longmode
 
.Lverify_cpu_sse_test:
   movl    $1,%eax
   cpuid
   andl    $SSE_MASK,%edx
   cmpl    $SSE_MASK,%edx
   je    .Lverify_cpu_sse_ok
   test    %di,%di
   jz    .Lverify_cpu_no_longmode    # only try to force SSE on AMD
   movl    $MSR_K7_HWCR,%ecx
   rdmsr
   btr    $15,%eax        # enable SSE
   wrmsr
   xor    %di,%di            # don't loop
   jmp    .Lverify_cpu_sse_test    # try again
 
.Lverify_cpu_no_longmode:
   popf                # Restore caller passed flags
   movl $1,%eax
   RET
.Lverify_cpu_sse_ok:
   popf                # Restore caller passed flags
   xorl %eax, %eax
   RET
SYM_FUNC_END(verify_cpu)