Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:41

0001 /*  cpuModel.S
0002  *
0003  *  This file contains all assembly code for the Intel Cpu identification.
0004  *  It is based on linux cpu detection code.
0005  *
0006  *  Intel also provides public similar code in the book
0007  *  called :
0008  *
0009  *  Pentium Processor Family
0010  *      Developer Family
0011  *  Volume  3 : Architecture and Programming Manual
0012  *
0013  * At the following place :
0014  *
0015  *  Chapter 5 : Feature determination
0016  *  Chapter 25: CPUID instruction
0017  *
0018  *  COPYRIGHT (c) 1998 valette@crf.canon.fr
0019  *
0020  *  The license and distribution terms for this file may be
0021  *  found in the file LICENSE in this distribution or at
0022  *  http://www.rtems.org/license/LICENSE.
0023  */
0024 
0025 #include <rtems/asm.h>
0026 #include <rtems/score/registers.h>
0027 
0028 BEGIN_CODE
0029     PUBLIC(checkCPUtypeSetCr0);
0030 /*
0031  * check Processor type: 386, 486, 6x86(L) or CPUID capable processor
0032  */
0033 
0034 SYM (checkCPUtypeSetCr0):
0035     /*
0036      *  Assume 386 for now
0037      */
0038     movl $3, SYM (x86)
0039     /*
0040      * Start using the EFLAGS AC bit determination method described in
0041      * the book mentioned above page 5.1. If this bit can be set we
0042      * have a 486 or above.
0043      */
0044     pushfl              /* save EFLAGS          */
0045 
0046     pushfl              /* Get EFLAGS in EAX        */
0047     popl eax
0048 
0049     movl eax,ecx            /* save original EFLAGS in ECX  */
0050     xorl $EFLAGS_ALIGN_CHECK,eax    /* flip AC bit in EAX       */
0051     pushl eax           /* set EAX as EFLAGS        */
0052     popfl
0053     pushfl              /* Get new EFLAGS in EAX    */
0054     popl eax
0055 
0056     xorl ecx,eax            /* check if AC bit changed  */
0057     andl $EFLAGS_ALIGN_CHECK,eax
0058     je is386            /* If not : we have a 386   */
0059     /*
0060      *  Assume 486 for now
0061      */
0062     movl $4,SYM (x86)
0063     movl ecx,eax            /* Restore orig EFLAGS in EAX   */
0064     xorl $EFLAGS_ID,eax     /* flip  ID flag        */
0065     pushl eax           /* set EAX as EFLAGS        */
0066     popfl
0067     pushfl              /* Get new EFLAGS in EAX    */
0068     popl eax
0069 
0070     xorl ecx,eax            /* check if ID bit changed  */
0071     andl $EFLAGS_ID,eax
0072 
0073     /*
0074      * if we are on a straight 486DX,
0075      * SX, or 487SX we can't change it
0076      * OTOH 6x86MXs and MIIs check OK
0077      * Also if we are on a Cyrix 6x86(L)
0078      */
0079     je is486x
0080 
0081 isnew:
0082     /*
0083      * restore original EFLAGS
0084      */
0085     popfl
0086     incl SYM(have_cpuid)    /* we have CPUID instruction */
0087 
0088     /*
0089      * Addressable Processor Ids
0090      *
0091      * CPUID.(EAX=4, ECX=0):EAX[31:26] + 1 = Y)
0092      */
0093     movl $4, eax
0094     movl $0, ecx
0095     cpuid
0096     movl eax,SYM(x86_capability_cores)
0097 
0098     /* use it to get :
0099      *  processor type,
0100      *  processor model,
0101      *  processor mask,
0102      * by using it with EAX = 1
0103      */
0104     movl $1, eax
0105     cpuid
0106     movl ebx,SYM(x86_capability_ebx) /* store ebx feature info */
0107     movl ecx,SYM(x86_capability_x) /* store ecx feature flags */
0108 
0109     movb al, cl     /* save reg for future use */
0110 
0111     andb $0x0f,ah       /* mask processor family   */
0112     movb ah,SYM (x86)   /* put result in x86 var   */
0113 
0114     andb $0xf0, al      /* get model           */
0115     shrb $4, al
0116     movb al,SYM (x86_model) /* store it in x86_model   */
0117 
0118     andb $0x0f, cl      /* get mask revision       */
0119     movb cl,SYM (x86_mask)  /* store it in x86_mask    */
0120 
0121     movl edx,SYM(x86_capability)    /* store feature flags in x86_capability */
0122 
0123     /* get vendor info by using CPUID with EXA = 0 */
0124     xorl eax, eax
0125     cpuid
0126 
0127     /*
0128      * store results contained in ebx, edx, ecx in
0129      * x86_vendor_id variable.
0130      */
0131     movl ebx,SYM(x86_vendor_id)
0132     movl edx,SYM(x86_vendor_id)+4
0133     movl ecx,SYM(x86_vendor_id)+8
0134 
0135     movl cr0,eax        /* 486+ */
0136     andl $(CR0_PAGING | CR0_PROTECTION_ENABLE | CR0_EXTENSION_TYPE), eax
0137     orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax
0138     jmp 2f
0139 
0140 /* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid
0141  * clobbering the new BX chipset used with the Pentium II, which has a register
0142  * at the same addresses as those used to access the Cyrix special configuration
0143  * registers (CCRs).
0144  */
0145     /*
0146      * A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2
0147      * (and it _must_ be 5 divided by 2) while other CPUs change
0148      * them in undefined ways. We need to know this since we may
0149      * need to enable the CPUID instruction at least.
0150      * We couldn't use this test before since the PPro and PII behave
0151      * like Cyrix chips in this respect.
0152      */
0153 is486x: xor ax,ax
0154     sahf
0155     movb $5,al
0156     movb $2,bl
0157     div bl
0158     lahf
0159     cmpb $2,ah
0160     jne ncyrix
0161     /*
0162      * N.B. The pattern of accesses to 0x22 and 0x23 is *essential*
0163      *      so do not try to "optimize" it! For the same reason we
0164      *  do all this with interrupts off.
0165      */
0166 #define setCx86(reg, val) \
0167     movb reg,al;    \
0168     outb al,$0x22;  \
0169     movb val,al;    \
0170     outb al,$0x23
0171 
0172 #define getCx86(reg) \
0173     movb reg,al;    \
0174     outb al,$0x22;  \
0175     inb $0x23,al
0176 
0177     cli
0178     getCx86($0xc3)      /*  get CCR3 */
0179     movb al,cl      /* Save old value */
0180     movb al,bl
0181     andb $0x0f,bl       /* Enable access to all config registers */
0182     orb $0x10,bl        /* by setting bit 4 */
0183     setCx86($0xc3,bl)
0184 
0185     getCx86($0xe8)      /* now we can get CCR4 */
0186     orb $0x80,al        /* and set bit 7 (CPUIDEN) */
0187     movb al,bl      /* to enable CPUID execution */
0188     setCx86($0xe8,bl)
0189 
0190         getCx86($0xfe)          /* DIR0 : let's check this is a 6x86(L) */
0191         andb $0xf0,al       /* should be 3xh */
0192     cmpb $0x30,al
0193     jne n6x86
0194         getCx86($0xe9)          /* CCR5 : we reset the SLOP bit */
0195         andb $0xfd,al       /* so that udelay calculation */
0196         movb al,bl      /* is correct on 6x86(L) CPUs */
0197         setCx86($0xe9,bl)
0198     setCx86($0xc3,cl)   /* Restore old CCR3 */
0199     sti
0200     jmp isnew       /* We enabled CPUID now */
0201 
0202 n6x86:  setCx86($0xc3,cl)   /* Restore old CCR3 */
0203     sti
0204 ncyrix:             /* restore original EFLAGS */
0205     popfl
0206     movl cr0,eax        /* 486 */
0207     andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax /* Save PG,PE,ET */
0208     orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax   /* set AM, WP, NE and MP */
0209     jmp 2f
0210 is386:              /* restore original EFLAGS */
0211     popfl
0212     movl cr0,eax        /* 386 */
0213     andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax /* Save PG,PE,ET */
0214     orl $CR0_MONITOR_COPROC,eax     /* set MP */
0215 2:  movl eax,cr0
0216     call check_x87
0217     ret
0218 
0219 
0220 /*
0221  * We depend on ET to be correct. This checks for 287/387.
0222  */
0223 check_x87:
0224     movb $0,SYM(hard_math)
0225     clts
0226     fninit
0227     fstsw ax
0228     cmpb $0,al
0229     je 1f
0230     movl cr0,eax        /* no coprocessor: have to set bits */
0231     xorl $4,eax     /* set EM */
0232     movl eax,cr0
0233     ret
0234     .align 16
0235 1:  movb $1,SYM(hard_math)
0236     .byte 0xDB,0xE4     /* fsetpm for 287, ignored by 387 */
0237     ret
0238 
0239 END_CODE
0240 
0241 BEGIN_DATA
0242     PUBLIC(x86)
0243     PUBLIC(have_cpuid)
0244     PUBLIC(x86_model)
0245     PUBLIC(x86_mask)
0246     PUBLIC(x86_capability)
0247     PUBLIC(x86_capability_ebx)
0248     PUBLIC(x86_capability_x)
0249     PUBLIC(x86_capability_cores)
0250     PUBLIC(x86_vendor_id)
0251     PUBLIC(hard_math)
0252 
0253 SYM(x86):
0254     .byte 0
0255 SYM(have_cpuid):
0256     .long 0
0257 SYM(x86_model):
0258     .byte 0
0259 SYM(x86_mask):
0260     .byte 0
0261 SYM(x86_capability):
0262     .long 0
0263 SYM(x86_capability_ebx):
0264     .long 0
0265 SYM(x86_capability_x):
0266     .long 0
0267 SYM(x86_capability_cores):
0268     .long 0
0269 SYM(x86_vendor_id):
0270     .zero 13
0271 SYM(hard_math):
0272     .byte 0
0273 END_DATA