1 | /* cpuModel.S |
---|
2 | * |
---|
3 | * This file contains all assembly code for the Intel Cpu identification. |
---|
4 | * It is based on linux cpu detection code. |
---|
5 | * |
---|
6 | * Intel also provides public similar code in the book |
---|
7 | * called : |
---|
8 | * |
---|
9 | * Pentium Processor Family |
---|
10 | * Developer Family |
---|
11 | * Volume 3 : Architecture and Programming Manual |
---|
12 | * |
---|
13 | * At the following place : |
---|
14 | * |
---|
15 | * Chapter 5 : Feature determination |
---|
16 | * Chapter 25: CPUID instruction |
---|
17 | * |
---|
18 | * COPYRIGHT (c) 1998 valette@crf.canon.fr |
---|
19 | * |
---|
20 | * The license and distribution terms for this file may be |
---|
21 | * found in the file LICENSE in this distribution or at |
---|
22 | * http://www.rtems.com/license/LICENSE. |
---|
23 | * |
---|
24 | * $Id$ |
---|
25 | */ |
---|
26 | |
---|
27 | #include <rtems/asm.h> |
---|
28 | #include <rtems/score/registers.h> |
---|
29 | |
---|
30 | BEGIN_CODE |
---|
31 | PUBLIC(checkCPUtypeSetCr0); |
---|
32 | /* |
---|
33 | * check Processor type: 386, 486, 6x86(L) or CPUID capable processor |
---|
34 | */ |
---|
35 | |
---|
36 | SYM (checkCPUtypeSetCr0): |
---|
37 | /* |
---|
38 | * Assume 386 for now |
---|
39 | */ |
---|
40 | movl $3, SYM (x86) |
---|
41 | /* |
---|
42 | * Start using the EFLAGS AC bit determination method described in |
---|
43 | * the book mentioned above page 5.1. If this bit can be set we |
---|
44 | * have a 486 or above. |
---|
45 | */ |
---|
46 | pushfl /* save EFLAGS */ |
---|
47 | |
---|
48 | pushfl /* Get EFLAGS in EAX */ |
---|
49 | popl eax |
---|
50 | |
---|
51 | movl eax,ecx /* save original EFLAGS in ECX */ |
---|
52 | xorl $EFLAGS_ALIGN_CHECK,eax /* flip AC bit in EAX */ |
---|
53 | pushl eax /* set EAX as EFLAGS */ |
---|
54 | popfl |
---|
55 | pushfl /* Get new EFLAGS in EAX */ |
---|
56 | popl eax |
---|
57 | |
---|
58 | xorl ecx,eax /* check if AC bit changed */ |
---|
59 | andl $EFLAGS_ALIGN_CHECK,eax |
---|
60 | je is386 /* If not : we have a 386 */ |
---|
61 | /* |
---|
62 | * Assume 486 for now |
---|
63 | */ |
---|
64 | movl $4,SYM (x86) |
---|
65 | movl ecx,eax /* Restore orig EFLAGS in EAX */ |
---|
66 | xorl $EFLAGS_ID,eax /* flip ID flag */ |
---|
67 | pushl eax /* set EAX as EFLAGS */ |
---|
68 | popfl |
---|
69 | pushfl /* Get new EFLAGS in EAX */ |
---|
70 | popl eax |
---|
71 | |
---|
72 | xorl ecx,eax /* check if ID bit changed */ |
---|
73 | andl $EFLAGS_ID,eax |
---|
74 | |
---|
75 | /* |
---|
76 | * if we are on a straight 486DX, |
---|
77 | * SX, or 487SX we can't change it |
---|
78 | * OTOH 6x86MXs and MIIs check OK |
---|
79 | * Also if we are on a Cyrix 6x86(L) |
---|
80 | */ |
---|
81 | je is486x |
---|
82 | |
---|
83 | isnew: |
---|
84 | /* |
---|
85 | * restore original EFLAGS |
---|
86 | */ |
---|
87 | popfl |
---|
88 | incl SYM(have_cpuid) /* we have CPUID instruction */ |
---|
89 | |
---|
90 | /* |
---|
91 | * Addressable Processor Ids |
---|
92 | * |
---|
93 | * CPUID.(EAX=4, ECX=0):EAX[31:26] + 1 = Y) |
---|
94 | */ |
---|
95 | movl $4, eax |
---|
96 | movl $0, ecx |
---|
97 | cpuid |
---|
98 | movl eax,SYM(x86_capability_cores) |
---|
99 | |
---|
100 | /* use it to get : |
---|
101 | * processor type, |
---|
102 | * processor model, |
---|
103 | * processor mask, |
---|
104 | * by using it with EAX = 1 |
---|
105 | */ |
---|
106 | movl $1, eax |
---|
107 | cpuid |
---|
108 | movl ebx,SYM(x86_capability_ebx) /* store ebx feature info */ |
---|
109 | movl ecx,SYM(x86_capability_x) /* store ecx feature flags */ |
---|
110 | |
---|
111 | movb al, cl /* save reg for future use */ |
---|
112 | |
---|
113 | andb $0x0f,ah /* mask processor family */ |
---|
114 | movb ah,SYM (x86) /* put result in x86 var */ |
---|
115 | |
---|
116 | andb $0xf0, al /* get model */ |
---|
117 | shrb $4, al |
---|
118 | movb al,SYM (x86_model) /* store it in x86_model */ |
---|
119 | |
---|
120 | andb $0x0f, cl /* get mask revision */ |
---|
121 | movb cl,SYM (x86_mask) /* store it in x86_mask */ |
---|
122 | |
---|
123 | movl edx,SYM(x86_capability) /* store feature flags in x86_capability */ |
---|
124 | |
---|
125 | /* get vendor info by using CPUID with EXA = 0 */ |
---|
126 | xorl eax, eax |
---|
127 | cpuid |
---|
128 | |
---|
129 | /* |
---|
130 | * store results contained in ebx, edx, ecx in |
---|
131 | * x86_vendor_id variable. |
---|
132 | */ |
---|
133 | movl ebx,SYM(x86_vendor_id) |
---|
134 | movl edx,SYM(x86_vendor_id)+4 |
---|
135 | movl ecx,SYM(x86_vendor_id)+8 |
---|
136 | |
---|
137 | movl cr0,eax /* 486+ */ |
---|
138 | andl $(CR0_PAGING | CR0_PROTECTION_ENABLE | CR0_EXTENSION_TYPE), eax |
---|
139 | orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax |
---|
140 | jmp 2f |
---|
141 | |
---|
142 | /* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid |
---|
143 | * clobbering the new BX chipset used with the Pentium II, which has a register |
---|
144 | * at the same addresses as those used to access the Cyrix special configuration |
---|
145 | * registers (CCRs). |
---|
146 | */ |
---|
147 | /* |
---|
148 | * A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2 |
---|
149 | * (and it _must_ be 5 divided by 2) while other CPUs change |
---|
150 | * them in undefined ways. We need to know this since we may |
---|
151 | * need to enable the CPUID instruction at least. |
---|
152 | * We couldn't use this test before since the PPro and PII behave |
---|
153 | * like Cyrix chips in this respect. |
---|
154 | */ |
---|
155 | is486x: xor ax,ax |
---|
156 | sahf |
---|
157 | movb $5,al |
---|
158 | movb $2,bl |
---|
159 | div bl |
---|
160 | lahf |
---|
161 | cmpb $2,ah |
---|
162 | jne ncyrix |
---|
163 | /* |
---|
164 | * N.B. The pattern of accesses to 0x22 and 0x23 is *essential* |
---|
165 | * so do not try to "optimize" it! For the same reason we |
---|
166 | * do all this with interrupts off. |
---|
167 | */ |
---|
168 | #define setCx86(reg, val) \ |
---|
169 | movb reg,al; \ |
---|
170 | outb al,$0x22; \ |
---|
171 | movb val,al; \ |
---|
172 | outb al,$0x23 |
---|
173 | |
---|
174 | #define getCx86(reg) \ |
---|
175 | movb reg,al; \ |
---|
176 | outb al,$0x22; \ |
---|
177 | inb $0x23,al |
---|
178 | |
---|
179 | cli |
---|
180 | getCx86($0xc3) /* get CCR3 */ |
---|
181 | movb al,cl /* Save old value */ |
---|
182 | movb al,bl |
---|
183 | andb $0x0f,bl /* Enable access to all config registers */ |
---|
184 | orb $0x10,bl /* by setting bit 4 */ |
---|
185 | setCx86($0xc3,bl) |
---|
186 | |
---|
187 | getCx86($0xe8) /* now we can get CCR4 */ |
---|
188 | orb $0x80,al /* and set bit 7 (CPUIDEN) */ |
---|
189 | movb al,bl /* to enable CPUID execution */ |
---|
190 | setCx86($0xe8,bl) |
---|
191 | |
---|
192 | getCx86($0xfe) /* DIR0 : let's check this is a 6x86(L) */ |
---|
193 | andb $0xf0,al /* should be 3xh */ |
---|
194 | cmpb $0x30,al |
---|
195 | jne n6x86 |
---|
196 | getCx86($0xe9) /* CCR5 : we reset the SLOP bit */ |
---|
197 | andb $0xfd,al /* so that udelay calculation */ |
---|
198 | movb al,bl /* is correct on 6x86(L) CPUs */ |
---|
199 | setCx86($0xe9,bl) |
---|
200 | setCx86($0xc3,cl) /* Restore old CCR3 */ |
---|
201 | sti |
---|
202 | jmp isnew /* We enabled CPUID now */ |
---|
203 | |
---|
204 | n6x86: setCx86($0xc3,cl) /* Restore old CCR3 */ |
---|
205 | sti |
---|
206 | ncyrix: /* restore original EFLAGS */ |
---|
207 | popfl |
---|
208 | movl cr0,eax /* 486 */ |
---|
209 | andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax /* Save PG,PE,ET */ |
---|
210 | orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax /* set AM, WP, NE and MP */ |
---|
211 | jmp 2f |
---|
212 | is386: /* restore original EFLAGS */ |
---|
213 | popfl |
---|
214 | movl cr0,eax /* 386 */ |
---|
215 | andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax /* Save PG,PE,ET */ |
---|
216 | orl $CR0_MONITOR_COPROC,eax /* set MP */ |
---|
217 | 2: movl eax,cr0 |
---|
218 | call check_x87 |
---|
219 | ret |
---|
220 | |
---|
221 | |
---|
222 | /* |
---|
223 | * We depend on ET to be correct. This checks for 287/387. |
---|
224 | */ |
---|
225 | check_x87: |
---|
226 | movb $0,SYM(hard_math) |
---|
227 | clts |
---|
228 | fninit |
---|
229 | fstsw ax |
---|
230 | cmpb $0,al |
---|
231 | je 1f |
---|
232 | movl cr0,eax /* no coprocessor: have to set bits */ |
---|
233 | xorl $4,eax /* set EM */ |
---|
234 | movl eax,cr0 |
---|
235 | ret |
---|
236 | .align 16 |
---|
237 | 1: movb $1,SYM(hard_math) |
---|
238 | .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ |
---|
239 | ret |
---|
240 | |
---|
241 | END_CODE |
---|
242 | |
---|
243 | BEGIN_DATA |
---|
244 | PUBLIC(x86) |
---|
245 | PUBLIC(have_cpuid) |
---|
246 | PUBLIC(x86_model) |
---|
247 | PUBLIC(x86_mask) |
---|
248 | PUBLIC(x86_capability) |
---|
249 | PUBLIC(x86_capability_ebx) |
---|
250 | PUBLIC(x86_capability_x) |
---|
251 | PUBLIC(x86_capability_cores) |
---|
252 | PUBLIC(x86_vendor_id) |
---|
253 | PUBLIC(hard_math) |
---|
254 | |
---|
255 | SYM(x86): |
---|
256 | .byte 0 |
---|
257 | SYM(have_cpuid): |
---|
258 | .long 0 |
---|
259 | SYM(x86_model): |
---|
260 | .byte 0 |
---|
261 | SYM(x86_mask): |
---|
262 | .byte 0 |
---|
263 | SYM(x86_capability): |
---|
264 | .long 0 |
---|
265 | SYM(x86_capability_ebx): |
---|
266 | .long 0 |
---|
267 | SYM(x86_capability_x): |
---|
268 | .long 0 |
---|
269 | SYM(x86_capability_cores): |
---|
270 | .long 0 |
---|
271 | SYM(x86_vendor_id): |
---|
272 | .zero 13 |
---|
273 | SYM(hard_math): |
---|
274 | .byte 0 |
---|
275 | END_DATA |
---|