Somewhat better CPUID support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



revisions to cpu.c to better support cpuid instruction, added detection of 
SSE2/2, 3DNow, PAE 

Changelog:
New file cpuid.h
Refactored cpuid support previously for FREEBSD to work for all _i386_ 
platforms.

Index: cpu.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/cpu.c,v
retrieving revision 1.3
diff -u -3 -p -r1.3 cpu.c
--- cpu.c	4 Dec 2003 01:41:53 -0000	1.3
+++ cpu.c	13 Jan 2004 02:39:51 -0000
@@ -31,7 +31,9 @@
 #ifdef HAVE_MACHINE_CPU_H
 # include <machine/cpu.h>
 #endif
-
+#ifdef  HAVE_UNISTD_H
+#include <unistd.h>
+#endif
 #include <ctype.h>
 #include <string.h>
 #include <stdarg.h>
@@ -40,6 +42,7 @@
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
 #endif
+#include "cpuid.h"
 
 
 #define NONAMELESSUNION
@@ -59,6 +62,101 @@ WINE_DEFAULT_DEBUG_CHANNEL(reg);
 #define ENTI	0x69746e65	/* "enti" */
 #define CAMD	0x444d4163	/* "cAMD" */
 
+int OS_SSESupported(void)
+{
+/* Stub for now
+    SSE requires the OS to support it
+    Test for it here and return  the level of sse support (1 or 2) for yes, 0 for no
+   */
+   int level=0;
+   #ifdef linux
+/* Here for reference not currently used
+ * Please test before relying on this code
+ */
+    {
+        char line[200];
+
+        FILE *f = fopen ("/proc/cpuinfo", "r");
+        if (!f)
+            return 0;    //Asume no sse support
+
+        while (fgets(line,200,f)!=NULL)
+       {
+            if (strstr(line,"sse2") ) if(level < 2) level=2;
+            if (strstr(line,"sse") ) if(level <1) level=1;
+       }
+      return level;
+    }
+
+#endif /* linux*/
+/*
+ *  How do we test for this on other Oses
+  */
+   return 0;
+}
+
+static inline int get_processor_count( void)
+{
+/* Get the number of configured processors in a multiprocessor system
+ * The method for doing this varies widely
+*/
+	int count=0;
+
+#ifdef linux
+/* Here for reference not currently used
+ * Please test before relying on this code
+ */
+    {
+        char line[200];
+        FILE *f = fopen ("/proc/cpuinfo", "r");
+        if (!f)
+            return 1;    //Asume we have at least one processor
+
+        while (fgets(line,200,f)!=NULL)
+       {
+            if (strstr(line,"processor") )count++;
+        }
+      if(count>0) return count;
+    }
+
+#endif /* linux*/
+
+#ifdef sun
+    count = sysconf(_SC_NPROCESSORS_ONLN);
+    if(count>0) return count;		 
+#endif /* sun*/
+
+#ifdef __NETBSD__
+    {
+    int mib[2];
+    int value[2]={0,0};
+
+          mib[0] = CTL_HW;
+          mib[1] = HW_NCPU;
+          value[1] = sizeof(int);
+           if (sysctl(mib, 2, value, value+1, NULL, 0) >= 0)
+               return(value[0]);
+    }
+#endif
+
+#ifdef __FREEBSD__
+    {
+    int ret, len, num;
+
+        len = sizeof(num);
+        ret = sysctlbyname("hw.ncpu", &num, &len, NULL, 0);
+        if(!ret) return num;
+
+    }
+#endif
+/*
+ * In the event this is an os not suported by this routine then assume 1 CPU
+*/
+
+return 1;
+
+}
+
 /* Calls cpuid with an eax of 'ax' and returns the 16 bytes in *p
  * We are compiled with -fPIC, so we can't clobber ebx.
  */
@@ -97,6 +195,131 @@ static inline int have_cpuid(void)
 #endif
 }
 
