Yavor Doganov wrote:
>> Well, I happen to know for a fact that the processor actually does have a
>> CPUID instruction, it's just disabled by default. It can be enabled quite
>> easily, which might be an avenue of enquiry for this problem.
>
> I would be very grateful if you tell me how to do it.  It will resolve
> the issue at least for our Cyrix CPUs, but will remain for 486.
>
> For me this bug is RC, because Debian is supposed to support CPUs >=i486
> for the i386 architecture.

I've attached a bit of hacked-together inline x86 assembly I wrote about
6 years ago to identify CPUs, which, as you would expect, enabled CPUID
on Cyrix CPUs to do it's job. It's not much use as-is, but should give
you the basic idea for enabling Cyrix's CPUID.

I did a bit of digging, and it seems this used to be handled correctly
by 2.4 kernels, but 2.6 seems to have broken it. People have suggested
patches, but nothing seems to have happened....as well, it doesn't work
on mine :)


        SCPUInfo                TempCPU;
        //The Dreaded Assembler
    __asm
    {
        inc     TempCPU.Family                  ;//We have to have at least a 
186

        //Determine whether model came before or after the 286   
        xor     ax, ax              ;//Set AX to 0
        push    ax                  ;//and push onto stack
        popf                        ;//Pop flag register off of stack
        pushf                       ;//Push back onto stack
        pop     ax                  ;//and pop off of AX
        and     ax, 0x0f000         ;//Do not clear the upper four bits
        cmp     ax, 0x0f000         ;//Are bits 12 - 15 all equal to 1?
        je      END                 ;//NO - It's a 186 or some other dinosaur
        inc     TempCPU.Family                  ;//YES - We have at least a 286

//Now work out if we have a 286 or above
                
        mov     ax, 0x07000         ;//Push 07000h 
        push    ax                  ;//onto stack
        popf                        ;//Pop flag register off
        pushf                       ;//and push back onto the stack
        pop     ax                  ;//Pop into AX register

        //Are Bits 12-14 NOT all 0
        and     ax, 0x07000         ;//Mask all except bits 12-14        
        je      END                 ;//NO --> Bits 12 - 14 are all 0 so a 286
        inc     TempCPU.Family         ;//YES -> We have at least a 386
                        
//Now work out if we have a 386 or above
                
        //Move the current EFLAGS into EAX
        pushfd                      ;//Push the EFLAGS onto the stack
        pop     eax                 ;//Pop the EFLAGS into EAX
                
        //Store our EFLAGS for later
        mov     ecx, eax            ;//Copy the value in EAX into ECX
                
        //Toggle bit 18 (The Alignment Check flag) in the EFLAGS register
        xor     eax, 0x00040000     ;

        //Move the new EFLAGS from EAX to the EFLAGS register
        push    eax                 ;//Push EAX onto the stack
        popfd                       ;//Pop the EFLAGS into EFLAGS register

        //Move the new EFLAGS back into EAX
        pushfd                      ;//Push the EFLAGS onto the stack
        pop     eax                 ;//Pop the EFLAGS into EAX
                
        //Compare our new EFLAGS with our original EFLAGS
        xor     eax, ecx            ;//XOR original & new EFLAGS
        jz      END                 ;//Can't toggle the AC bit, so a 386

        //Restore original EFlags
        push    ecx                 ;//Push ECX onto the stack
        popfd                       ;//Pop old flags into the EFlags register
                
        //Increment our counter
        inc     TempCPU.Family         ;//We have at least a 486

//Now work out if we have the CPUID instuction availible
        //CPUID test counter
        mov     ebx, 0              ;//Clear our counter

CPUIDTEST:
        //We still have the original flags in ECX
        mov     eax, ecx            ;//Move the value in ECX into EAX

        //Toggle bit 21 (The ID flag) in the EFLAGS register
        xor     eax, 0x00200000     ;
                
        //Move the new EFLAGS from EAX to the EFLAGS register
        push    eax                 ;//Push EAX onto the stack
        popfd                       ;//Pop the EFLAGS into EFLAGS register

        //Move the new EFLAGS back into EAX
        pushfd                      ;//Push the EFLAGS onto the stack
        pop     eax                 ;//Pop the EFLAGS into EAX
                
        //Compare our new EFLAGS with our original EFLAGS
        xor     eax, ecx            ;//XOR original and new EFLAGS
        jnz     POSTCPUID           ;//Can't toggle the AC bit, so a 486

        cmp     ebx, 1              ;//See if EBX = 1;
        je      END                 ;//YES-> Failed to enable CPUID, so a 486

        //But, Cyrix 6x86 processors can have the ID bit enabled

        //Enable the Configuration Control Registers (MAPEN)

            //Select register CCR3 (c3h) -- (Port 22h is register selection)
            mov    al, 0xc3         ;// c3h = index of CCR3
            out    0x22, al         ;// read/write of port 23h from/to CCR3
                
            //Get MAPEN (bits 4-7) value -- (Port 23h is now CCR3)
            in     al, 0x23         ;// Get from port 23h (al= value of CCR3)
            and    al, 0x0f         ;// Clear MAPEN, preserving bits 0-3
            or     al, 0x10         ;// MAPEN to 1 -> registers accessible

            // Save future CCR3
            mov    ah, al           ;// AH has a copy of our new CCR3
         
            //Select register CCR3 (c3h) -- (Port 22h is register selection)
            mov    al, 0xc3         ;// c3h = index of CCR3
            out    0x22, al         ;// read/write of port 23h from/to CCR3
                
            //Send out our (Modified) CCR3 so we can modify the CPU registers
            mov    al, ah           ;// Copy our new CCR3 into AL
            out    0x23, al         ;// MAPEN (bits 4-7) in CCR3 are set

        //Enable the CPUID Instruction
            //Select register CCR4 (e8h) -- (Port 22h is register selection)
            mov    al, 0xe8         ;// e8h = index of CCR4
            out    0x22, al         ;// read/write of port 23h from/to CCR4
                
            //Get the value of CCR4 -- (Port 23h is now CCR4)
            in     al, 0x23         ;// Get from port 23h (al= value of CCR4)
            or     al, 0x80         ;// Enable the CPUID Instruction bit

            // Save future CCR4
            mov    ah, al           ;// copy CCR4 where CPUID is Enabled

            //Select register CCR4 (e8h) -- (Port 22h is register selection)
            mov    al, 0xe8         ;// e8h = index of CCR4
            out    0x22, al         ;// read/write of port 23h from/to CCR4

            //Send out our new (Modified) CCR4 so CPUID is availible
            mov    al, ah           ;// AH has a copy of our new CCR4
            out    0x23, al         ;// CPUIDEN (bit 7) in CCR4 is cleared
                                
        //Disable the Configuration Control Registers (MAPEN)
            //Select register CCR3 (c3h) -- (Port 22h is register selection)
            mov    al, 0xc3         ;// c3h = index of CCR3
            out    0x22, al         ;// read/write of port 23h from/to CCR3
         
            //Get MAPEN value -- (Port 23h is now CCR3)
            in     al, 0x23         ;// Get from port 23h (al= value of CCR3)
            and    al, 0x0f         ;// Clear MAPEN (bits 4-7)
                
            // Save future CCR3
            mov    ah, al           ;// AH has a copy of our new CCR3
                
            //Select register CCR3 (c3h) -- (Port 22h is register selection)
            mov    al, 0xc3         ;// c3h = index of CCR3
            out    0x22, al         ;// read/write of port 23h from/to CCR3
                
            //Send out our new CCR3 so the CPU registers are locked
            mov    al, ah           ;// Copy our new CCR3 into AL
            out    0x23, al         ;// MAPEN (bits 4-7) in CCR3 are set to 0
        
            //Only try again once
            mov    ebx, 1           ;//Move 1 into EBX

            //Try again
            jmp    CPUIDTEST        ;//Jump to the CPUID test label

POSTCPUID:
        //Restore original EFlags
            push    ecx             ;//Push ECX onto the stack
            popfd                   ;//Pop old flags into the EFlags register
                
//We can now use the CPUID instruction to get a lot of processor information
            mov             eax, 0  ;//We want function 0 of CPUID
            cpuid                   ;//See macro at the top of this file

        //This checks that we have a (GenuineIntel) processor
            cmp     ebx, 0x756e6547 ;//ASCII: uneG (Genu)
            jne     NOTINTEL        
            cmp     edx, 0x49656e69 ;//ASCII: Ieni (ineI)
            jne     NOTINTEL        
            cmp     ecx, 0x6c65746e ;//ASCII: letn (ntel)
            jne     NOTINTEL        
            mov     TempCPU.Manufacturer, INTEL_CPU ;//Intel Processor

        //Now lets get some more specific information
            mov     eax, 1          ;//We want function 1 of CPUID
            cpuid                   ;//See macro at the top of this file

        //Get processor Info from the processor signature in the EAX register
            mov     ebx, eax        ;//Copy processor info into EBX from EAX
            and     ebx, 0xf        ;//We want bits 0-3
            mov     TempCPU.Stepping, ebx ;//Fill in the stepping info

            mov     ebx, eax        ;//Copy processor info into EBX from EAX
            and     ebx, 0xf0       ;//We want bits 4-7
            shr     ebx, 4;
            mov     TempCPU.Model, ebx  ;//Fill in the model info

            mov     ebx, eax        ;//Copy processor info into EBX from EAX
            and     ebx, 0xf00      ;//We want bits 8-11
            shr     ebx, 8;
            mov     TempCPU.Family, ebx;//Fill in the family info
                
            mov     ebx, eax        ;//Copy processor info into EBX from EAX
            and     ebx, 0x1000     ;//We want bit 12 (13 is dual processor)
            shr     ebx, 12;
            mov     TempCPU.IsOverDrive, ebx;//Do we have an Overdrive?

            mov     TempCPU.Serial1, eax ;//Copy processor sig. for serial 
number

        //Test for MMX Capabilty using the feature flags in the EDX register
            mov    ebx, edx         ;//Copy feature flags into EBX from EDX
            and    ebx, 0x00800000  ;//Check for bit 23 (MMX Present)
            shr    ebx, 23          ;//Shift the result to bit 1
            mov    TempCPU.MMX, ebx                     ;//After AND, 1 for 
MMX, 0 for no MMX

        //Test for SIMD Capabilty using the feature flags in the EDX register
            mov    ebx, edx         ;//Copy feature flags into EBX from EDX
            and    ebx, 0x02000000  ;//Check for bit 25 (SIMD Present)
            shr    ebx, 25          ;//Shift the result to bit 1
            mov    TempCPU.SIMD, ebx   ;//Else, we have SIMD

        //Now lets get the cache information
            mov    eax, 2           ;//We want function 2 of CPUID
            cpuid                   ;//See macro at the top of this file

        //For now, only get the 2nd level cache size
            and    edx, 0xff        ;
            mov    TempCPU.Level2Cache, edx     ;
        
        //Now lets get the serial number
            mov    eax, 3           ;//We want function 3 of CPUID
            cpuid                   ;//See macro at the top of this file

            mov    TempCPU.Serial2, edx         ;
            mov    TempCPU.Serial3, ecx         ;
        
            jmp    END              ;

NOTINTEL:
        //Now lets try getting the manufacturer ID again
            mov    eax, 0           ;//We want function 1 of CPUID
            cpuid                   ;//See macro at the top of this file

            cmp    ebx, 0x69727943  ;//ASCII: iryC (Cyri)
            jne    NOTCYRIX         ;
            cmp    edx, 0x736e4978  ;//ASCII: snIx (xIns)
            jne    NOTCYRIX         ;
            cmp    ecx, 0x64616574  ;//ASCII: daet (tead)
            jne    NOTCYRIX         ;
            mov    TempCPU.Manufacturer, CYRIX_CPU ;//Intel Processor

                         //Get Misc. Processor Info
            //Select register DIR0 (FEh) -- (Port 22h is register selection)
            mov    al, 0xfe                     ;// feh = index of DIR0
            out    0x22, al                     ;// read/write of port 23h 
from/to DIR0
                
            //Get the value of DIR0 -- (Port 23h is now DIR0)
            in     al, 0x23                     ;// Get from port 23h (al= 
value of DIR0)
                        mov    TempCPU.ClockMultiplier, eax;

        //Now lets get some more specific information
            mov    eax, 1           ;//We want function 1 of CPUID
            cpuid                   ;//See macro at the top of this file

        //Get the processor Info from processor signature in the EAX register
            mov    ebx, eax         ;//Copy processor info into EBX from EAX
            and    ebx, 0xf         ;//We want bits 0-3
            mov    TempCPU.Stepping, ebx        ;//Fill in the stepping info

            mov    ebx, eax         ;//Copy processor info into EBX from EAX
            and    ebx, 0xf0        ;//We want bits 4-7
            shr    ebx, 4           ;
            mov    TempCPU.Model, ebx           ;//Fill in the model info

            mov    ebx, eax         ;//Copy processor info into EBX from EAX
            and    ebx, 0xf00       ;//We want bits 8-11
            shr    ebx, 8           ;
            mov    TempCPU.Family, ebx  ;//Fill in the Family info
                
            mov    ebx, eax         ;//Copy processor info into EBX from EAX
            and    ebx, 0x1000      ;//We want bit 12 (bit 13 is for dual 
