[ab0df696] | 1 | /* cpuModel.S |
---|
| 2 | * |
---|
| 3 | * This file contains all assembly code for the Intel Cpu identification. |
---|
| 4 | * It is based on linux cpu detection code. |
---|
| 5 | * |
---|
| 6 | * Intel also provides public similar code in the book |
---|
| 7 | * called : |
---|
| 8 | * |
---|
| 9 | * Pentium Processor Family |
---|
| 10 | * Developer Family |
---|
| 11 | * Volume 3 : Architecture and Programming Manual |
---|
| 12 | * |
---|
| 13 | * At the following place : |
---|
| 14 | * |
---|
| 15 | * Chapter 5 : Feature determination |
---|
| 16 | * Chapter 25: CPUID instruction |
---|
| 17 | * |
---|
| 18 | * COPYRIGHT (c) 1998 valette@crf.canon.fr |
---|
| 19 | * |
---|
| 20 | * The license and distribution terms for this file may be |
---|
| 21 | * found in the file LICENSE in this distribution or at |
---|
[1715986] | 22 | * http://www.rtems.com/license/LICENSE. |
---|
[ab0df696] | 23 | * |
---|
| 24 | * $Id$ |
---|
| 25 | */ |
---|
| 26 | |
---|
[6caf92f8] | 27 | #include <rtems/asm.h> |
---|
[fe484110] | 28 | #include <rtems/score/registers.h> |
---|
[ab0df696] | 29 | |
---|
| 30 | BEGIN_CODE |
---|
| 31 | PUBLIC(checkCPUtypeSetCr0); |
---|
| 32 | /* |
---|
| 33 | * check Processor type: 386, 486, 6x86(L) or CPUID capable processor |
---|
| 34 | */ |
---|
| 35 | |
---|
| 36 | SYM (checkCPUtypeSetCr0): |
---|
| 37 | /* |
---|
| 38 | * Assume 386 for now |
---|
| 39 | */ |
---|
| 40 | movl $3, SYM (x86) |
---|
| 41 | /* |
---|
[aa9eb940] | 42 | * Start using the EFLAGS AC bit determination method described in |
---|
| 43 | * the book mentioned above page 5.1. If this bit can be set we |
---|
[ab0df696] | 44 | * have a 486 or above. |
---|
| 45 | */ |
---|
| 46 | pushfl /* save EFLAGS */ |
---|
| 47 | |
---|
| 48 | pushfl /* Get EFLAGS in EAX */ |
---|
| 49 | popl eax |
---|
| 50 | |
---|
| 51 | movl eax,ecx /* save original EFLAGS in ECX */ |
---|
| 52 | xorl $EFLAGS_ALIGN_CHECK,eax /* flip AC bit in EAX */ |
---|
| 53 | pushl eax /* set EAX as EFLAGS */ |
---|
| 54 | popfl |
---|
| 55 | pushfl /* Get new EFLAGS in EAX */ |
---|
| 56 | popl eax |
---|
| 57 | |
---|
| 58 | xorl ecx,eax /* check if AC bit changed */ |
---|
| 59 | andl $EFLAGS_ALIGN_CHECK,eax |
---|
| 60 | je is386 /* If not : we have a 386 */ |
---|
| 61 | /* |
---|
| 62 | * Assume 486 for now |
---|
| 63 | */ |
---|
| 64 | movl $4,SYM (x86) |
---|
| 65 | movl ecx,eax /* Restore orig EFLAGS in EAX */ |
---|
| 66 | xorl $EFLAGS_ID,eax /* flip ID flag */ |
---|
| 67 | pushl eax /* set EAX as EFLAGS */ |
---|
| 68 | popfl |
---|
| 69 | pushfl /* Get new EFLAGS in EAX */ |
---|
| 70 | popl eax |
---|
| 71 | |
---|
| 72 | xorl ecx,eax /* check if ID bit changed */ |
---|
| 73 | andl $EFLAGS_ID,eax |
---|
| 74 | |
---|
| 75 | /* |
---|
| 76 | * if we are on a straight 486DX, |
---|
| 77 | * SX, or 487SX we can't change it |
---|
| 78 | * OTOH 6x86MXs and MIIs check OK |
---|
| 79 | * Also if we are on a Cyrix 6x86(L) |
---|
| 80 | */ |
---|
| 81 | je is486x |
---|
| 82 | |
---|
| 83 | isnew: |
---|
| 84 | /* |
---|
| 85 | * restore original EFLAGS |
---|
| 86 | */ |
---|
| 87 | popfl |
---|
| 88 | incl SYM(have_cpuid) /* we have CPUID instruction */ |
---|
| 89 | |
---|
| 90 | /* use it to get : |
---|
| 91 | * processor type, |
---|
| 92 | * processor model, |
---|
| 93 | * processor mask, |
---|
| 94 | * by using it with EAX = 1 |
---|
| 95 | */ |
---|
| 96 | movl $1, eax |
---|
| 97 | cpuid |
---|
| 98 | |
---|
| 99 | movb al, cl /* save reg for future use */ |
---|
| 100 | |
---|
| 101 | andb $0x0f,ah /* mask processor family */ |
---|
| 102 | movb ah,SYM (x86) /* put result in x86 var */ |
---|
| 103 | |
---|
| 104 | andb $0xf0, al /* get model */ |
---|
| 105 | shrb $4, al |
---|
| 106 | movb al,SYM (x86_model) /* store it in x86_model */ |
---|
| 107 | |
---|
| 108 | andb $0x0f, cl /* get mask revision */ |
---|
| 109 | movb cl,SYM (x86_mask) /* store it in x86_mask */ |
---|
| 110 | |
---|
| 111 | movl edx,SYM(x86_capability) /* store feature flags in x86_capability */ |
---|
| 112 | |
---|
| 113 | /* get vendor info by using CPUID with EXA = 0 */ |
---|
| 114 | xorl eax, eax |
---|
| 115 | cpuid |
---|
| 116 | |
---|
| 117 | /* |
---|
| 118 | * store results contained in ebx, edx, ecx in |
---|
| 119 | * x86_vendor_id variable. |
---|
| 120 | */ |
---|
| 121 | movl ebx,SYM(x86_vendor_id) |
---|
| 122 | movl edx,SYM(x86_vendor_id)+4 |
---|
| 123 | movl ecx,SYM(x86_vendor_id)+8 |
---|
| 124 | |
---|
| 125 | movl cr0,eax /* 486+ */ |
---|
| 126 | andl $(CR0_PAGING | CR0_PROTECTION_ENABLE | CR0_EXTENSION_TYPE), eax |
---|
| 127 | orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax |
---|
| 128 | jmp 2f |
---|
| 129 | |
---|
| 130 | /* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid |
---|
| 131 | * clobbering the new BX chipset used with the Pentium II, which has a register |
---|
| 132 | * at the same addresses as those used to access the Cyrix special configuration |
---|
| 133 | * registers (CCRs). |
---|
| 134 | */ |
---|
| 135 | /* |
---|
| 136 | * A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2 |
---|
| 137 | * (and it _must_ be 5 divided by 2) while other CPUs change |
---|
| 138 | * them in undefined ways. We need to know this since we may |
---|
| 139 | * need to enable the CPUID instruction at least. |
---|
| 140 | * We couldn't use this test before since the PPro and PII behave |
---|
| 141 | * like Cyrix chips in this respect. |
---|
| 142 | */ |
---|
| 143 | is486x: xor ax,ax |
---|
| 144 | sahf |
---|
[eb17041] | 145 | movb $5,al |
---|
| 146 | movb $2,bl |
---|
[ab0df696] | 147 | div bl |
---|
| 148 | lahf |
---|
| 149 | cmpb $2,ah |
---|
| 150 | jne ncyrix |
---|
| 151 | /* |
---|
| 152 | * N.B. The pattern of accesses to 0x22 and 0x23 is *essential* |
---|
| 153 | * so do not try to "optimize" it! For the same reason we |
---|
| 154 | * do all this with interrupts off. |
---|
| 155 | */ |
---|
| 156 | #define setCx86(reg, val) \ |
---|
[eb17041] | 157 | movb reg,al; \ |
---|
| 158 | outb al,$0x22; \ |
---|
| 159 | movb val,al; \ |
---|
| 160 | outb al,$0x23 |
---|
[ab0df696] | 161 | |
---|
| 162 | #define getCx86(reg) \ |
---|
[eb17041] | 163 | movb reg,al; \ |
---|
| 164 | outb al,$0x22; \ |
---|
| 165 | inb $0x23,al |
---|
[ab0df696] | 166 | |
---|
| 167 | cli |
---|
| 168 | getCx86($0xc3) /* get CCR3 */ |
---|
[eb17041] | 169 | movb al,cl /* Save old value */ |
---|
| 170 | movb al,bl |
---|
| 171 | andb $0x0f,bl /* Enable access to all config registers */ |
---|
| 172 | orb $0x10,bl /* by setting bit 4 */ |
---|
| 173 | setCx86($0xc3,bl) |
---|
[ab0df696] | 174 | |
---|
| 175 | getCx86($0xe8) /* now we can get CCR4 */ |
---|
[eb17041] | 176 | orb $0x80,al /* and set bit 7 (CPUIDEN) */ |
---|
| 177 | movb al,bl /* to enable CPUID execution */ |
---|
| 178 | setCx86($0xe8,bl) |
---|
[ab0df696] | 179 | |
---|
| 180 | getCx86($0xfe) /* DIR0 : let's check this is a 6x86(L) */ |
---|
[eb17041] | 181 | andb $0xf0,al /* should be 3xh */ |
---|
| 182 | cmpb $0x30,al |
---|
[ab0df696] | 183 | jne n6x86 |
---|
| 184 | getCx86($0xe9) /* CCR5 : we reset the SLOP bit */ |
---|
[eb17041] | 185 | andb $0xfd,al /* so that udelay calculation */ |
---|
| 186 | movb al,bl /* is correct on 6x86(L) CPUs */ |
---|
| 187 | setCx86($0xe9,bl) |
---|
| 188 | setCx86($0xc3,cl) /* Restore old CCR3 */ |
---|
[ab0df696] | 189 | sti |
---|
| 190 | jmp isnew /* We enabled CPUID now */ |
---|
| 191 | |
---|
[eb17041] | 192 | n6x86: setCx86($0xc3,cl) /* Restore old CCR3 */ |
---|
[ab0df696] | 193 | sti |
---|
| 194 | ncyrix: /* restore original EFLAGS */ |
---|
| 195 | popfl |
---|
| 196 | movl cr0,eax /* 486 */ |
---|
| 197 | andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax /* Save PG,PE,ET */ |
---|
| 198 | orl $(CR0_ALIGMENT_MASK | CR0_WRITE_PROTECT | CR0_NUMERIC_ERROR | CR0_MONITOR_COPROC),eax /* set AM, WP, NE and MP */ |
---|
| 199 | jmp 2f |
---|
| 200 | is386: /* restore original EFLAGS */ |
---|
| 201 | popfl |
---|
| 202 | movl cr0,eax /* 386 */ |
---|
| 203 | andl $(CR0_PAGING | CR0_EXTENSION_TYPE | CR0_PROTECTION_ENABLE),eax /* Save PG,PE,ET */ |
---|
| 204 | orl $CR0_MONITOR_COPROC,eax /* set MP */ |
---|
| 205 | 2: movl eax,cr0 |
---|
| 206 | call check_x87 |
---|
| 207 | ret |
---|
| 208 | |
---|
| 209 | |
---|
| 210 | /* |
---|
| 211 | * We depend on ET to be correct. This checks for 287/387. |
---|
| 212 | */ |
---|
| 213 | check_x87: |
---|
| 214 | movb $0,SYM(hard_math) |
---|
| 215 | clts |
---|
| 216 | fninit |
---|
| 217 | fstsw ax |
---|
| 218 | cmpb $0,al |
---|
| 219 | je 1f |
---|
| 220 | movl cr0,eax /* no coprocessor: have to set bits */ |
---|
| 221 | xorl $4,eax /* set EM */ |
---|
| 222 | movl eax,cr0 |
---|
| 223 | ret |
---|
| 224 | .align 16 |
---|
| 225 | 1: movb $1,SYM(hard_math) |
---|
| 226 | .byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */ |
---|
| 227 | ret |
---|
| 228 | |
---|
| 229 | END_CODE |
---|
| 230 | |
---|
| 231 | BEGIN_DATA |
---|
| 232 | PUBLIC(x86) |
---|
| 233 | PUBLIC(have_cpuid) |
---|
| 234 | PUBLIC(x86_model) |
---|
| 235 | PUBLIC(x86_mask) |
---|
| 236 | PUBLIC(x86_capability) |
---|
| 237 | PUBLIC(x86_vendor_id) |
---|
| 238 | PUBLIC(hard_math) |
---|
| 239 | |
---|
| 240 | SYM(x86): |
---|
| 241 | .byte 0 |
---|
| 242 | SYM(have_cpuid): |
---|
| 243 | .long 0 |
---|
| 244 | SYM(x86_model): |
---|
| 245 | .byte 0 |
---|
| 246 | SYM(x86_mask): |
---|
| 247 | .byte 0 |
---|
| 248 | SYM(x86_capability): |
---|
| 249 | .long 0 |
---|
| 250 | SYM(x86_vendor_id): |
---|
| 251 | .zero 13 |
---|
| 252 | SYM(hard_math): |
---|
| 253 | .byte 0 |
---|
| 254 | END_DATA |
---|