summaryrefslogtreecommitdiffstats
path: root/c/src/lib/libcpu/i386/cpuModel.S
diff options
context:
space:
mode:
authorJoel Sherrill <joel.sherrill@OARcorp.com>1998-08-05 15:15:46 +0000
committerJoel Sherrill <joel.sherrill@OARcorp.com>1998-08-05 15:15:46 +0000
commitab0df696d09f6b53b33345d207f8aead63a6fcab (patch)
treee446f792382e49b1169482837cd842a82fb2350b /c/src/lib/libcpu/i386/cpuModel.S
parentFixed name of Buffer so this would compile. (diff)
downloadrtems-ab0df696d09f6b53b33345d207f8aead63a6fcab.tar.bz2
Automatic CPU type detection code from Eric Valette <valette@crf.canon.fr>.
Enabled on the pc386.
Diffstat (limited to 'c/src/lib/libcpu/i386/cpuModel.S')
-rw-r--r--c/src/lib/libcpu/i386/cpuModel.S255
1 files changed, 255 insertions, 0 deletions
diff --git a/c/src/lib/libcpu/i386/cpuModel.S b/c/src/lib/libcpu/i386/cpuModel.S
new file mode 100644
index 0000000000..aaace8af9f
--- /dev/null
+++ b/c/src/lib/libcpu/i386/cpuModel.S
@@ -0,0 +1,255 @@
+/* cpuModel.S
+ *
+ * This file contains all assembly code for the Intel Cpu identification.
+ * It is based on linux cpu detection code.
+ *
+ * Intel also provides public similar code in the book
+ * called :
+ *
+ * Pentium Processor Family
+ * Developer Family
+ * Volume 3 : Architecture and Programming Manual
+ *
+ * At the following place :
+ *
+ * Chapter 5 : Feature determination
+ * Chapter 25: CPUID instruction
+ *
+ * COPYRIGHT (c) 1998 valette@crf.canon.fr
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.OARcorp.com/rtems/license.html.
+ *
+ * $Id$
+ */
+
+#include <asm.h>
+#include <libcpu/registers.h>
+
+BEGIN_CODE
+ PUBLIC(checkCPUtypeSetCr0);
+/*
+ * check Processor type: 386, 486, 6x86(L) or CPUID capable processor
+ */
+
+SYM (checkCPUtypeSetCr0):
+ /*
+ * Assume 386 for now
+ */
+ movl $3, SYM (x86)
+ /*
+ * Start using the EFAGS AC bit determination method described in
+ * the book mentionned above page 5.1. If this bit can be set we
+ * have a 486 or above.
+ */
+ pushfl /* save EFLAGS */
+
+ pushfl /* Get EFLAGS in EAX */
+ popl eax
+
+ movl eax,ecx /* save original EFLAGS in ECX */
+ xorl $EFLAGS_ALIGN_CHECK,eax /* flip AC bit in EAX */
+ pushl eax /* set EAX as EFLAGS */
+ popfl
+ pushfl /* Get new EFLAGS in EAX */
+ popl eax
+
+ xorl ecx,eax /* check if AC bit changed */
+ andl $EFLAGS_ALIGN_CHECK,eax
+ je is386 /* If not : we have a 386 */
+ /*
+ * Assume 486 for now
+ */
+ movl $4,SYM (x86)
+ movl ecx,eax /* Restore orig EFLAGS in EAX */
+ xorl $EFLAGS_ID,eax /* flip ID flag */
+ pushl eax /* set EAX as EFLAGS */
+ popfl
+ pushfl /* Get new EFLAGS in EAX */
+ popl eax
+
+ xorl ecx,eax /* check if ID bit changed */
+ andl $EFLAGS_ID,eax
+
+ /*
+ * if we are on a straight 486DX,
+ * SX, or 487SX we can't change it
+ * OTOH 6x86MXs and MIIs check OK
+ * Also if we are on a Cyrix 6x86(L)
+ */
+ je is486x
+
+isnew:
+ /*
+ * restore original EFLAGS
+ */
+ popfl
+ incl SYM(have_cpuid) /* we have CPUID instruction */
+
+ /* use it to get :
+ * processor type,
+ * processor model,
+ * processor mask,
+ * by using it with EAX = 1
+ */
+ movl $1, eax
+ cpuid
+
+ movb al, cl /* save reg for future use */
+
+ andb $0x0f,ah /* mask processor family */
+ movb ah,SYM (x86) /* put result in x86 var */
+
+ andb $0xf0, al /* get model */
+ shrb $4, al
+ movb al,SYM (x86_model) /* store it in x86_model */
+
+ andb $0x0f, cl /* get mask revision */
+ movb cl,SYM (x86_mask) /* store it in x86_mask */
+
+ movl edx,SYM(x86_capability) /* store feature flags in x86_capability */
+
+ /* get vendor info by using CPUID with EXA = 0 */
+ xorl eax, eax
+ cpuid
+
+ /*
+ * store results contained in ebx, edx, ecx in
+ * x86_vendor_id variable.
+ */
+ movl ebx,SYM(x86_vendor_id)
+ movl edx,SYM(x86_vendor_id)+4
+ movl ecx,SYM(x86_vendor_id)+8
+
+ movl cr0,eax /* 486+ */
+ andl $(CR0_PAGING | CR0_PROTECTION_ENABLE | CR0_EXTENSION_TYPE), eax
+ orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax
+ jmp 2f
+
+/* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid
+ * clobbering the new BX chipset used with the Pentium II, which has a register
+ * at the same addresses as those used to access the Cyrix special configuration
+ * registers (CCRs).
+ */
+ /*
+ * A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2
+ * (and it _must_ be 5 divided by 2) while other CPUs change
+ * them in undefined ways. We need to know this since we may
+ * need to enable the CPUID instruction at least.
+ * We couldn't use this test before since the PPro and PII behave
+ * like Cyrix chips in this respect.
+ */
+is486x: xor ax,ax
+ sahf
+ movb $5,ax
+ movb $2,bx
+ div bl
+ lahf
+ cmpb $2,ah
+ jne ncyrix
+ /*
+ * N.B. The pattern of accesses to 0x22 and 0x23 is *essential*
+ * so do not try to "optimize" it! For the same reason we
+ * do all this with interrupts off.
+ */
+#define setCx86(reg, val) \
+ movb reg,ax; \
+ outb ax,$0x22; \
+ movb val,ax; \
+ outb ax,$0x23
+
+#define getCx86(reg) \
+ movb reg,ax; \
+ outb ax,$0x22; \
+ inb $0x23,ax
+
+ cli
+ getCx86($0xc3) /* get CCR3 */
+ movb ax,cx /* Save old value */
+ movb ax,bx
+ andb $0x0f,bx /* Enable access to all config registers */
+ orb $0x10,bx /* by setting bit 4 */
+ setCx86($0xc3,bx)
+
+ getCx86($0xe8) /* now we can get CCR4 */
+ orb $0x80,ax /* and set bit 7 (CPUIDEN) */
+ movb ax,bx /* to enable CPUID execution */
+ setCx86($0xe8,bx)
+
+ getCx86($0xfe) /* DIR0 : let's check this is a 6x86(L) */
+ andb $0xf0,ax /* should be 3xh */
+ cmpb $0x30,ax
+ jne n6x86
+ getCx86($0xe9) /* CCR5 : we reset the SLOP bit */
+ andb $0xfd,ax /* so that udelay calculation */
+ movb ax,bx /* is correct on 6x86(L) CPUs */
+ setCx86($0xe9,bx)
+ setCx86($0xc3,cx) /* Restore old CCR3 */
+ sti
+ jmp isnew /* We enabled CPUID now */
+
+n6x86: setCx86($0xc3,cx) /* Restore old CCR3 */
+ sti
+ncyrix: /* restore original EFLAGS */
+ popfl
+ movl cr0,eax /* 486 */
+ andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax /* Save PG,PE,ET */
+ orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax /* set AM, WP, NE and MP */
+ jmp 2f
+is386: /* restore original EFLAGS */
+ popfl
+ movl cr0,eax /* 386 */
+ andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax /* Save PG,PE,ET */
+ orl $CR0_MONITOR_COPROC,eax /* set MP */
+2: movl eax,cr0
+ call check_x87
+ ret
+
+
+/*
+ * We depend on ET to be correct. This checks for 287/387.
+ */
+check_x87:
+ movb $0,SYM(hard_math)
+ clts
+ fninit
+ fstsw ax
+ cmpb $0,al
+ je 1f
+ movl cr0,eax /* no coprocessor: have to set bits */
+ xorl $4,eax /* set EM */
+ movl eax,cr0
+ ret
+ .align 16
+1: movb $1,SYM(hard_math)
+ .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
+ ret
+
+END_CODE
+
+BEGIN_DATA
+ PUBLIC(x86)
+ PUBLIC(have_cpuid)
+ PUBLIC(x86_model)
+ PUBLIC(x86_mask)
+ PUBLIC(x86_capability)
+ PUBLIC(x86_vendor_id)
+ PUBLIC(hard_math)
+
+SYM(x86):
+ .byte 0
+SYM(have_cpuid):
+ .long 0
+SYM(x86_model):
+ .byte 0
+SYM(x86_mask):
+ .byte 0
+SYM(x86_capability):
+ .long 0
+SYM(x86_vendor_id):
+ .zero 13
+SYM(hard_math):
+ .byte 0
+END_DATA
+