Better cpuid support

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

 



Improves CPUID instruction support in cpu.c by reorganising the existing 
FREEBSD code. Added more robust cpuid detection with built in 386 detection. 
Code should work for all i386 platforms and has been tested under solaris. 
Created subroutines for detecting number of CPUs and OS SSE support as this 
can't be done in a uniform way across different OSes. Extracted relevant code 
from each section to perform these OS specific operations (Mostly untested).

Both NetBSD and linux could could use cpuid instruction method now to permit 
considerable removal of redundant code. This hasn't been done yet since test 
facilities are only available under Solaris. I would appreciate testing of 
the code under linux and the BSDs. 

Changelog:
Support for general cpuid detection on all _i386_ platforms
Isolated OS specifics to subroutines

attached, cpuid.h cpu.c.diff


#ifndef _CPUID_H_
#define _CPUID_H_

#define CPU_FEATURE_FPU 	0x01   		/* 0. FPU Supported */
#define CPU_FEATURE_VME 	0x02    	/* 1. Virtual Mode Extensions */
#define CPU_FEATURE_DEBUG 	0x04   		/* 2. Breakpoint functions */
#define CPU_FEATURE_PAGESIZE 	0x08		/* 3. Page Size Extensions */
#define CPU_FEATURE_TIMESTAMP 	0x10		/* 4. Time Stanmping */
#define CPU_FEATURE_MSR		0x20 		/* 5. Model Specific registerrs */
#define CPU_FEATURE_PAE		0x40 		/* 6. Page Addres Extensions */
#define CPU_FEATURE_MCE		0x80 		/* 7. Machine Check Exception */
#define CPU_FEATURE_CMPXCHG8	0x100  		/* 8. Compare exchange 8 byte instruction */
#define CPU_FEATURE_APIC	0x200  		/* 9. APIC present */
#define CPU_FEATURE_10		0x400		/* 10 CPU Feature 10 */.
#define CPU_FEATURE_SYSCALL	0x800 	 	/* 11. Fast system call extensions */
#define CPU_FEATURE_MTRR	0x1000  	/* 12. Memory Type/range regs supported */
#define CPU_FEATURE_GPAGE	0x2000  	/* 13. Global paging register */
#define CPU_FEATURE_MCA		0x4000  	/* 14. Machine CHeck Architecture */
#define CPU_FEATURE_CMI		0x8000  	/* 15. COnditional move instructions */
#define CPU_FEATURE_PAT		0x10000l  	/* 16. Page Atribute table */
#define CPU_FEATURE_PAE36    	0x20000l  	/* 17. Page address extensions to 36 bits */
#define CPU_FEATURE_PSN		0x40000l	/* 18  Processor Serial Number.*/
#define CPU_FEATURE_CLFLUSH	0x80000l	/* 19. Has CLFLUSH Instruction */
#define CPU_FEATURE_20		0x100000l	/* 20. */
#define CPU_FEATURE_DS		0x200000l	/* 21 Has Debug Store (Intel) */
#define CPU_FEATURE_AMDMMX	0x400000l	/* 22 AMD MMX Extensions */
#define CPU_FEATURE_MMX		0x800000l	/* 23 INTEL MMX */
#define CPU_FEATURE_FXSAVE	0x1000000l	/* 24 Fast FP Save */
#define CPU_FEATURE_SIMD	0x2000000l	/* 25 Intel SIMD */
#define CPU_FEATURE_SIMD2	0x4000000l	/* 26 INtel SIMD 2 */
#define CPU_FEATURE_SNOOP	0x8000000l
#define CPU_FEATURE_28		0x10000000l	/* 28 */
#define CPU_FEATURE_THERMMON	0x20000000l	/* 29 */
#define CPU_FEATURE_3DNOWEXT	0x40000000l	/* 30 AMD 3DNow! Extensions */
#define CPU_FEATURE_3DNOW	0x80000000l	/* 31 AMD 3DNow! */

#define CPU_VENDOR_AMD 1
#define CPU_VENDOR_INTEL 0
#define CPU_VENDOR_CYRIX 2


typedef struct cpuid_s
{
	unsigned long eax,ebx,ecx,edx;
	int brand,apic,vendor;
	int ex_family,ex_model,cpu_type,family,model,stepping;
	unsigned long features,ex_features;
	char IdString[13];
	int nocpuid;

} cpuid_t;

cpuid_t CPUID_GetId(void);
int CPUID_IsVendor(int vendor);
int CPUID_IsFeatureAvailable(unsigned long feature);

#endif
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:52:14 -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,44 +62,227 @@ WINE_DEFAULT_DEBUG_CHANNEL(reg);
 #define ENTI	0x69746e65	/* "enti" */
 #define CAMD	0x444d4163	/* "cAMD" */
 
-/* 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.
+static inline 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
  */
-static inline void do_cpuid(int ax, int *p)
+    {
+        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)
 {
-#ifdef __i386__
-	__asm__("pushl %%ebx\n\t"
-                "cpuid\n\t"
-                "movl %%ebx, %%esi\n\t"
-                "popl %%ebx"
-                : "=a" (p[0]), "=S" (p[1]), "=c" (p[2]), "=d" (p[3])
-                :  "0" (ax));
+/* 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;
+
 }
 
-/* From xf86info havecpuid.c 1.11 */
-static inline int have_cpuid(void)
+
+static cpuid_t CpuId(unsigned long inst)
 {
-#ifdef __i386__
-	unsigned int f1, f2;
-	__asm__("pushfl\n\t"
-                "pushfl\n\t"
-                "popl %0\n\t"
-                "movl %0,%1\n\t"
-                "xorl %2,%0\n\t"
-                "pushl %0\n\t"
-                "popfl\n\t"
-                "pushfl\n\t"
-                "popl %0\n\t"
-                "popfl"
-                : "=&r" (f1), "=&r" (f2)
-                : "ir" (0x00200000));
-	return ((f1^f2) & 0x00200000) != 0;
-#else
-        return 0;
-#endif
+
+	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
+*/
+
+static 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);
 }
 
+static 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 +707,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