1 | ; $Id: bs3-mode-CpuDetect.asm 60554 2016-04-18 19:11:32Z vboxsync $
|
---|
2 | ;; @file
|
---|
3 | ; BS3Kit - Bs3CpuDetect
|
---|
4 | ;
|
---|
5 |
|
---|
6 | ;
|
---|
7 | ; Copyright (C) 2007-2016 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 | TMPL_BEGIN_TEXT
|
---|
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 | BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_HYBRID_0_ARGS
|
---|
45 | CPU 8086
|
---|
46 | push xBP
|
---|
47 | mov xBP, xSP
|
---|
48 | pushf
|
---|
49 | push xCX
|
---|
50 | push xDX
|
---|
51 | push xBX
|
---|
52 |
|
---|
53 | %ifndef TMPL_CMN_PAGING
|
---|
54 | %ifdef TMPL_RM
|
---|
55 | %if 1 ; this is simpler
|
---|
56 | ;
|
---|
57 | ; FLAGS bits 15:12 are always set on 8086, 8088, V20, V30, 80186, and
|
---|
58 | ; 80188. FLAGS bit 15 is always zero on 286+, whereas bit 14 is NT and
|
---|
59 | ; bits 13:12 are IOPL.
|
---|
60 | ;
|
---|
61 | test byte [xBP - xCB + 1], 80h ; Top byte of saved flags.
|
---|
62 | jz .286plus
|
---|
63 | %else
|
---|
64 | ;
|
---|
65 | ; When executing 'PUSH SP' the 8086, 8088, V20, V30, 80186, and 80188
|
---|
66 | ; should be pushing the updated SP value instead of the initial one.
|
---|
67 | ;
|
---|
68 | push xSP
|
---|
69 | pop xAX
|
---|
70 | cmp xAX, xSP
|
---|
71 | je .286plus
|
---|
72 | %endif
|
---|
73 |
|
---|
74 | ;
|
---|
75 | ; Older than 286.
|
---|
76 | ;
|
---|
77 | ; Detect 8086/8088/V20/V30 vs. 80186/80188 by checking for pre 80186
|
---|
78 | ; shift behavior. the 80186/188 and later will mask the CL value according
|
---|
79 | ; to the width of the destination register, whereas 8086/88 and V20/30 will
|
---|
80 | ; perform the exact number of shifts specified.
|
---|
81 | ;
|
---|
82 | mov cl, 20h ; Shift count; 80186/88 and later will mask this by 0x1f (or 0xf)?
|
---|
83 | mov dx, 7fh
|
---|
84 | shl dx, cl
|
---|
85 | cmp dx, 7fh ; If no change, this is a 80186/88.
|
---|
86 | mov xAX, BS3CPU_80186
|
---|
87 | je .return
|
---|
88 |
|
---|
89 | ;
|
---|
90 | ; Detect 8086/88 vs V20/30 by exploiting undocumented POP CS encoding
|
---|
91 | ; that was redefined on V20/30 to SET1.
|
---|
92 | ;
|
---|
93 | xor ax, ax ; clear
|
---|
94 | push cs
|
---|
95 | db 0fh ; 8086/88: pop cs V20/30: set1 bl,cl
|
---|
96 | db 14h, 3ch ; 8086/88: add al, 3ch
|
---|
97 | ; 8086/88: al = 3ch V20/30: al = 0, cs on stack, bl modified.
|
---|
98 | cmp al, 3ch
|
---|
99 | jne .is_v20_or_v30
|
---|
100 | mov xAX, BS3CPU_8086
|
---|
101 | jmp .return
|
---|
102 |
|
---|
103 | .is_v20_or_v30:
|
---|
104 | pop xCX ; unclaimed CS
|
---|
105 | mov xAX, BS3CPU_V20
|
---|
106 | jmp .return
|
---|
107 |
|
---|
108 | %endif ; TMPL_RM
|
---|
109 |
|
---|
110 | CPU 286
|
---|
111 | .286plus:
|
---|
112 | ;
|
---|
113 | ; The 4th bit of the machine status word / CR0 indicates the precense
|
---|
114 | ; of a 80387 or later co-processor (a 80287+80386 => ET=0). 486 and
|
---|
115 | ; later should be hardcoding this to 1, according to the documentation
|
---|
116 | ; (need to test on 486SX). The initial idea here then would be to
|
---|
117 | ; assume 386+ if ET=1.
|
---|
118 | ;
|
---|
119 | ; However, it turns out the 286 I've got here has bits 4 thru 15 all
|
---|
120 | ; set. This is very nice though, because only bits 4 and 5 are defined
|
---|
121 | ; on later CPUs and the remainder MBZ. So, check whether any of the MBZ
|
---|
122 | ; bits are set, if so, then it's 286.
|
---|
123 | ;
|
---|
124 | smsw ax
|
---|
125 | test ax, ~(X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS | X86_CR0_ET | X86_CR0_NE)
|
---|
126 | jnz .is_286
|
---|
127 |
|
---|
128 | ;
|
---|
129 | ; Detect 80286 by checking whether the IOPL and NT bits of EFLAGS can be
|
---|
130 | ; modified or not. There are different accounts of these bits. Dr.Dobb's
|
---|
131 | ; (http://www.drdobbs.com/embedded-systems/processor-detection-schemes/184409011)
|
---|
132 | ; say they are undefined on 286es and will always be zero. Whereas Intel
|
---|
133 | ; iAPX 286 Programmer's Reference Manual (both order #210498-001 and
|
---|
134 | ; #210498-003) documents both IOPL and NT, but with comment 4 on page
|
---|
135 | ; C-43 stating that they cannot be POPFed in real mode and will both
|
---|
136 | ; remain 0. This is different from the 386+, where the NT flag isn't
|
---|
137 | ; privileged according to page 3-37 in #230985-003. Later Intel docs
|
---|
138 | ; (#235383-052US, page 4-192) documents real mode as taking both NT and
|
---|
139 | ; IOPL from what POPF reads off the stack - which is the behavior
|
---|
140 | ; observed a 386SX here.
|
---|
141 | ;
|
---|
142 | test al, X86_CR0_PE ; This flag test doesn't work in protected mode, ...
|
---|
143 | jnz .386plus ; ... so ASSUME 386plus if in PE for now.
|
---|
144 |
|
---|
145 | pushf ; Save a copy of the original flags for restoring IF.
|
---|
146 | pushf
|
---|
147 | pop ax
|
---|
148 | xor ax, X86_EFL_IOPL | X86_EFL_NT ; Try modify IOPL and NT.
|
---|
149 | and ax, ~X86_EFL_IF ; Try clear IF.
|
---|
150 | push ax ; Load modified flags.
|
---|
151 | popf
|
---|
152 | pushf ; Get actual flags.
|
---|
153 | pop dx
|
---|
154 | popf ; Restore IF, IOPL and NT.
|
---|
155 | cmp ax, dx
|
---|
156 | je .386plus ; If any of the flags are set, we're on 386+.
|
---|
157 |
|
---|
158 | ; While we could in theory be in v8086 mode at this point and be fooled
|
---|
159 | ; by a flaky POPF implementation, we assume this isn't the case in our
|
---|
160 | ; execution environment.
|
---|
161 |
|
---|
162 | .is_286:
|
---|
163 | mov ax, BS3CPU_80286
|
---|
164 | jmp .return
|
---|
165 | %endif ; !TMPL_CMN_PAGING
|
---|
166 |
|
---|
167 | CPU 386
|
---|
168 | .386plus:
|
---|
169 | ;
|
---|
170 | ; Check for CPUID and AC. The former flag indicates CPUID support, the
|
---|
171 | ; latter was introduced with the 486.
|
---|
172 | ;
|
---|
173 | mov ebx, esp ; Save esp.
|
---|
174 | and esp, 0fffch ; Clear high word and don't trigger ACs.
|
---|
175 | pushfd
|
---|
176 | mov eax, [esp] ; eax = original EFLAGS.
|
---|
177 | xor dword [esp], X86_EFL_ID | X86_EFL_AC ; Flip the ID and AC flags.
|
---|
178 | popfd ; Load modified flags.
|
---|
179 | pushfd ; Save actual flags.
|
---|
180 | xchg eax, [esp] ; Switch, so the stack has the original flags.
|
---|
181 | xor eax, [esp] ; Calc changed flags.
|
---|
182 | popf ; Restore EFLAGS.
|
---|
183 | mov esp, ebx ; Restore possibly unaligned ESP.
|
---|
184 | test eax, X86_EFL_ID
|
---|
185 | jnz .have_cpuid ; If ID changed, we've got CPUID.
|
---|
186 | test eax, X86_EFL_AC
|
---|
187 | mov xAX, BS3CPU_80486
|
---|
188 | jnz .return ; If AC changed, we've got a 486 without CPUID (or similar).
|
---|
189 | mov xAX, BS3CPU_80386
|
---|
190 | jmp .return
|
---|
191 |
|
---|
192 | CPU 586
|
---|
193 | .have_cpuid:
|
---|
194 | ;
|
---|
195 | ; Do a very simple minded check here using the (standard) family field.
|
---|
196 | ; While here, we also check for PAE.
|
---|
197 | ;
|
---|
198 | mov eax, 1
|
---|
199 | cpuid
|
---|
200 |
|
---|
201 | ; Calc the extended family and model values before we mess up EAX.
|
---|
202 | mov cl, ah
|
---|
203 | and cl, 0fh
|
---|
204 | cmp cl, 0fh
|
---|
205 | jnz .not_extended_family
|
---|
206 | mov ecx, eax
|
---|
207 | shr ecx, 20
|
---|
208 | and cl, 7fh
|
---|
209 | add cl, 0fh
|
---|
210 | .not_extended_family: ; cl = family
|
---|
211 | mov ch, al
|
---|
212 | shr ch, 4
|
---|
213 | cmp cl, 0fh
|
---|
214 | jae .extended_model
|
---|
215 | cmp cl, 06h ; actually only intel, but we'll let this slip for now.
|
---|
216 | jne .done_model
|
---|
217 | .extended_model:
|
---|
218 | shr eax, 12
|
---|
219 | and al, 0f0h
|
---|
220 | or ch, al
|
---|
221 | .done_model: ; ch = model
|
---|
222 |
|
---|
223 | ; Start assembling return flags, checking for PSE + PAE.
|
---|
224 | mov eax, X86_CPUID_FEATURE_EDX_PSE | X86_CPUID_FEATURE_EDX_PAE
|
---|
225 | and eax, edx
|
---|
226 | mov ah, al
|
---|
227 | AssertCompile(X86_CPUID_FEATURE_EDX_PAE_BIT > BS3CPU_F_PAE_BIT - 8) ; 6 vs 10-8=2
|
---|
228 | and al, X86_CPUID_FEATURE_EDX_PAE
|
---|
229 | shr al, X86_CPUID_FEATURE_EDX_PAE_BIT - (BS3CPU_F_PAE_BIT - 8)
|
---|
230 | AssertCompile(X86_CPUID_FEATURE_EDX_PSE_BIT == BS3CPU_F_PSE_BIT - 8) ; 3 vs 11-8=3
|
---|
231 | and ah, X86_CPUID_FEATURE_EDX_PSE
|
---|
232 | or ah, al
|
---|
233 | or ah, (BS3CPU_F_CPUID >> 8)
|
---|
234 |
|
---|
235 | ; Add the CPU type based on the family and model values.
|
---|
236 | cmp cl, 6
|
---|
237 | jne .not_family_06h
|
---|
238 | mov al, BS3CPU_PPro
|
---|
239 | cmp ch, 1
|
---|
240 | jbe .return
|
---|
241 | mov al, BS3CPU_PProOrNewer
|
---|
242 | jmp .NewerThanPPro
|
---|
243 |
|
---|
244 | .not_family_06h:
|
---|
245 | mov al, BS3CPU_PProOrNewer
|
---|
246 | ja .NewerThanPPro
|
---|
247 | cmp cl, 5
|
---|
248 | mov al, BS3CPU_Pentium
|
---|
249 | je .return
|
---|
250 | cmp cl, 4
|
---|
251 | mov al, BS3CPU_80486
|
---|
252 | je .return
|
---|
253 | cmp cl, 3
|
---|
254 | mov al, BS3CPU_80386
|
---|
255 | je .return
|
---|
256 |
|
---|
257 | .NewerThanPPro:
|
---|
258 |
|
---|
259 | ; Check for extended leaves and long mode.
|
---|
260 | push xAX ; save PAE+PProOrNewer
|
---|
261 | mov eax, 0x80000000
|
---|
262 | cpuid
|
---|
263 | sub eax, 0x80000001 ; Minimum leaf 0x80000001
|
---|
264 | cmp eax, 0x00010000 ; At most 0x10000 leaves.
|
---|
265 | ja .no_ext_leaves
|
---|
266 |
|
---|
267 | mov eax, 0x80000001
|
---|
268 | cpuid
|
---|
269 | pop xAX ; restore PAE+PProOrNewer
|
---|
270 | test edx, X86_CPUID_EXT_FEATURE_EDX_LONG_MODE
|
---|
271 | jz .no_long_mode
|
---|
272 | or ax, BS3CPU_F_CPUID_EXT_LEAVES | BS3CPU_F_LONG_MODE
|
---|
273 | jmp .return
|
---|
274 | .no_long_mode:
|
---|
275 | or ax, BS3CPU_F_CPUID_EXT_LEAVES
|
---|
276 | jmp .return
|
---|
277 | .no_ext_leaves:
|
---|
278 | pop xAX ; restore PAE+PProOrNewer
|
---|
279 |
|
---|
280 | CPU 8086
|
---|
281 | .return:
|
---|
282 | ;
|
---|
283 | ; Save the return value.
|
---|
284 | ;
|
---|
285 | mov [BS3_DATA16_WRT(g_uBs3CpuDetected)], ax
|
---|
286 |
|
---|
287 | ;
|
---|
288 | ; Epilogue.
|
---|
289 | ;
|
---|
290 | pop xBX
|
---|
291 | pop xDX
|
---|
292 | pop xCX
|
---|
293 | popf
|
---|
294 | pop xBP
|
---|
295 | BS3_HYBRID_RET
|
---|
296 |
|
---|
297 | BS3_PROC_END_MODE Bs3CpuDetect
|
---|
298 |
|
---|