summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/i386/cpuModel.S
blob: 8245fd86a2c497164cae878c66d00c856bd0e488 (plain) (blame)
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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*  cpuModel.S
 *
 *  This file contains all assembly code for the Intel Cpu identification.
 *  It is based on linux cpu detection code.
 *
 *  Intel also provides public similar code in the book
 *  called :
 *
 *	Pentium Processor Family
 *		Developer Family
 *	Volume  3 :	Architecture and Programming Manual
 *
 * At the following place :
 *
 *	Chapter 5 :	Feature determination
 *	Chapter 25:	CPUID instruction
 *
 *  COPYRIGHT (c) 1998 valette@crf.canon.fr
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.com/license/LICENSE.
 *
 *  $Id$
 */

#include <rtems/asm.h>
#include <rtems/score/registers.h>

BEGIN_CODE
	PUBLIC(checkCPUtypeSetCr0);
/*
 * check Processor type: 386, 486, 6x86(L) or CPUID capable processor
 */

SYM (checkCPUtypeSetCr0):
	/*
	 *  Assume 386 for now
	 */
	movl $3, SYM (x86)
	/*
	 * Start using the EFLAGS AC bit determination method described in
	 * the book mentioned above page 5.1. If this bit can be set we
	 * have a 486 or above.
	 */
	pushfl				/* save EFLAGS			*/

	pushfl				/* Get EFLAGS in EAX		*/
	popl eax

	movl eax,ecx			/* save original EFLAGS in ECX	*/
	xorl $EFLAGS_ALIGN_CHECK,eax	/* flip AC bit in EAX		*/
	pushl eax			/* set EAX as EFLAGS		*/
	popfl
	pushfl				/* Get new EFLAGS in EAX	*/
	popl eax

	xorl ecx,eax			/* check if AC bit changed	*/
	andl $EFLAGS_ALIGN_CHECK,eax
	je is386			/* If not : we have a 386	*/
	/*
	 *  Assume 486 for now
	 */
	movl $4,SYM (x86)
	movl ecx,eax			/* Restore orig EFLAGS in EAX	*/
	xorl $EFLAGS_ID,eax		/* flip  ID flag		*/
	pushl eax			/* set EAX as EFLAGS		*/
	popfl
	pushfl				/* Get new EFLAGS in EAX	*/
	popl eax

	xorl ecx,eax			/* check if ID bit changed	*/
	andl $EFLAGS_ID,eax

	/*
	 * if we are on a straight 486DX,
	 * SX, or 487SX we can't change it
	 * OTOH 6x86MXs and MIIs check OK
	 * Also if we are on a Cyrix 6x86(L)
	 */
	je is486x

isnew:
	/*
	 * restore original EFLAGS
	 */
	popfl
	incl SYM(have_cpuid)	/* we have CPUID instruction */

	/*
	 * Addressable Processor Ids
	 *
	 * CPUID.(EAX=4, ECX=0):EAX[31:26] + 1 = Y)
	 */
	movl $4, eax
	movl $0, ecx
	cpuid
	movl eax,SYM(x86_capability_cores)

	/* use it to get :
	 *	processor type,
	 *	processor model,
	 *	processor mask,
	 * by using it with EAX = 1
	 */
	movl $1, eax
	cpuid
	movl ebx,SYM(x86_capability_ebx) /* store ebx feature info */
	movl ecx,SYM(x86_capability_x) /* store ecx feature flags */

	movb al, cl		/* save reg for future use */

	andb $0x0f,ah		/* mask processor family   */
	movb ah,SYM (x86)	/* put result in x86 var   */

	andb $0xf0, al		/* get model		   */
	shrb $4, al
	movb al,SYM (x86_model) /* store it in x86_model   */

	andb $0x0f, cl		/* get mask revision	   */
	movb cl,SYM (x86_mask)  /* store it in x86_mask	   */

	movl edx,SYM(x86_capability)	/* store feature flags in x86_capability */

	/* get vendor info by using CPUID with EXA = 0 */
	xorl eax, eax
	cpuid

	/*
	 * store results contained in ebx, edx, ecx in
	 * x86_vendor_id variable.
	 */
	movl ebx,SYM(x86_vendor_id)
	movl edx,SYM(x86_vendor_id)+4
	movl ecx,SYM(x86_vendor_id)+8

	movl cr0,eax		/* 486+ */
	andl $(CR0_PAGING | CR0_PROTECTION_ENABLE | CR0_EXTENSION_TYPE), eax
	orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax
	jmp 2f

/* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid
 * clobbering the new BX chipset used with the Pentium II, which has a register
 * at the same addresses as those used to access the Cyrix special configuration
 * registers (CCRs).
 */
	/*
	 * A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2
	 * (and it _must_ be 5 divided by 2) while other CPUs change
	 * them in undefined ways. We need to know this since we may
	 * need to enable the CPUID instruction at least.
	 * We couldn't use this test before since the PPro and PII behave
	 * like Cyrix chips in this respect.
	 */
is486x:	xor ax,ax
	sahf
	movb $5,al
	movb $2,bl
	div bl
	lahf
	cmpb $2,ah
	jne ncyrix
	/*
	 * N.B. The pattern of accesses to 0x22 and 0x23 is *essential*
	 *      so do not try to "optimize" it! For the same reason we
	 *	do all this with interrupts off.
	 */
#define setCx86(reg, val) \
	movb reg,al;	\
	outb al,$0x22;	\
	movb val,al;	\
	outb al,$0x23

#define getCx86(reg) \
	movb reg,al;	\
	outb al,$0x22;	\
	inb $0x23,al

	cli
	getCx86($0xc3)		/*  get CCR3 */
	movb al,cl		/* Save old value */
	movb al,bl
	andb $0x0f,bl		/* Enable access to all config registers */
	orb $0x10,bl		/* by setting bit 4 */
	setCx86($0xc3,bl)

	getCx86($0xe8)		/* now we can get CCR4 */
	orb $0x80,al		/* and set bit 7 (CPUIDEN) */
	movb al,bl		/* to enable CPUID execution */
	setCx86($0xe8,bl)

        getCx86($0xfe)          /* DIR0 : let's check this is a 6x86(L) */
        andb $0xf0,al		/* should be 3xh */
	cmpb $0x30,al
	jne n6x86
        getCx86($0xe9)          /* CCR5 : we reset the SLOP bit */
        andb $0xfd,al		/* so that udelay calculation */
        movb al,bl		/* is correct on 6x86(L) CPUs */
        setCx86($0xe9,bl)
	setCx86($0xc3,cl)	/* Restore old CCR3 */
	sti
	jmp isnew		/* We enabled CPUID now */

n6x86:	setCx86($0xc3,cl)	/* Restore old CCR3 */
	sti
ncyrix:				/* restore original EFLAGS */
	popfl
	movl cr0,eax		/* 486 */
	andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax	/* Save PG,PE,ET */
	orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax	/* set AM, WP, NE and MP */
	jmp 2f
is386:				/* restore original EFLAGS */
	popfl
	movl cr0,eax		/* 386 */
	andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax	/* Save PG,PE,ET */
	orl $CR0_MONITOR_COPROC,eax		/* set MP */
2:	movl eax,cr0
	call check_x87
	ret


/*
 * We depend on ET to be correct. This checks for 287/387.
 */
check_x87:
	movb $0,SYM(hard_math)
	clts
	fninit
	fstsw ax
	cmpb $0,al
	je 1f
	movl cr0,eax		/* no coprocessor: have to set bits */
	xorl $4,eax		/* set EM */
	movl eax,cr0
	ret
	.align 16
1:	movb $1,SYM(hard_math)
	.byte 0xDB,0xE4		/* fsetpm for 287, ignored by 387 */
	ret

END_CODE

BEGIN_DATA
	PUBLIC(x86)
	PUBLIC(have_cpuid)
	PUBLIC(x86_model)
	PUBLIC(x86_mask)
	PUBLIC(x86_capability)
	PUBLIC(x86_capability_ebx)
	PUBLIC(x86_capability_x)
	PUBLIC(x86_capability_cores)
	PUBLIC(x86_vendor_id)
	PUBLIC(hard_math)

SYM(x86):
	.byte 0
SYM(have_cpuid):
	.long 0
SYM(x86_model):
	.byte 0
SYM(x86_mask):
	.byte 0
SYM(x86_capability):
	.long 0
SYM(x86_capability_ebx):
	.long 0
SYM(x86_capability_x):
	.long 0
SYM(x86_capability_cores):
	.long 0
SYM(x86_vendor_id):
	.zero 13
SYM(hard_math):
	.byte 0
END_DATA