![]() |
The OS
Journal http://www.osjournal.hopto.org/ A project by the people at alt.os.development |
||||||||||||
Introduction If you are programming an os and want to use all new features added to newer processors you will need way to check which features the current processor is compatible with. To do this on the Pentium processors and later is easy since you are provided an instruction called CPUID for this purpose. On the 486 and older it's harder. although some 486 processors support the CPUID instruction, most of them doesn't. It is possible to check whether the processor is a 8086, 286, 386 or 486 without the CPUID instruction. This can be done in various ways where one of them is to execute an instruction which only works on the processor checked and later. If the processor encounters an instruction it can't handle, an invalid opcode exception wil be generated. In this article we are going to go threw most of the things you need to do to identify your processor. Checking for 8086, 286, 386, 486 processors The CPUID instruction The CPUID instruction is streightforward. The first you should do is to load EAX with 0. CPUID returns different values in EAX, EBX, ECX and EDX depending on the value in EAX before the call. When CPUID is executed with EAX=0, it will return the following:
Each processor manufacturer have their own vendor string. For example, Intels vendor string is "GenuineIntel" and AMD's vendor string is "AuthenticAMD" while old Cyrix uses "CyrixInstead". This is very important because the information retrieved if you call CPUID with EAX>0 is different between different manufacturers. We have to store all this information in some variables for future use. The following C++ example will execute the CPUID instruction with EAX=0 and store the information retrieved in 2 variables. #include <stdio.h>
#include <string.h>
void main() {
char VendorSign[13]; //We need somewhere to store our vendorstring
unsigned long MaxEAX; //This will be used to store the maximum EAX
//possible to call CPUID with.
asm {
XOR EAX, EAX
//An efficient alternatvie to MOV EAX, 0x0
CPUID
//This instruction will load our registers with the data we need.
MOV dword ptr [VendorSign], EBX
//Copy the first 4 bytes in the VendorString from EBX.
MOV dword ptr [VendorSign+4], EDX
//Copy the next 4 bytes.
MOV dword ptr [VendorSign+8], ECX
//Copy the next 4 bytes.
MOV dword ptr MaxEAX, EAX
//EAX contains the maximum value to call CPUID with. Copy it to the
//MaxEAX variable.
}
VendorSign[12]=0; //The last character in the VendorSign can be anything.
//To make sure that it stops at the last character we add
//a zero character at the end
printf("Vendor string: %s\n", VendorSign);
printf("Maximum EAX value: %i\n", MaxEAX);
}
CPUID, EAX=1 Now it gets even more interesting. If you execute CPUID while EAX=1 instead you retrieve the processor family, model and stepping stored in EAX. You will also get a bitmap containing information on which functions (MMX, 3DNow etc.) that exists on the processor. Do not call CPUID with EAX=1 if our variable, MaxEAX is less than 1. It is a bit more frustrating when using EAX=1 because the processor manufacturers don't have the same meaning for the bits in the bitmap returned. Some are the same but many differ, we therefore have to check our VendorSign variable to determine how we should treat our bitmap. In this example we will make an array of all features in the bitmap that is correct for the Intel processor. We then check if AMD is used and change the array to the correct features. We will concentrate on Intel and AMD in this document. Adding support for Cyrix and other processors is left as an exercise for the reader;) char* Comp1[32]; //This is the array that will hold the short names
//for our features bitmap. The names added below is
//valid for the Intel processors and many of them
//are valid for other processors as well.
if(strcmp(VendorSign, "GenuineIntel")==0) {
Comp1[0]="FPU"; //Floating Point Unit
Comp1[1]="VME"; //Virtual Mode Extension
Comp1[2]="DE"; //Debugging Extension
Comp1[3]="PSE"; //Page Size Extension
Comp1[4]="TSC"; //Time Stamp Counter
Comp1[5]="MSR"; //Model Specific Registers
Comp1[6]="PAE"; //Physical Address Extesnion
Comp1[7]="MCE"; //Machine Check Extension
Comp1[8]="CX8"; //CMPXCHG8 Instruction
Comp1[9]="APIC"; //On-chip APIC Hardware
Comp1[10]=""; //Reserved
Comp1[11]="SEP"; //SYSENTER SYSEXIT
Comp1[12]="MTRR"; //Machine Type Range Registers
Comp1[13]="PGE"; //Global Paging Extension
Comp1[14]="MCA"; //Machine Check Architecture
Comp1[15]="CMOV"; //Conditional Move Instrction
Comp1[16]="PAT"; //Page Attribute Table
Comp1[17]="PSE-36"; //36-bit Page Size Extension
Comp1[18]="PSN"; //96-bit Processor Serial Number
Comp1[19]="CLFSH"; //CLFLUSH Instruction
Comp1[20]=""; //Reserved
Comp1[21]="DS"; //Debug Trace Store
Comp1[22]="ACPI"; //ACPI Support
Comp1[23]="MMX"; //MMX Technology
Comp1[24]="FXSR"; //FXSAVE FXRSTOR (Fast save and restore)
Comp1[25]="SSE"; //Streaming SIMD Extensions
Comp1[26]="SSE2"; //Streaming SIMD Extensions 2
Comp1[27]="SS"; //Self-Snoop
Comp1[28]="HTT"; //Hyper-Threading Technology
Comp1[29]="TM"; //Thermal Monitor Supported
Comp1[30]="IA-64"; //IA-64 capable
Comp1[31]=""; //Reserved
}
else if(strcmp(VendorSign, "AuthenticAMD")==0) {
Comp1[0]="FPU"; //Floating Point Unit
Comp1[1]="VME"; //Virtual Mode Extension
Comp1[2]="DE"; //Debugging Extension
Comp1[3]="PSE"; //Page Size Extension
Comp1[4]="TSC"; //Time Stamp Counter
Comp1[5]="MSR"; //Model Specific Registers
Comp1[6]="PAE"; //Physical Address Extesnion
Comp1[7]="MCE"; //Machine Check Extension
Comp1[8]="CX8"; //CMPXCHG8 Instruction
Comp1[9]="APIC"; //On-chip APIC Hardware
Comp1[10]=""; //Reserved
Comp1[11]="SEP"; //SYSENTER SYSEXIT
Comp1[12]="MTRR"; //Machine Type Range Registers
Comp1[13]="PGE"; //Global Paging Extension
Comp1[14]="MCA"; //Machine Check Architecture
Comp1[15]="CMOV"; //Conditional Move Instrction
Comp1[16]="PAT"; //Page Attribute Table
Comp1[17]="PSE-36"; //36-bit Page Size Extension
Comp1[18]=""; //?
Comp1[19]="MPC"; //MultiProcessing Capable (I made this short up, correct?)
Comp1[20]=""; //Reserved
Comp1[21]=""; //?
Comp1[22]="MIE"; //AMD Multimedia Instruction Extensions (I made this short up, correct?)
Comp1[23]="MMX"; //MMX Technology
Comp1[24]="FXSR"; //FXSAVE FXRSTOR (Fast save and restore)
Comp1[25]="SSE"; //Streaming SIMD Extensions
Comp1[26]=""; //?
Comp1[27]=""; //?
Comp1[28]=""; //?
Comp1[29]=""; //?
Comp1[30]="3DNowExt"; //3DNow Instruction Extensions (I made this short up, correct?)
Comp1[31]="3DNow"; //3DNow Instructions (I made this short up, correct?)
}
unsigned long REGEAX, REGEBX, REGECX, REGEDX;
int dFamily, dModel, dStepping, dFamilyEx, dModelEx;
char dType[10];
int dComp1Supported[32];
int dBrand, dCacheLineSize, dLogicalProcessorCount, dLocalAPICID;
if(MaxEAX>=1) {
asm {
MOV EAX, 1
CPUID
MOV [REGEAX], EAX
MOV [REGEBX], EBX
MOV [REGECX], ECX
MOV [REGEDX], EDX
}
dFamily=((REGEAX>>8)&0xF);
dModel=((REGEAX>>4)&0xF);
dStepping=(REGEAX&0xF);
dFamilyEx=((REGEAX>>20)&0xFF);
dModelEx=((REGEAX>>16)&0xF);
switch(((REGEAX>>12)&0x7)) {
case 0:
strcpy(dType, "Original");
break;
case 1:
strcpy(dType, "OverDrive");
break;
case 2:
strcpy(dType, "Dual");
break;
}
for(unsigned long C=1, Q=0;Q<32;C*=2, Q++) {
dComp1Supported[Q]=(REGEDX&C)!=0?1:0;
}
dBrand=REGEBX&0xFF;
dCacheLineSize=((strcmp(Comp1[19], "CLFSH")==0)&&(dComp1Supported[19]==1))?((REGEBX>>8)&0xFF)*8:-1;
dLogicalProcessorCount=((strcmp(Comp1[28], "HTT")==0)&&(dComp1Supported[28]==1))?((REGEBX>>16)&0xFF):-1;
dLocalAPICID=((REGEBX>>24)&0xFF); //This only works on P4 or later, must check for that in the future
}
printf("%s\n", dType);
printf("Family %i, Model %i, Stepping %i\n", dFamily, dModel, dStepping);
printf("Extended Family %i, Extended Model %i\n", dFamilyEx, dModelEx);
printf("Supported flags: ");
for(unsigned long Q=0;Q<27;Q++) {
if(dComp1Supported[Q]) {
printf("%s ", Comp1[Q]);
}
}
printf("\n");
printf("CacheLineSize: %i\n", dCacheLineSize);
printf("Logical processor count: %i\n", dLogicalProcessorCount);
printf("Local APIC ID: %i\n", dLocalAPICID);
TODO
http://www.osjournal.n3.net/!11 | |||||||||||||