1 | ; $Id: bs3-mode-CpuDetect.asm 76553 2019-01-01 01:45:53Z vboxsync $
|
---|
2 | ;; @file
|
---|
3 | ; BS3Kit - Bs3CpuDetect
|
---|
4 | ;
|
---|
5 |
|
---|
6 | ;
|
---|
7 | ; Copyright (C) 2007-2019 Oracle Corporation
|
---|
8 | ;
|
---|
9 | ; This file is part of VirtualBox Open Source Edition (OSE), as
|
---|
10 | ; available from http://www.virtualbox.org. This file is free software;
|
---|
11 | ; you can redistribute it and/or modify it under the terms of the GNU
|
---|
12 | ; General Public License (GPL) as published by the Free Software
|
---|
13 | ; Foundation, in version 2 as it comes in the "COPYING" file of the
|
---|
14 | ; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
|
---|
15 | ; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
|
---|
16 | ;
|
---|
17 | ; The contents of this file may alternatively be used under the terms
|
---|
18 | ; of the Common Development and Distribution License Version 1.0
|
---|
19 | ; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
|
---|
20 | ; VirtualBox OSE distribution, in which case the provisions of the
|
---|
21 | ; CDDL are applicable instead of those of the GPL.
|
---|
22 | ;
|
---|
23 | ; You may elect to license modified versions of this file under the
|
---|
24 | ; terms and conditions of either the GPL or the CDDL or both.
|
---|
25 | ;
|
---|
26 |
|
---|
27 | %include "bs3kit-template-header.mac"
|
---|
28 |
|
---|
29 | BS3_EXTERN_DATA16 g_uBs3CpuDetected
|
---|
30 |
|
---|
31 |
|
---|
32 | ;;
|
---|
33 | ; Rough CPU detection, mainly for detecting really old CPUs.
|
---|
34 | ;
|
---|
35 | ; A Bs3CpuDetectEx can be added if this is insufficient.
|
---|
36 | ;
|
---|
37 | ; @returns BS3CPU_xxx in xAX.
|
---|
38 | ; @cproto BS3_DECL(BS3CPU) Bs3CpuDetect(void);
|
---|
39 | ;
|
---|
40 | ; @uses xAX.
|
---|
41 | ;
|
---|
42 | ; @remarks ASSUMES we're in ring-0 when not in some kind of real mode.
|
---|
43 | ;
|
---|
44 | ; @note We put the real mode version of this code in the RMTEXT16 segment
|
---|
45 | ; to save space elsewhere. We generate a far call stub that goes
|
---|
46 | ; to the right segment.
|
---|
47 | ;
|
---|
48 | %if TMPL_MODE == BS3_MODE_RM
|
---|
49 | BS3_BEGIN_RMTEXT16
|
---|
50 | BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_FAR
|
---|
51 | %else
|
---|
52 | TMPL_BEGIN_TEXT
|
---|
53 | BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_HYBRID
|
---|
54 | %endif
|
---|
55 | CPU 8086
|
---|
56 | push xBP
|
---|
57 | mov xBP, xSP
|
---|
58 | pushf ; xBP - xCB*1
|
---|
59 | push xCX ; xBP - xCB*2
|
---|
60 | push xDX ; xBP - xCB*3
|
---|
61 | push xBX ; xBP - xCB*4
|
---|
62 | sub xSP, 20h ; xBP - xCB*4 - 20h
|
---|
63 |
|
---|
64 | %ifndef TMPL_CMN_PAGING
|
---|
65 | %ifdef TMPL_RM
|
---|
66 | %if 1 ; this is simpler
|
---|
67 | ;
|
---|
68 | ; FLAGS bits 15:12 are always set on 8086, 8088, V20, V30, 80186, and
|
---|
69 | ; 80188. FLAGS bit 15 is always zero on 286+, whereas bit 14 is NT and
|
---|
70 | ; bits 13:12 are IOPL.
|
---|
71 | ;
|
---|
72 | test byte [xBP - xCB + 1], 80h ; Top byte of saved flags.
|
---|
73 | jz .286plus
|
---|
74 | %else
|
---|
75 | ;
|
---|
76 | ; When executing 'PUSH SP' the 8086, 8088, V20, V30, 80186, and 80188
|
---|
77 | ; should be pushing the updated SP value instead of the initial one.
|
---|
78 | ;
|
---|
79 | push xSP
|
---|
80 | pop xAX
|
---|
81 | cmp xAX, xSP
|
---|
82 | je .286plus
|
---|
83 | %endif
|
---|
84 |
|
---|
85 | ;
|
---|
86 | ; Older than 286.
|
---|
87 | ;
|
---|
88 | ; Detect 8086/8088/V20/V30 vs. 80186/80188 by checking for pre 80186
|
---|
89 | ; shift behavior. the 80186/188 and later will mask the CL value according
|
---|
90 | ; to the width of the destination register, whereas 8086/88 and V20/30 will
|
---|
91 | ; perform the exact number of shifts specified.
|
---|
92 | ;
|
---|
93 | mov cl, 20h ; Shift count; 80186/88 and later will mask this by 0x1f (or 0xf)?
|
---|
94 | mov dx, 7fh
|
---|
95 | shl dx, cl
|
---|
96 | cmp dx, 7fh ; If no change, this is a 80186/88.
|
---|
97 | mov xAX, BS3CPU_80186
|
---|
98 | je .return
|
---|
99 |
|
---|
100 | ;
|
---|
101 | ; Detect 8086/88 vs V20/30 by exploiting undocumented POP CS encoding
|
---|
102 | ; that was redefined on V20/30 to SET1.
|
---|
103 | ;
|
---|
104 | xor ax, ax ; clear
|
---|
105 | push cs
|
---|
106 | db 0fh ; 8086/88: pop cs V20/30: set1 bl,cl
|
---|
107 | db 14h, 3ch ; 8086/88: add al, 3ch
|
---|
108 | ; 8086/88: al = 3ch V20/30: al = 0, cs on stack, bl modified.
|
---|
109 | cmp al, 3ch
|
---|
110 | jne .is_v20_or_v30
|
---|
111 | mov xAX, BS3CPU_8086
|
---|
112 | jmp .return
|
---|
113 |
|
---|
114 | .is_v20_or_v30:
|
---|
115 | pop xCX ; unclaimed CS
|
---|
116 | mov xAX, BS3CPU_V20
|
---|
117 | jmp .return
|
---|
118 |
|
---|
119 | %endif ; TMPL_RM
|
---|
120 |
|
---|
121 | CPU 286
|
---|
122 | .286plus:
|
---|
123 | ;
|
---|
124 | ; The 4th bit of the machine status word / CR0 indicates the precense
|
---|
125 | ; of a 80387 or later co-processor (a 80287+80386 => ET=0). 486 and
|
---|
126 | ; later should be hardcoding this to 1, according to the documentation
|
---|
127 | ; (need to test on 486SX). The initial idea here then would be to
|
---|
128 | ; assume 386+ if ET=1.
|
---|
129 | ;
|
---|
130 | ; The second idea was to check whether any reserved bits are set,
|
---|
131 | ; because the 286 here has bits 4 thru 15 all set. Unfortunately, it
|
---|
132 | ; turned out the 386SX and AMD 486DX-40 also sets bits 4 thru 15 when
|
---|
133 | ; using SMSW. So, nothing conclusive to distinguish 386 from 286, but
|
---|
134 | ; we've probably got a safe 486+ detection here.
|
---|
135 | ;
|
---|
136 | ;; @todo check if LOADALL can set any of the reserved bits on a 286 or 386.
|
---|
137 | smsw ax
|
---|
138 | test ax, ~(X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS | X86_CR0_ET | X86_CR0_NE)
|
---|
139 | jz .486plus
|
---|
140 |
|
---|
141 | ;
|
---|
142 | ; The 286 stores 0xff in the high byte of the SIDT and SGDT base
|
---|
143 | ; address (since it only did 24-bit addressing and the top 8-bit was
|
---|
144 | ; reserved for the 386). ASSUMES low IDT (which is the case for BS3Kit).
|
---|
145 | ;
|
---|
146 | sidt [xBP - xCB*4 - 20h]
|
---|
147 | cmp byte [xBP - xCB*4 - 20h + 2 + 3], 0ffh
|
---|
148 | jne .386plus
|
---|
149 |
|
---|
150 | %if 0
|
---|
151 | ;
|
---|
152 | ; Detect 80286 by checking whether the IOPL and NT bits of EFLAGS can be
|
---|
153 | ; modified or not. There are different accounts of these bits. Dr.Dobb's
|
---|
154 | ; (http://www.drdobbs.com/embedded-systems/processor-detection-schemes/184409011)
|
---|
155 | ; say they are undefined on 286es and will always be zero. Whereas Intel
|
---|
156 | ; iAPX 286 Programmer's Reference Manual (both order #210498-001 and
|
---|
157 | ; #210498-003) documents both IOPL and NT, but with comment 4 on page
|
---|
158 | ; C-43 stating that they cannot be POPFed in real mode and will both
|
---|
159 | ; remain 0. This is different from the 386+, where the NT flag isn't
|
---|
160 | ; privileged according to page 3-37 in #230985-003. Later Intel docs
|
---|
161 | ; (#235383-052US, page 4-192) documents real mode as taking both NT and
|
---|
162 | ; IOPL from what POPF reads off the stack - which is the behavior
|
---|
163 | ; observed a 386SX here.
|
---|
164 | ;
|
---|
165 | test al, X86_CR0_PE ; This flag test doesn't work in protected mode, ...
|
---|
166 | jnz .386plus ; ... so ASSUME 386plus if in PE for now.
|
---|
167 |
|
---|
168 | pushf ; Save a copy of the original flags for restoring IF.
|
---|
169 | pushf
|
---|
170 | pop ax
|
---|
171 | xor ax, X86_EFL_IOPL | X86_EFL_NT ; Try modify IOPL and NT.
|
---|
172 | and ax, ~X86_EFL_IF ; Try clear IF.
|
---|
173 | push ax ; Load modified flags.
|
---|
174 | popf
|
---|
175 | pushf ; Get actual flags.
|
---|
176 | pop dx
|
---|
177 | popf ; Restore IF, IOPL and NT.
|
---|
178 | cmp ax, dx
|
---|
179 | je .386plus ; If any of the flags are set, we're on 386+.
|
---|
180 |
|
---|
181 | ; While we could in theory be in v8086 mode at this point and be fooled
|
---|
182 | ; by a flaky POPF implementation, we assume this isn't the case in our
|
---|
183 | ; execution environment.
|
---|
184 | %endif
|
---|
185 | .is_286:
|
---|
186 | mov ax, BS3CPU_80286
|
---|
187 | jmp .return
|
---|
188 | %endif ; !TMPL_CMN_PAGING
|
---|
189 |
|
---|
190 | CPU 386
|
---|
191 | .386plus:
|
---|
192 | .486plus:
|
---|
193 | ;
|
---|
194 | ; Check for CPUID and AC. The former flag indicates CPUID support, the
|
---|
195 | ; latter was introduced with the 486.
|
---|
196 | ;
|
---|
197 | mov ebx, esp ; Save esp.
|
---|
198 | and esp, 0fffch ; Clear high word and don't trigger ACs.
|
---|
199 | pushfd
|
---|
200 | mov eax, [esp] ; eax = original EFLAGS.
|
---|
201 | xor dword [esp], X86_EFL_ID | X86_EFL_AC ; Flip the ID and AC flags.
|
---|
202 | popfd ; Load modified flags.
|
---|
203 | pushfd ; Save actual flags.
|
---|
204 | xchg eax, [esp] ; Switch, so the stack has the original flags.
|
---|
205 | xor eax, [esp] ; Calc changed flags.
|
---|
206 | popf ; Restore EFLAGS.
|
---|
207 | mov esp, ebx ; Restore possibly unaligned ESP.
|
---|
208 | test eax, X86_EFL_ID
|
---|
209 | jnz .have_cpuid ; If ID changed, we've got CPUID.
|
---|
210 | test eax, X86_EFL_AC
|
---|
211 | mov xAX, BS3CPU_80486
|
---|
212 | jnz .return ; If AC changed, we've got a 486 without CPUID (or similar).
|
---|
213 | mov xAX, BS3CPU_80386
|
---|
214 | jmp .return
|
---|
215 |
|
---|
216 | CPU 586
|
---|
217 | .have_cpuid:
|
---|
218 | ;
|
---|
219 | ; Do a very simple minded check here using the (standard) family field.
|
---|
220 | ; While here, we also check for PAE.
|
---|
221 | ;
|
---|
222 | mov eax, 1
|
---|
223 | cpuid
|
---|
224 |
|
---|
225 | ; Calc the extended family and model values before we mess up EAX.
|
---|
226 | mov cl, ah
|
---|
227 | and cl, 0fh
|
---|
228 | cmp cl, 0fh
|
---|
229 | jnz .not_extended_family
|
---|
230 | mov ecx, eax
|
---|
231 | shr ecx, 20
|
---|
232 | and cl, 7fh
|
---|
233 | add cl, 0fh
|
---|
234 | .not_extended_family: ; cl = family
|
---|
235 | mov ch, al
|
---|
236 | shr ch, 4
|
---|
237 | cmp cl, 0fh
|
---|
238 | jae .extended_model
|
---|
239 | cmp cl, 06h ; actually only intel, but we'll let this slip for now.
|
---|
240 | jne .done_model
|
---|
241 | .extended_model:
|
---|
242 | shr eax, 12
|
---|
243 | and al, 0f0h
|
---|
244 | or ch, al
|
---|
245 | .done_model: ; ch = model
|
---|
246 |
|
---|
247 | ; Start assembling return flags, checking for PSE + PAE.
|
---|
248 | mov eax, X86_CPUID_FEATURE_EDX_PSE | X86_CPUID_FEATURE_EDX_PAE
|
---|
249 | and eax, edx
|
---|
250 | mov ah, al
|
---|
251 | AssertCompile(X86_CPUID_FEATURE_EDX_PAE_BIT > BS3CPU_F_PAE_BIT - 8) ; 6 vs 10-8=2
|
---|
252 | and al, X86_CPUID_FEATURE_EDX_PAE
|
---|
253 | shr al, X86_CPUID_FEATURE_EDX_PAE_BIT - (BS3CPU_F_PAE_BIT - 8)
|
---|
254 | AssertCompile(X86_CPUID_FEATURE_EDX_PSE_BIT == BS3CPU_F_PSE_BIT - 8) ; 3 vs 11-8=3
|
---|
255 | and ah, X86_CPUID_FEATURE_EDX_PSE
|
---|
256 | or ah, al
|
---|
257 | or ah, (BS3CPU_F_CPUID >> 8)
|
---|
258 |
|
---|
259 | ; Add the CPU type based on the family and model values.
|
---|
260 | cmp cl, 6
|
---|
261 | jne .not_family_06h
|
---|
262 | mov al, BS3CPU_PPro
|
---|
263 | cmp ch, 1
|
---|
264 | jbe .return
|
---|
265 | mov al, BS3CPU_PProOrNewer
|
---|
266 | jmp .NewerThanPPro
|
---|
267 |
|
---|
268 | .not_family_06h:
|
---|
269 | mov al, BS3CPU_PProOrNewer
|
---|
270 | ja .NewerThanPPro
|
---|
271 | cmp cl, 5
|
---|
272 | mov al, BS3CPU_Pentium
|
---|
273 | je .return
|
---|
274 | cmp cl, 4
|
---|
275 | mov al, BS3CPU_80486
|
---|
276 | je .return
|
---|
277 | cmp cl, 3
|
---|
278 | mov al, BS3CPU_80386
|
---|
279 | je .return
|
---|
280 |
|
---|
281 | .NewerThanPPro:
|
---|
282 |
|
---|
283 | ; Check for extended leaves and long mode.
|
---|
284 | push xAX ; save PAE+PProOrNewer
|
---|
285 | mov eax, 0x80000000
|
---|
286 | cpuid
|
---|
287 | sub eax, 0x80000001 ; Minimum leaf 0x80000001
|
---|
288 | cmp eax, 0x00010000 ; At most 0x10000 leaves.
|
---|
289 | ja .no_ext_leaves
|
---|
290 |
|
---|
291 | mov eax, 0x80000001
|
---|
292 | cpuid
|
---|
293 | pop xAX ; restore PAE+PProOrNewer
|
---|
294 | test edx, X86_CPUID_EXT_FEATURE_EDX_LONG_MODE
|
---|
295 | jz .no_long_mode
|
---|
296 | or ah, ((BS3CPU_F_CPUID_EXT_LEAVES | BS3CPU_F_LONG_MODE) >> 8)
|
---|
297 | jmp .no_check_for_nx
|
---|
298 | .no_long_mode:
|
---|
299 | or ah, (BS3CPU_F_CPUID_EXT_LEAVES >> 8)
|
---|
300 | .no_check_for_nx:
|
---|
301 | test edx, X86_CPUID_EXT_FEATURE_EDX_NX
|
---|
302 | jz .return
|
---|
303 | or ax, BS3CPU_F_NX
|
---|
304 | jmp .return
|
---|
305 |
|
---|
306 | .no_ext_leaves:
|
---|
307 | pop xAX ; restore PAE+PProOrNewer
|
---|
308 |
|
---|
309 | CPU 8086
|
---|
310 | .return:
|
---|
311 | ;
|
---|
312 | ; Save the return value.
|
---|
313 | ;
|
---|
314 | mov [BS3_DATA16_WRT(g_uBs3CpuDetected)], ax
|
---|
315 |
|
---|
316 | ;
|
---|
317 | ; Epilogue.
|
---|
318 | ;
|
---|
319 | add xSP, 20h
|
---|
320 | pop xBX
|
---|
321 | pop xDX
|
---|
322 | pop xCX
|
---|
323 | popf
|
---|
324 | pop xBP
|
---|
325 | BS3_HYBRID_RET
|
---|
326 |
|
---|
327 | BS3_PROC_END_MODE Bs3CpuDetect
|
---|
328 |
|
---|
329 |
|
---|
330 | %if TMPL_MODE == BS3_MODE_RM
|
---|
331 | BS3_BEGIN_TEXT16_NEARSTUBS
|
---|
332 | BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_NEAR
|
---|
333 | call far TMPL_FAR_NM(Bs3CpuDetect)
|
---|
334 | ret
|
---|
335 | BS3_PROC_END_MODE Bs3CpuDetect
|
---|
336 | %endif
|
---|
337 |
|
---|