summaryrefslogtreecommitdiffstats
path: root/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm')
-rw-r--r--src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm337
1 files changed, 337 insertions, 0 deletions
diff --git a/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm
new file mode 100644
index 00000000..0ccfbf80
--- /dev/null
+++ b/src/VBox/ValidationKit/bootsectors/bs3kit/bs3-mode-CpuDetect.asm
@@ -0,0 +1,337 @@
+; $Id: bs3-mode-CpuDetect.asm $
+;; @file
+; BS3Kit - Bs3CpuDetect
+;
+
+;
+; Copyright (C) 2007-2019 Oracle Corporation
+;
+; This file is part of VirtualBox Open Source Edition (OSE), as
+; available from http://www.virtualbox.org. This file is free software;
+; you can redistribute it and/or modify it under the terms of the GNU
+; General Public License (GPL) as published by the Free Software
+; Foundation, in version 2 as it comes in the "COPYING" file of the
+; VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+; VirtualBox OSE distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+
+%include "bs3kit-template-header.mac"
+
+BS3_EXTERN_DATA16 g_uBs3CpuDetected
+
+
+;;
+; Rough CPU detection, mainly for detecting really old CPUs.
+;
+; A Bs3CpuDetectEx can be added if this is insufficient.
+;
+; @returns BS3CPU_xxx in xAX.
+; @cproto BS3_DECL(BS3CPU) Bs3CpuDetect(void);
+;
+; @uses xAX.
+;
+; @remarks ASSUMES we're in ring-0 when not in some kind of real mode.
+;
+; @note We put the real mode version of this code in the RMTEXT16 segment
+; to save space elsewhere. We generate a far call stub that goes
+; to the right segment.
+;
+%if TMPL_MODE == BS3_MODE_RM
+BS3_BEGIN_RMTEXT16
+BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_FAR
+%else
+TMPL_BEGIN_TEXT
+BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_HYBRID
+%endif
+CPU 8086
+ push xBP
+ mov xBP, xSP
+ pushf ; xBP - xCB*1
+ push xCX ; xBP - xCB*2
+ push xDX ; xBP - xCB*3
+ push xBX ; xBP - xCB*4
+ sub xSP, 20h ; xBP - xCB*4 - 20h
+
+%ifndef TMPL_CMN_PAGING
+ %ifdef TMPL_RM
+ %if 1 ; this is simpler
+ ;
+ ; FLAGS bits 15:12 are always set on 8086, 8088, V20, V30, 80186, and
+ ; 80188. FLAGS bit 15 is always zero on 286+, whereas bit 14 is NT and
+ ; bits 13:12 are IOPL.
+ ;
+ test byte [xBP - xCB + 1], 80h ; Top byte of saved flags.
+ jz .286plus
+ %else
+ ;
+ ; When executing 'PUSH SP' the 8086, 8088, V20, V30, 80186, and 80188
+ ; should be pushing the updated SP value instead of the initial one.
+ ;
+ push xSP
+ pop xAX
+ cmp xAX, xSP
+ je .286plus
+ %endif
+
+ ;
+ ; Older than 286.
+ ;
+ ; Detect 8086/8088/V20/V30 vs. 80186/80188 by checking for pre 80186
+ ; shift behavior. the 80186/188 and later will mask the CL value according
+ ; to the width of the destination register, whereas 8086/88 and V20/30 will
+ ; perform the exact number of shifts specified.
+ ;
+ mov cl, 20h ; Shift count; 80186/88 and later will mask this by 0x1f (or 0xf)?
+ mov dx, 7fh
+ shl dx, cl
+ cmp dx, 7fh ; If no change, this is a 80186/88.
+ mov xAX, BS3CPU_80186
+ je .return
+
+ ;
+ ; Detect 8086/88 vs V20/30 by exploiting undocumented POP CS encoding
+ ; that was redefined on V20/30 to SET1.
+ ;
+ xor ax, ax ; clear
+ push cs
+ db 0fh ; 8086/88: pop cs V20/30: set1 bl,cl
+ db 14h, 3ch ; 8086/88: add al, 3ch
+ ; 8086/88: al = 3ch V20/30: al = 0, cs on stack, bl modified.
+ cmp al, 3ch
+ jne .is_v20_or_v30
+ mov xAX, BS3CPU_8086
+ jmp .return
+
+.is_v20_or_v30:
+ pop xCX ; unclaimed CS
+ mov xAX, BS3CPU_V20
+ jmp .return
+
+ %endif ; TMPL_RM
+
+CPU 286
+.286plus:
+ ;
+ ; The 4th bit of the machine status word / CR0 indicates the precense
+ ; of a 80387 or later co-processor (a 80287+80386 => ET=0). 486 and
+ ; later should be hardcoding this to 1, according to the documentation
+ ; (need to test on 486SX). The initial idea here then would be to
+ ; assume 386+ if ET=1.
+ ;
+ ; The second idea was to check whether any reserved bits are set,
+ ; because the 286 here has bits 4 thru 15 all set. Unfortunately, it
+ ; turned out the 386SX and AMD 486DX-40 also sets bits 4 thru 15 when
+ ; using SMSW. So, nothing conclusive to distinguish 386 from 286, but
+ ; we've probably got a safe 486+ detection here.
+ ;
+ ;; @todo check if LOADALL can set any of the reserved bits on a 286 or 386.
+ smsw ax
+ test ax, ~(X86_CR0_PE | X86_CR0_MP | X86_CR0_EM | X86_CR0_TS | X86_CR0_ET | X86_CR0_NE)
+ jz .486plus
+
+ ;
+ ; The 286 stores 0xff in the high byte of the SIDT and SGDT base
+ ; address (since it only did 24-bit addressing and the top 8-bit was
+ ; reserved for the 386). ASSUMES low IDT (which is the case for BS3Kit).
+ ;
+ sidt [xBP - xCB*4 - 20h]
+ cmp byte [xBP - xCB*4 - 20h + 2 + 3], 0ffh
+ jne .386plus
+
+ %if 0
+ ;
+ ; Detect 80286 by checking whether the IOPL and NT bits of EFLAGS can be
+ ; modified or not. There are different accounts of these bits. Dr.Dobb's
+ ; (http://www.drdobbs.com/embedded-systems/processor-detection-schemes/184409011)
+ ; say they are undefined on 286es and will always be zero. Whereas Intel
+ ; iAPX 286 Programmer's Reference Manual (both order #210498-001 and
+ ; #210498-003) documents both IOPL and NT, but with comment 4 on page
+ ; C-43 stating that they cannot be POPFed in real mode and will both
+ ; remain 0. This is different from the 386+, where the NT flag isn't
+ ; privileged according to page 3-37 in #230985-003. Later Intel docs
+ ; (#235383-052US, page 4-192) documents real mode as taking both NT and
+ ; IOPL from what POPF reads off the stack - which is the behavior
+ ; observed a 386SX here.
+ ;
+ test al, X86_CR0_PE ; This flag test doesn't work in protected mode, ...
+ jnz .386plus ; ... so ASSUME 386plus if in PE for now.
+
+ pushf ; Save a copy of the original flags for restoring IF.
+ pushf
+ pop ax
+ xor ax, X86_EFL_IOPL | X86_EFL_NT ; Try modify IOPL and NT.
+ and ax, ~X86_EFL_IF ; Try clear IF.
+ push ax ; Load modified flags.
+ popf
+ pushf ; Get actual flags.
+ pop dx
+ popf ; Restore IF, IOPL and NT.
+ cmp ax, dx
+ je .386plus ; If any of the flags are set, we're on 386+.
+
+ ; While we could in theory be in v8086 mode at this point and be fooled
+ ; by a flaky POPF implementation, we assume this isn't the case in our
+ ; execution environment.
+ %endif
+.is_286:
+ mov ax, BS3CPU_80286
+ jmp .return
+%endif ; !TMPL_CMN_PAGING
+
+CPU 386
+.386plus:
+.486plus:
+ ;
+ ; Check for CPUID and AC. The former flag indicates CPUID support, the
+ ; latter was introduced with the 486.
+ ;
+ mov ebx, esp ; Save esp.
+ and esp, 0fffch ; Clear high word and don't trigger ACs.
+ pushfd
+ mov eax, [esp] ; eax = original EFLAGS.
+ xor dword [esp], X86_EFL_ID | X86_EFL_AC ; Flip the ID and AC flags.
+ popfd ; Load modified flags.
+ pushfd ; Save actual flags.
+ xchg eax, [esp] ; Switch, so the stack has the original flags.
+ xor eax, [esp] ; Calc changed flags.
+ popf ; Restore EFLAGS.
+ mov esp, ebx ; Restore possibly unaligned ESP.
+ test eax, X86_EFL_ID
+ jnz .have_cpuid ; If ID changed, we've got CPUID.
+ test eax, X86_EFL_AC
+ mov xAX, BS3CPU_80486
+ jnz .return ; If AC changed, we've got a 486 without CPUID (or similar).
+ mov xAX, BS3CPU_80386
+ jmp .return
+
+CPU 586
+.have_cpuid:
+ ;
+ ; Do a very simple minded check here using the (standard) family field.
+ ; While here, we also check for PAE.
+ ;
+ mov eax, 1
+ cpuid
+
+ ; Calc the extended family and model values before we mess up EAX.
+ mov cl, ah
+ and cl, 0fh
+ cmp cl, 0fh
+ jnz .not_extended_family
+ mov ecx, eax
+ shr ecx, 20
+ and cl, 7fh
+ add cl, 0fh
+.not_extended_family: ; cl = family
+ mov ch, al
+ shr ch, 4
+ cmp cl, 0fh
+ jae .extended_model
+ cmp cl, 06h ; actually only intel, but we'll let this slip for now.
+ jne .done_model
+.extended_model:
+ shr eax, 12
+ and al, 0f0h
+ or ch, al
+.done_model: ; ch = model
+
+ ; Start assembling return flags, checking for PSE + PAE.
+ mov eax, X86_CPUID_FEATURE_EDX_PSE | X86_CPUID_FEATURE_EDX_PAE
+ and eax, edx
+ mov ah, al
+ AssertCompile(X86_CPUID_FEATURE_EDX_PAE_BIT > BS3CPU_F_PAE_BIT - 8) ; 6 vs 10-8=2
+ and al, X86_CPUID_FEATURE_EDX_PAE
+ shr al, X86_CPUID_FEATURE_EDX_PAE_BIT - (BS3CPU_F_PAE_BIT - 8)
+ AssertCompile(X86_CPUID_FEATURE_EDX_PSE_BIT == BS3CPU_F_PSE_BIT - 8) ; 3 vs 11-8=3
+ and ah, X86_CPUID_FEATURE_EDX_PSE
+ or ah, al
+ or ah, (BS3CPU_F_CPUID >> 8)
+
+ ; Add the CPU type based on the family and model values.
+ cmp cl, 6
+ jne .not_family_06h
+ mov al, BS3CPU_PPro
+ cmp ch, 1
+ jbe .return
+ mov al, BS3CPU_PProOrNewer
+ jmp .NewerThanPPro
+
+.not_family_06h:
+ mov al, BS3CPU_PProOrNewer
+ ja .NewerThanPPro
+ cmp cl, 5
+ mov al, BS3CPU_Pentium
+ je .return
+ cmp cl, 4
+ mov al, BS3CPU_80486
+ je .return
+ cmp cl, 3
+ mov al, BS3CPU_80386
+ je .return
+
+.NewerThanPPro:
+
+ ; Check for extended leaves and long mode.
+ push xAX ; save PAE+PProOrNewer
+ mov eax, 0x80000000
+ cpuid
+ sub eax, 0x80000001 ; Minimum leaf 0x80000001
+ cmp eax, 0x00010000 ; At most 0x10000 leaves.
+ ja .no_ext_leaves
+
+ mov eax, 0x80000001
+ cpuid
+ pop xAX ; restore PAE+PProOrNewer
+ test edx, X86_CPUID_EXT_FEATURE_EDX_LONG_MODE
+ jz .no_long_mode
+ or ah, ((BS3CPU_F_CPUID_EXT_LEAVES | BS3CPU_F_LONG_MODE) >> 8)
+ jmp .no_check_for_nx
+.no_long_mode:
+ or ah, (BS3CPU_F_CPUID_EXT_LEAVES >> 8)
+.no_check_for_nx:
+ test edx, X86_CPUID_EXT_FEATURE_EDX_NX
+ jz .return
+ or ax, BS3CPU_F_NX
+ jmp .return
+
+.no_ext_leaves:
+ pop xAX ; restore PAE+PProOrNewer
+
+CPU 8086
+.return:
+ ;
+ ; Save the return value.
+ ;
+ mov [BS3_DATA16_WRT(g_uBs3CpuDetected)], ax
+
+ ;
+ ; Epilogue.
+ ;
+ add xSP, 20h
+ pop xBX
+ pop xDX
+ pop xCX
+ popf
+ pop xBP
+ BS3_HYBRID_RET
+
+BS3_PROC_END_MODE Bs3CpuDetect
+
+
+%if TMPL_MODE == BS3_MODE_RM
+BS3_BEGIN_TEXT16_NEARSTUBS
+BS3_PROC_BEGIN_MODE Bs3CpuDetect, BS3_PBC_NEAR
+ call far TMPL_FAR_NM(Bs3CpuDetect)
+ ret
+BS3_PROC_END_MODE Bs3CpuDetect
+%endif
+