summaryrefslogtreecommitdiffstats
path: root/arch/x86/kernel/verify_cpu.S
blob: 641f0fe1e5b4abbc612967c1028c9421e0966294 (plain)
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)