PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Abfangen von CPU-Exceptions (invalid instruction)


liquid
2003-12-23, 12:13:23
Morgen,

ich bin grade dabei CPU detection code in mein Projekt einzuarbeiten und stoße als ASM-n00b natürlich auf massive Gegenwehr des Inline-Assemblers.

Vorlage ist Code von AMD. Da wird am Anfang geguckt ob der CPU überhaupt die cpuid Instruktion versteht. Die invalid instruction exception der CPU wird bei Eintreten aufgefangen und das Detecting abgebrochen.
Allerdings ist das Code für den MSVC und ich arbeite ja mit den MinGW. Dumm einerseits, da das Intel-ASM ist und ich AT&T brauche. Dann kommt noch hinzu, dass das Abfangen der Exception über einen __try (nicht try!) Block passiert, der in windef.h deklariert wird (über excpt.h).

In windef.h sieht man allerdings ganz deutlich (und die Devs haben es auch kommentiert), dass __try ein Dummy-Define ist, damit Source mit gerade diesem Kommado überhaupt compiled. Abgefangen wird da allerdings gar nüscht.

Und da mein Code sowieso möglich OS-unabhängig sein soll, suche ich jetzt nach einer Möglichkeit CPU-Exceptions (die ja wohl vom OS gehandelt werden) selber zu bearbeiten, in meinem Fall schlicht zu ignorieren und weiterzumachen. Ist sowas möglich, oder sollte ich mir lieber eine andere Methode des Detecting suchen?

Ich wär natürlich trotzdem daran interessiert, wie dieses Exception Handling auf OS-Level abläuft. Zecki???!!! :D

cya
liquid

zeckensack
2003-12-26, 21:20:59
Ich hab' mal die Rentiere verscheucht :|

Zum Thema kann ich dir auch nicht viel sagen, ausser
1)ich hab's auch mal versucht, und ganz schnell wieder aufgegeben ... (http://www.forum-3dcenter.org/vbulletin/showthread.php?postid=834385#post834385)
2)__try gibt's zwar nicht wirklich, aber MinGW kennt auf jeden Fall try, throw und catch (ohne führende Unterstriche!). Wie man jetzt genau CPU-Exceptions fängt ... siehe oben :naughty:

tb
2004-01-16, 02:15:28
static bool cpuid(unsigned long function, unsigned long& out_eax, unsigned long& out_ebx, unsigned long& out_ecx, unsigned long& out_edx)
{
bool retval = true;
unsigned long local_eax, local_ebx, local_ecx, local_edx;
_asm pushad;

__try
{
_asm
{
xor edx, edx // Clue the compiler that EDX is about to be used.
mov eax, function // set up CPUID to return processor version and features
// 0 = vendor string, 1 = version info, 2 = cache info
cpuid // code bytes = 0fh, 0a2h
mov local_eax, eax // features returned in eax
mov local_ebx, ebx // features returned in ebx
mov local_ecx, ecx // features returned in ecx
mov local_edx, edx // features returned in edx
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
retval = false;
}

out_eax = local_eax;
out_ebx = local_ebx;
out_ecx = local_ecx;
out_edx = local_edx;

_asm popad

return retval;
}



Frag nicht, woher der Code stammt...

Thomas

liquid
2004-01-16, 13:56:16
thx@tb, aber ich habe das Problem jetzt doch anders gelöst. Den Code den du gepostet hast, habe ich nämlich in einer etwas anderen Variante. Allerdings will ich es SO gerade nicht machen, da das Exceptionhandling OS-spezifisch gehandhabt werden muss und dieser Code unter Linux wahrscheinlich nicht funktionieren wird.
Dennoch trotzdem Danke!

cya
liquid

Coda
2004-01-21, 17:21:27
Öhm es gibt auch eine Flag ob CPUID unterstützt wird, das must du nicht mit Exception fangen machen

liquid
2004-01-21, 17:35:24
Sicher Coda, rat mal wie ich das momentan bewerkstellige ;)

Das Problem hat sich aber nun auch verallgemeinert, da es nicht mehr nur CPUID ist sondern auch SSE geprüft werden soll. Und da kommt man mit einfachen EFlags auch nicht weiter.

cya
liquid