processors but I don't care)
            shr    ebx, 12          ;
            mov    TempCPU.IsOverDrive, ebx ;//Do we have an Overdrive?

            mov    TempCPU.Serial1, eax ;//Copy processor sig. for serial number

        //Test for MMX Capabilty using the feature flags in the EDX register
            mov    ebx, edx         ;//Copy feature flags into EBX from EDX
            and    ebx, 0x00800000  ;//Check for bit 23 (MMX Present)
            shr    ebx, 23          ;//Shift the result to bit 1
            mov    TempCPU.MMX, ebx                     ;//After AND, 1 for 
MMX, 0 for no MMX
                
        //Test for SIMD Capabilty using the feature flags in the EDX register
            mov    ebx, edx         ;//Copy feature flags into EBX from EDX
            and    ebx, 0x02000000  ;//Check for bit 25 (SIMD Present)
            shr    ebx, 25          ;//Shift the result to bit 1
            mov    TempCPU.SIMD, ebx            ;//Else, we have SIMD

            jmp    END              ;

NOTCYRIX:
            mov    TempCPU.Manufacturer, AMD_CPU;//Assume AMD for now

            //Now lets get some more specific information
            mov    eax, 1           ;//We want function 1 of CPUID
            cpuid                   ;//See macro at the top of this file
                
            //Test for MMX Capabilty using feature flags in the EDX register
            mov    ebx, edx         ;//Copy feature flags into EBX from EDX
            and    ebx, 0x00800000  ;//Check for bit 23 (MMX Present)
            shr    ebx, 23          ;//Shift the result to bit 1
            mov    TempCPU.MMX, ebx                     ;//After AND, 1 for 
MMX, 0 for no MMX

            jmp    END              ;
                
END:
        }

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to