+static cpuid_t CpuId(unsigned long inst)
+{
+
+	static unsigned long _eax,_ebx,_ecx,_edx;
+	static cpuid_t cpuid;
+
+
+	_eax=inst;
+	_ebx=_edx=_ecx=0;
+
+
+/*
+ * Note this is supposed to work on 386s
+ * It is not very likely wine will be in use on these systems
+ * Note that this method will fail if an interrupt modifies the ID bit
+ * of the flags register, but I expect this is unlikely to happen in practise
+ * If it does, the worst that can happen is we get identified as a 386 system
+ */
+
+	asm (
+	"\tpushl %%ebx\n"
+	"\tmovl %%eax,%%ecx\n"
+	"\tpushfl\n"
+	"\tpopl %%eax\n"
+	"\tmovl %%eax,%%ebx\n"
+	"\txorl  $2097152, %%eax\n"
+	"\tpushl %%eax\n"
+	"\tpopfl\n"
+	"\tpushfl\n"
+	"\tpopl %%eax\n"
+	"\tpushl %%ebx\n"
+	"\tpopfl\n"
+	"\tandl $2097152,%%eax\n"
+	"\tandl $2097152,%%ebx\n"
+	"\txorl %%eax,%%ebx\n"
+	"\tjnz have_cpuid\n"
+	"\tclrl %%ebx\n"
+	"\tjmp endit\n"
+	"have_cpuid:\tmovl %%ecx,%%eax\n"
+	"\tcpuid\n"
+	"endit:\n"
+	"\tmovl %%ebx,%0\n"
+	"\tpopl %%ebx\n"
+	:
+	"=r"(_ebx),"=a"(_eax),"=c"(_ecx),"=d"(_edx):"a"(inst)
+
+	);
+
+	cpuid.eax=_eax;
+	cpuid.ebx=_ebx;
+	cpuid.ecx=_ecx;
+	cpuid.edx=_edx;
+	*(unsigned long *) &cpuid.IdString[0]=_ebx;
+	*(unsigned long *) &cpuid.IdString[8]=_ecx;
+	*(unsigned long *) &cpuid.IdString[4]=_edx;
+
+	cpuid.IdString[12]=0;
+
+	return(cpuid);
+}
+/************************************************
+ *  Interpet the Results of the CPUID Instruction
+*/
+
+cpuid_t CPUID_GetId()
+{
+	cpuid_t cpuid,cpuid1,cpuid2;
+	int amd;
+
+	cpuid=CpuId(0);
+	if(!strcmp(cpuid.IdString,"AuthenticAMD"))
+	{
+		cpuid.vendor=amd=CPU_VENDOR_AMD;
+		cpuid2=CpuId(0x80000001);
+	}
+	cpuid1=CpuId(0x00000001);
+
+	if(!cpuid.IdString[0]) {
+
+  		cpuid.nocpuid=1;
+		cpuid.features=0;
+		cpuid.ex_features=0;
+		cpuid.brand=0;
+		cpuid.apic=0;
+		cpuid.ex_family=0;
+		cpuid.ex_model=0;
+		cpuid.cpu_type=0;
+		cpuid.family=3;
+		cpuid.model=0;
+		cpuid.stepping=0;
+		strcpy(cpuid.IdString,"i386");
+		return(cpuid);
+	}
+
+
+/* Lets fill out the rest of the data structure
+ * Opcode 1 (in CPUID 1) holds the processor info
+ *
+ * Store the feature information.
+ */
+	cpuid.nocpuid=0;
+	cpuid.features=cpuid1.edx;
+	cpuid.ex_features=cpuid2.edx;
+	cpuid.brand=cpuid1.ebx&0xff;
+	cpuid.apic=(cpuid1.ebx>>24) &0xff;
+	cpuid.ex_family=(cpuid1.eax>>20) &0xff;
+	cpuid.ex_model=(cpuid1.eax>>16) &0x0f;
+	cpuid.cpu_type=(cpuid1.eax>>12) &0x03;
+	cpuid.family=(cpuid1.eax>>8) &0x0f;
+	cpuid.model=(cpuid1.eax>>4) &0x0f;
+	cpuid.stepping=(cpuid1.eax) &0x0f;
+
+	return(cpuid);
+}
+
+int CPUID_IsFeatureAvailable(unsigned long feature){
+cpuid_t cpuid;
+
+	cpuid=CPUID_GetId();
+	if(cpuid.vendor==CPU_VENDOR_AMD) return(!!(cpuid.ex_features&feature));
+	else return(!!(cpuid.features&feature));
+}
+
+
+
 static BYTE PF[64] = {0,};
 static ULONGLONG cpuHz = 1000000000; /* default to a 1GHz */
 
@@ -521,63 +744,62 @@ VOID WINAPI GetSystemInfo(
 
         }
         memcpy(si,&cachedsi,sizeof(*si));
