; Filename: CPUID3A.ASM ; Copyright (c) Intel Corporation 1993-2009 ; ; This program has been developed by Intel Corporation. Intel ; has various intellectual property rights which it may assert ; under certain circumstances, such as if another ; manufacturer’s processor mis-identifies itself as being ; “GenuineIntel” when the CPUID instruction is executed. ; ; Intel specifically disclaims all warranties, express or ; implied, and all liability, including consequential and other ; indirect damages, for the use of this program, including ; liability for infringement of any proprietary rights, ; and including the warranties of merchantability and fitness ; for a particular purpose. Intel does not assume any ; responsibility for any errors which may appear in this program ; nor any responsibility to update it. ; ;**************************************************************** ; ; This code contains two procedures: ; _get_cpu_type: Identifies processor type in _cpu_type: ; 0=8086/8088 processor ; 2=Intel 286 processor ; 3=Intel386(TM) family processor ; 4=Intel486(TM) family processor ; 5=Pentium(R) family processor ; 6=P6 family of processors ; F=Pentium 4 family of processors ; ; _get_fpu_type: Identifies FPU type in _fpu_type: ; 0=FPU not present ; 1=FPU present ; 2=287 present (only if cpu_type=3) ; 3=387 present (only if cpu_type=3) ; ;**************************************************************** ; ; This program has been compiled with Microsoft Macro Assembler ; 6.15. If this code is compiled with no options specified and ; linked with CPUID3.C or CPUID3B.ASM, it’s assumed to correctly ; identify the current Intel 8086/8088, 80286, 80386, 80486, ; Pentium(R), Pentium(R) Pro, Pentium(R) II, Pentium(R) II ; Xeon(R), Pentium(R) II OverDrive(R), Intel(R) Celeron(R), ; Pentium(R) III, Pentium(R) III Xeon(R), Pentium(R) 4, Intel(R) ; Xeon(R) DP and MP, Intel(R) Core(TM), Intel(R) Core(TM) 2, ; Intel(R) Core(TM) i7, and Intel(R) Atom(TM) processors when ; executed in real-address mode. ; ; NOTE: When using this code with C program CPUID3.C, 32-bit ; segments are recommended. ; ;**************************************************************** TITLE CPUID3A ; comment the following line for 32-bit segments .DOSSEG ; uncomment the following 2 lines for 32-bit segments ;.386 ;.MODEL flat ; comment the following line for 32-bit segments .MODEL small CPU_ID MACRO db 0Fh ; CPUID opcodes db 0A2h ENDM .DATA public _cpu_type public _fpu_type public _v86_flag public _cpuid_flag public _intel_CPU public _max_funct public _vendor_id public _cpu_signature public _features_ebx public _features_ecx public _features_edx public _funct_5_eax public _funct_5_ebx public _funct_5_ecx public _funct_5_edx public _funct_6_eax public _funct_6_ebx public _funct_6_ecx public _funct_6_edx public _funct_A_eax public _funct_A_ebx public _funct_A_ecx public _funct_A_edx public _ext_max_funct public _ext_funct_1_eax public _ext_funct_1_ebx public _ext_funct_1_ecx public _ext_funct_1_edx public _ext_funct_6_eax public _ext_funct_6_ebx public _ext_funct_6_ecx public _ext_funct_6_edx public _ext_funct_7_eax public _ext_funct_7_ebx public _ext_funct_7_ecx public _ext_funct_7_edx public _ext_funct_8_eax public _ext_funct_8_ebx public _ext_funct_8_ecx public _ext_funct_8_edx public _cache_eax public _cache_ebx public _cache_ecx public _cache_edx public _dcp_cache_eax public _dcp_cache_ebx public _dcp_cache_ecx public _dcp_cache_edx public _brand_string _cpu_type db 0 _fpu_type db 0 _v86_flag db 0 _cpuid_flag db 0 _intel_CPU db 0 _max_funct dd 0 ; Maximum function from CPUID.0.EAX _vendor_id db “------------” intel_id db “GenuineIntel” _cpu_signature dd 0 ; CPUID.1.EAX _features_ebx dd 0 ; CPUID.1.EBX _features_ecx dd 0 ; CPUID.1.ECX - feature flags _features_edx dd 0 ; CPUID.1.EDX - feature flags _funct_5_eax dd 0 ; CPUID.5.EAX - monitor/mwait leaf _funct_5_ebx dd 0 ; CPUID.5.EBX - monitor/mwait leaf _funct_5_ecx dd 0 ; CPUID.5.ECX - monitor/mwait leaf _funct_5_edx dd 0 ; CPUID.5.EDX - monitor/mwait leaf _funct_6_eax dd 0 ; CPUID.6.EAX - sensor & power mgmt. flags _funct_6_ebx dd 0 ; CPUID.6.EBX - sensor & power mgmt. flags _funct_6_ecx dd 0 ; CPUID.6.ECX - sensor & power mgmt. flags _funct_6_edx dd 0 ; CPUID.6.EDX - sensor & power mgmt. flags _funct_A_eax dd 0 ; CPUID.A.EAX _funct_A_ebx dd 0 ; CPUID.A.EBX _funct_A_ecx dd 0 ; CPUID.A.ECX _funct_A_edx dd 0 ; CPUID.A.EDX _ext_max_funct dd 0 ; Max ext leaf from CPUID.80000000h.EAX _ext_funct_1_eax dd 0 _ext_funct_1_ebx dd 0 _ext_funct_1_ecx dd 0 _ext_funct_1_edx dd 0 _ext_funct_6_eax dd 0 _ext_funct_6_ebx dd 0 _ext_funct_6_ecx dd 0 _ext_funct_6_edx dd 0 _ext_funct_7_eax dd 0 _ext_funct_7_ebx dd 0 _ext_funct_7_ecx dd 0 _ext_funct_7_edx dd 0 _ext_funct_8_eax dd 0 _ext_funct_8_ebx dd 0 _ext_funct_8_ecx dd 0 _ext_funct_8_edx dd 0 _cache_eax dd 0 _cache_ebx dd 0 _cache_ecx dd 0 _cache_edx dd 0 _dcp_cache_eax dd 0 _dcp_cache_ebx dd 0 _dcp_cache_ecx dd 0 _dcp_cache_edx dd 0 _brand_string db 48 dup (0) fp_status dw 0 CPUID_regs struct regEax dd 0 regEbx dd 0 regEcx dd 0 regEdx dd 0 CPUID_regs ends .CODE .586 ;****************************************************************************** ; _cpuidEx ; Input: SP+2 – EAX value to set ; SP+6 – ECX value to set ; SP+10 – offset of CPUID_regs structure ;****************************************************************************** _cpuidEx proc public push bp ; .small model causes proc call to push IP onto stack, mov bp, sp ; so parameters passed to proc start at SP+4 after PUSH BP pushad mov eax, [bp+4] ; get EAX from stack mov ecx, [bp+8] ; get ECX from stack cpuid mov si, [bp+12] ; get offset of CPUID_regs structure mov dword ptr [si].CPUID_regs.regEax, eax mov dword ptr [si].CPUID_regs.regEbx, ebx mov dword ptr [si].CPUID_regs.regEcx, ecx mov dword ptr [si].CPUID_regs.regEdx, edx popad pop bp ret _cpuidEx endp ; comment the following line for 32-bit segments .8086 ; uncomment the following line for 32-bit segments ;.386 ;********************************************************************* _get_cpu_type proc public ; This procedure determines the type of processor in a system ; and sets the _cpu_type variable with the appropriate ; value. If the CPUID instruction is available, it is used ; to determine more specific details about the processor. ; All registers are used by this procedure, none are preserved. ; To avoid AC faults, the AM bit in CR0 must not be set. ; Intel 8086 processor check ; Bits 12-15 of the FLAGS register are always set on the ; 8086 processor. ; ; For 32-bit segments comment the following lines down to the next ; comment line that says “STOP” ; check_8086: pushf ; push original FLAGS pop ax ; get original FLAGS mov cx, ax ; save original FLAGS and ax, 0FFFh ; clear bits 12-15 in FLAGS push ax ; save new FLAGS value on stack popf ; replace current FLAGS value pushf ; get new FLAGS pop ax ; store new FLAGS in AX and ax, 0F000h ; if bits 12-15 are set, then cmp ax, 0F000h ; processor is an 8086/8088 mov _cpu_type, 0 ; turn on 8086/8088 flag jne check_80286 ; go check for 80286 push sp ; double check with push sp pop dx ; if value pushed was different cmp dx, sp ; means it’s really an 8086 jne end_cpu_type ; jump if processor is 8086/8088 mov _cpu_type, 10h ; indicate unknown processor jmp end_cpu_type ; Intel 286 processor check ; Bits 12-15 of the FLAGS register are always clear on the ; Intel 286 processor in real-address mode. .286 check_80286: smsw ax ; save machine status word and ax, 1 ; isolate PE bit of MSW mov _v86_flag, al ; save PE bit to indicate V86 or cx, 0F000h ; try to set bits 12-15 push cx ; save new FLAGS value on stack popf ; replace current FLAGS value pushf ; get new FLAGS pop ax ; store new FLAGS in AX and ax, 0F000h ; if bits 12-15 are clear mov _cpu_type, 2 ; processor=80286, turn on 80286 flag jz end_cpu_type ; jump if processor is 80286 ; Intel386 processor check ; The AC bit, bit #18, is a new bit introduced in the EFLAGS ; register on the Intel486 processor to generate alignment ; faults. ; This bit cannot be set on the Intel386 processor. .386 ; “STOP” ; ; it is safe to use 386 instructions check_80386: pushfd ; push original EFLAGS pop eax ; get original EFLAGS mov ecx, eax ; save original EFLAGS xor eax, 40000h ; flip AC bit in EFLAGS push eax ; save new EFLAGS value on stack popfd ; replace current EFLAGS value pushfd ; get new EFLAGS pop eax ; store new EFLAGS in EAX xor eax, ecx ; can’t toggle AC bit processor=80386 mov _cpu_type, 3 ; turn on 80386 processor flag jz end_cpu_type ; jump if 80386 processor push ecx popfd ; restore AC bit in EFLAGS first ; Intel486 processor check ; Checking for ability to set/clear ID flag (Bit 21) in EFLAGS ; which indicates the presence of a processor with the CPUID ; instruction. .486 check_80486: mov _cpu_type, 4 ; turn on 80486 processor flag mov eax, ecx ; get original EFLAGS xor eax, 200000h ; flip ID bit in EFLAGS push eax ; save new EFLAGS value on stack popfd ; replace current EFLAGS value pushfd ; get new EFLAGS pop eax ; store new EFLAGS in EAX xor eax, ecx ; can’t toggle ID bit, je end_cpu_type ; processor=80486 ; Execute CPUID instruction to determine vendor, family, ; model, stepping and features. For the purpose of this ; code, only the initial set of CPUID information is saved. .586 mov _cpuid_flag, 1 ; flag indicating use of CPUID inst. push ebx ; save registers push esi push edi xor eax, eax ; get Vendor ID cpuid mov _max_funct, eax mov dword ptr _vendor_id, ebx mov dword ptr _vendor_id[4], edx mov dword ptr _vendor_id[8], ecx cmp dword ptr intel_id, ebx jne end_cpuid_type cmp dword ptr intel_id[4], edx jne end_cpuid_type cmp dword ptr intel_id[8], ecx jne end_cpuid_type ; if not equal, not an Intel processor mov _intel_CPU, 1 ; indicate an Intel processor cmp eax, 1 ; is 1 valid input for CPUID? jb ext_functions ; jmp if no mov eax, 1 ; get family/model/stepping/features cpuid mov _cpu_signature, eax mov _features_ebx, ebx mov _features_ecx, ecx mov _features_edx, edx shr eax, 8 ; isolate family and eax, 0Fh mov _cpu_type, al ; set _cpu_type with family cmp _max_funct, 2 ; is 2 valid input for CPUID? jb ext_functions ; jmp if no mov eax, 2 ; set up to read cache descriptor cpuid ; This code supports one iteration only mov _cache_eax, eax mov _cache_ebx, ebx mov _cache_ecx, ecx mov _cache_edx, edx cmp _max_funct, 4 ; is 4 valid input for CPUID? jb ext_functions ; jmp if no mov eax, 4 ; set up to read deterministic cache params xor ecx, ecx cpuid push eax and al, 1Fh ; determine if valid cache parameters read cmp al, 00h ; EAX[4:0] = 0 indicates invalid cache pop eax je cpuid_5 mov _dcp_cache_eax, eax ; store deterministic cache information mov _dcp_cache_ebx, ebx mov _dcp_cache_ecx, ecx mov _dcp_cache_edx, edx cpuid_5: cmp _max_funct, 5 ; is 5 valid input for CPUID? jb ext_functions ; jmp if no mov eax, 5 cpuid mov _funct_5_eax, eax mov _funct_5_ebx, ebx mov _funct_5_ecx, ecx mov _funct_5_edx, edx cmp _max_funct, 6 ; is 6 valid input for CPUID? jb ext_functions ; jmp if no mov eax, 6 cpuid mov _funct_6_eax, eax mov _funct_6_ebx, ebx mov _funct_6_ecx, ecx mov _funct_6_edx, edx cmp _max_funct, 0Ah ; is 0Ah valid input for CPUID? jb ext_functions ; jmp if no mov eax, 0Ah cpuid mov _funct_A_eax, eax mov _funct_A_ebx, ebx mov _funct_A_ecx, ecx mov _funct_A_edx, edx ext_functions: mov eax, 80000000h ; check if brand string is supported cpuid mov _ext_max_funct, eax cmp _ext_max_funct, 80000001h ; is 80000001h valid input for CPUID? jb end_cpuid_type ; jmp if no mov eax, 80000001h ; Get the Extended Feature Flags cpuid mov _ext_funct_1_eax, eax mov _ext_funct_1_ebx, ebx mov _ext_funct_1_ecx, ecx mov _ext_funct_1_edx, edx cmp _ext_max_funct, 80000002h ; is 80000002h valid input for CPUID? jb end_cpuid_type ; jmp if no mov di, offset _brand_string mov eax, 80000002h ; get first 16 bytes of brand string cpuid mov dword ptr [di], eax ; save bytes 0-15 mov dword ptr [di+4], ebx mov dword ptr [di+8], ecx mov dword ptr [di+12], edx add di, 16 mov eax, 80000003h cpuid mov dword ptr [di], eax ; save bytes 16-31 mov dword ptr [di+4], ebx mov dword ptr [di+8], ecx mov dword ptr [di+12], edx add di, 16 mov eax, 80000004h cpuid mov dword ptr [di], eax ; save bytes 32-47 mov dword ptr [di+4], ebx mov dword ptr [di+8], ecx mov dword ptr [di+12], edx cmp _ext_max_funct, 80000006h ; is 80000006h valid input for CPUID? jb end_cpuid_type ; jmp if no mov eax, 80000006h cpuid mov _ext_funct_6_eax, eax mov _ext_funct_6_ebx, ebx mov _ext_funct_6_ecx, ecx mov _ext_funct_6_edx, edx cmp _ext_max_funct, 80000007h ; is 80000007h valid input for CPUID? jb end_cpuid_type ; jmp if no mov eax, 80000007h cpuid mov _ext_funct_7_eax, eax mov _ext_funct_7_ebx, ebx mov _ext_funct_7_ecx, ecx mov _ext_funct_7_edx, edx cmp _ext_max_funct, 80000008h ; is 80000008h valid input for CPUID? jb end_cpuid_type ; jmp if no mov eax, 80000008h cpuid mov _ext_funct_8_eax, eax mov _ext_funct_8_ebx, ebx mov _ext_funct_8_ecx, ecx mov _ext_funct_8_edx, edx end_cpuid_type: pop edi ; restore registers pop esi pop ebx ; comment the following line for 32-bit segments .8086 end_cpu_type: ret _get_cpu_type endp ;********************************************************************* _get_fpu_type proc public ; This procedure determines the type of FPU in a system ; and sets the _fpu_type variable with the appropriate value. ; All registers are used by this procedure, none are preserved. ; Coprocessor check ; The algorithm is to determine whether the floating-point ; status and control words are present. If not, no ; coprocessor exists. If the status and control words can ; be saved, the correct coprocessor is then determined ; depending on the processor type. The Intel386 processor can ; work with either an Intel287 NDP or an Intel387 NDP. ; The infinity of the coprocessor must be checked to determine ; the correct coprocessor type. fninit ; reset FP status word mov fp_status, 5A5Ah ; initialize temp word to non-zero fnstsw fp_status ; save FP status word mov ax, fp_status ; check FP status word cmp al, 0 ; was correct status written mov _fpu_type, 0 ; no FPU present jne end_fpu_type check_control_word: fnstcw fp_status ; save FP control word mov ax, fp_status ; check FP control word and ax, 103fh ; selected parts to examine cmp ax, 3fh ; was control word correct mov _fpu_type, 0 jne end_fpu_type ; incorrect control word, no FPU mov _fpu_type, 1 ; 80287/80387 check for the Intel386 processor check_infinity: cmp _cpu_type, 3 jne end_fpu_type fld1 ; must use default control from FNINIT fldz ; form infinity fdiv ; 8087/Intel287 NDP say +inf = -inf fld st ; form negative infinity fchs ; Intel387 NDP says +inf <> -inf fcompp ; see if they are the same fstsw fp_status ; look at status from FCOMPP mov ax, fp_status mov _fpu_type, 2 ; store Intel287 NDP for FPU type sahf ; see if infinities matched jz end_fpu_type ; jump if 8087 or Intel287 is present mov _fpu_type, 3 ; store Intel387 NDP for FPU type end_fpu_type: ret _get_fpu_type endp end