tb
2004-01-22, 05:08:47
static bool cpuid(unsigned long function, unsigned long& out_eax, unsigned long& out_ebx, unsigned long& out_ecx, unsigned long& out_edx)
{
#ifdef _LINUX
asm("cpuid": "=a" (out_eax), "=b" (out_ebx), "=c" (out_ecx), "=d" (out_edx) : "a" (function));
return true;
#else
bool retval = true;
unsigned long local_eax, local_ebx, local_ecx, local_edx;
_asm pushad;

__try
{
_asm
{
xor edx, edx // Clue the compiler that EDX is about to be used.
mov eax, function // set up CPUID to return processor version and features
// 0 = vendor string, 1 = version info, 2 = cache info
cpuid // code bytes = 0fh, 0a2h
mov local_eax, eax // features returned in eax
mov local_ebx, ebx // features returned in ebx
mov local_ecx, ecx // features returned in ecx
mov local_edx, edx // features returned in edx
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
retval = false;
}

out_eax = local_eax;
out_ebx = local_ebx;
out_ecx = local_ecx;
out_edx = local_edx;

_asm popad

return retval;
#endif


}

bool CheckMMXTechnology(void)
{
unsigned long eax,ebx,edx,unused;
if( !cpuid(1,eax,ebx,unused,edx) )
return false;

return ( edx & 0x800000 ) != 0;
}

bool CheckSSETechnology(void)
{
unsigned long eax,ebx,edx,unused;
if( !cpuid(1,eax,ebx,unused,edx) )
return false;

return ( edx & 0x2000000L ) != 0;
}

bool CheckSSE2Technology(void)
{
unsigned long eax,ebx,edx,unused;
if( !cpuid(1,eax,ebx,unused,edx) )
return false;

return ( edx & 0x04000000 ) != 0;
}

bool Check3DNowTechnology(void)
{
unsigned long eax, unused;
if( !cpuid(0x80000000,eax,unused,unused,unused) )
return false;

if ( eax > 0x80000000L )
{
if( !cpuid(0x80000001,unused,unused,unused,eax) )
return false;

return ( eax & 1<<31 ) != 0;
}
return false;
}

bool CheckCMOVTechnology()
{
unsigned long eax,ebx,edx,unused;
if( !cpuid(1,eax,ebx,unused,edx) )
return false;

return ( edx & (1<<15) ) != 0;
}

bool CheckFCMOVTechnology(void)
{
unsigned long eax,ebx,edx,unused;
if( !cpuid(1,eax,ebx,unused,edx) )
return false;

return ( edx & (1<<16) ) != 0;
}

bool CheckRDTSCTechnology(void)
{
unsigned long eax,ebx,edx,unused;
if( !cpuid(1,eax,ebx,unused,edx) )
return false;

return ( edx & 0x10 ) != 0;
}

// Return the Processor's vendor identification string, or "Generic_x86" if it doesn't exist on this CPU
const char* GetProcessorVendorId()
{
unsigned long unused, VendorIDRegisters[3];

static char VendorID[13];

memset( VendorID, 0, sizeof(VendorID) );
if( !cpuid(0,unused, VendorIDRegisters[0], VendorIDRegisters[2], VendorIDRegisters[1] ) )
{
strcpy( VendorID, "Generic_x86" );
}
else
{
memcpy( VendorID+0, &(VendorIDRegisters[0]), sizeof( VendorIDRegisters[0] ) );
memcpy( VendorID+4, &(VendorIDRegisters[1]), sizeof( VendorIDRegisters[1] ) );
memcpy( VendorID+8, &(VendorIDRegisters[2]), sizeof( VendorIDRegisters[2] ) );
}


return VendorID;
}

// Returns non-zero if Hyper-Threading Technology is supported on the processors and zero if not. This does not mean that
// Hyper-Threading Technology is necessarily enabled.
static bool HTSupported(void)
{
const unsigned int HT_BIT = 0x10000000; // EDX[28] - Bit 28 set indicates Hyper-Threading Technology is supported in hardware.
const unsigned int FAMILY_ID = 0x0f00; // EAX[11:8] - Bit 11 thru 8 contains family processor id
const unsigned int EXT_FAMILY_ID = 0x0f00000; // EAX[23:20] - Bit 23 thru 20 contains extended family processor id
const unsigned int PENTIUM4_ID = 0x0f00; // Pentium 4 family processor id

unsigned long unused,
reg_eax = 0,
reg_edx = 0,
vendor_id[3] = {0, 0, 0};

// verify cpuid instruction is supported
if( !cpuid(0,unused, vendor_id[0],vendor_id[2],vendor_id[1])
|| !cpuid(1,reg_eax,unused,unused,reg_edx) )
return false;

// Check to see if this is a Pentium 4 or later processor
if (((reg_eax & FAMILY_ID) == PENTIUM4_ID) || (reg_eax & EXT_FAMILY_ID))
if (vendor_id[0] == 'uneG' && vendor_id[1] == 'Ieni' && vendor_id[2] == 'letn')
return (reg_edx & HT_BIT) != 0; // Genuine Intel Processor with Hyper-Threading Technology

return false; // This is not a genuine Intel processor.
}

// Returns the number of logical processors per physical processors.
static unsigned char LogicalProcessorsPerPackage(void)
{
const unsigned NUM_LOGICAL_BITS = 0x00FF0000; // EBX[23:16] indicate number of logical processors per package

unsigned long unused,
reg_ebx = 0;
if (!HTSupported())
return 1;

if( !cpuid(1,unused,reg_ebx,unused,unused) )
return 1;

return (unsigned char) ((reg_ebx & NUM_LOGICAL_BITS) >> 16);
}


Vielleicht hilfts, sollte problemlos unter Windows und Linux laufen. Warum verwendest Du nicht LCC (http://www.cs.virginia.edu/~lcc-win32/)?

Thomas

liquid
2004-01-22, 11:06:01
/* The FP part of SSE introduces a new architectural state and therefore

requires support from the operating system. So even if CPUID indicates

support for SSE FP, the application might not be able to use it. If

CPUID indicates support for SSE FP, check here whether it is also

supported by the OS, and turn off the SSE FP feature bit if there

is no OS support for SSE FP.



Operating systems that do not support SSE FP return an illegal

instruction exception if execution of an SSE FP instruction is performed.

Here, a sample SSE FP instruction is executed, and is checked for an

exception using the (non-standard) __try/__except mechanism

of Microsoft Visual C.

*/

Das steht als Kommentar in einer Abfragefunktion drin, die auf der AMD Dev-Page zu finden ist. Zecki wird den Inhalt des Kommentars bestätigen können. Es geht ja nicht darum zu gucken ob die Hardware SSE hat, das ist ja noch so ziemlich das einfachste.
Aber OS-unabhängig zu prüfen ob denn das OS auch dazu fähig ist die Instrukionen auszuführen ist wohl schon etwas schwerer oder gar ein Ding der Unmöglichkeit, wenn man nicht vorhat auf jede Menge Defines zurückzugreifen.

Dennoch, der Code ist interessant, besonders das mit dem HyperThreading. Thx!

cya
liquid