-#elif defined(__FreeBSD__)
-	{
+#elif defined (__i386__)
+	/* Note for posterity, this code should probably be used for netbsd too */
+	int sse_lvl;
+	cpuid_t cpuId;
 	unsigned int regs[4], regs2[4];
-	int ret, len, num;
+	
 	if (!have_cpuid())
 		regs[0] = 0;			/* No cpuid support -- skip the rest */
 	else
-		do_cpuid(0x00000000, regs);	/* get standard cpuid level and vendor name */
-	if (regs[0]>=0x00000001) {		/* Check for supported cpuid version */
-		do_cpuid(0x00000001, regs2);	/* get cpu features */
-		switch ((regs2[0] >> 8)&0xf) {	/* cpu family */
+		cpuId= CPUID_GetId();
+/* get standard cpuid level and vendor name */
+	if (cpuId.family!=0)
+	{
+		switch (cpuId.family) {	/* cpu family */
 		case 3: cachedsi.dwProcessorType = PROCESSOR_INTEL_386;
 			cachedsi.wProcessorLevel = 3;
 			break;
 		case 4: cachedsi.dwProcessorType = PROCESSOR_INTEL_486;
 			cachedsi.wProcessorLevel = 4;
+			cachedsi.wProcessorRevision = cpuId.stepping;
 			break;
 		case 5:
 			cachedsi.dwProcessorType = PROCESSOR_INTEL_PENTIUM;
 			cachedsi.wProcessorLevel = 5;
+			cachedsi.wProcessorRevision = cpuId.stepping;
 			break;
 		case 6:
 		case 15: /* PPro/2/3/4 has same info as P1 */
 			cachedsi.dwProcessorType = PROCESSOR_INTEL_PENTIUM;
 			cachedsi.wProcessorLevel = 6;
+			cachedsi.wProcessorRevision = cpuId.stepping;
 			break;
 		default:
-			FIXME("unknown FreeBSD cpu family %d, please report! (-> setting to 386)\n", \
-				(regs2[0] >> 8)&0xf);
+			FIXME("unknown FreeBSD cpu family %d, please report! (-> setting to 386)\n", cpuId.family);
 			break;
 		}
-		PF[PF_FLOATING_POINT_EMULATED]     = !(regs2[3] & 1);
-		PF[PF_RDTSC_INSTRUCTION_AVAILABLE] = (regs2[3] & (1 << 4 )) >> 4;
-		PF[PF_COMPARE_EXCHANGE_DOUBLE]     = (regs2[3] & (1 << 8 )) >> 8;
-		PF[PF_MMX_INSTRUCTIONS_AVAILABLE]  = (regs2[3] & (1 << 23)) >> 23;
-		/* Check for OS support of SSE -- Is this used, and should it be sse1 or sse2? */
-		/*len = sizeof(num);
-		ret = sysctlbyname("hw.instruction_sse", &num, &len, NULL, 0);
-		if (!ret)
-			PF[PF_XMMI_INSTRUCTIONS_AVAILABLE] = num;*/
-		
-		if (regs[1] == AUTH &&
-		    regs[3] == ENTI &&
-		    regs[2] == CAMD) {
-			do_cpuid(0x80000000, regs);		/* get vendor cpuid level */
-			if (regs[0]>=0x80000001) {
-				do_cpuid(0x80000001, regs2);	/* get vendor features */
-				PF[PF_3DNOW_INSTRUCTIONS_AVAILABLE] = 
-				    (regs2[3] & (1 << 31 )) >> 31;
-			}
+	
+		PF[PF_FLOATING_POINT_EMULATED]     = !CPUID_IsFeatureAvailable(CPU_FEATURE_FPU);
+		PF[PF_RDTSC_INSTRUCTION_AVAILABLE] = CPUID_IsFeatureAvailable(CPU_FEATURE_TIMESTAMP);
+		PF[PF_COMPARE_EXCHANGE_DOUBLE]     = CPUID_IsFeatureAvailable(CPU_FEATURE_CMPXCHG8);
+		PF[PF_MMX_INSTRUCTIONS_AVAILABLE]  = CPUID_IsFeatureAvailable(CPU_FEATURE_MMX);
+		/* Check for OS support of SSE -- Is this used, and should it be sse1 or sse2
+		*/
+		if ((sse_lvl=OS_SSESupported())){
+			if (sse_lvl >0)PF[PF_XMMI_INSTRUCTIONS_AVAILABLE] = CPUID_IsFeatureAvailable(CPU_FEATURE_SIMD);
+			if(sse_lvl>1) PF[PF_XMMI64_INSTRUCTIONS_AVAILABLE] = CPUID_IsFeatureAvailable(CPU_FEATURE_SIMD2);
 		}
+		PF[PF_PAE_ENABLED] = CPUID_IsFeatureAvailable(CPU_FEATURE_PAE);
+			PF[PF_3DNOW_INSTRUCTIONS_AVAILABLE] = CPUID_IsFeatureAvailable(CPU_FEATURE_3DNOW);
+			    
+		
 	}
-	len = sizeof(num);
-	ret = sysctlbyname("hw.ncpu", &num, &len, NULL, 0);
-	if (!ret)
-		cachedsi.dwNumberOfProcessors = num;
-	}
+
+
+	cachedsi.dwNumberOfProcessors = get_processor_count( );
+
 	memcpy(si,&cachedsi,sizeof(*si));
 #else
 	FIXME("not yet supported on this system\n");

[Index of Archives]     [Gimp for Windows]     [Red Hat]     [Samba]     [Yosemite Camping]     [Graphics Cards]     [Wine Home]

  Powered by Linux