Moved MAKEINTATOM to winbase.h and added Unicode version.
[wine] / misc / cpu.c
1 /*
2  * What processor?
3  *
4  * Copyright 1995,1997 Morten Welinder
5  * Copyright 1997-1998 Marcus Meissner
6  */
7
8 #include <ctype.h>
9 #include <string.h>
10 #include <stdio.h>
11 #include "config.h"
12 #include "wine/port.h"
13 #include "winbase.h"
14 #include "winreg.h"
15 #include "winnt.h"
16 #include "winerror.h"
17 #include "debugtools.h"
18
19 DEFAULT_DEBUG_CHANNEL(reg);
20
21 static BYTE PF[64] = {0,};
22
23 /***********************************************************************
24  *                      GetSystemInfo                   [KERNEL32.@]
25  *
26  * Gets the current system information.
27  *
28  * On the first call it creates cached values, so it doesn't have to determine
29  * them repeatedly. On Linux, the /proc/cpuinfo special file is used.
30  *
31  * It creates a registry subhierarchy, looking like:
32  * \HARDWARE\DESCRIPTION\System\CentralProcessor\<processornumber>\
33  *                                                      Identifier (CPU x86)
34  * Note that there is a hierarchy for every processor installed, so this
35  * supports multiprocessor systems. This is done like Win95 does it, I think.
36  *                                                      
37  * It also creates a cached flag array for IsProcessorFeaturePresent().
38  *
39  * No NULL ptr check for LPSYSTEM_INFO in Win9x.
40  * 
41  * RETURNS
42  *      nothing, really
43  */
44 VOID WINAPI GetSystemInfo(
45         LPSYSTEM_INFO si        /* [out] system information */
46 ) {
47         static int cache = 0;
48         static SYSTEM_INFO cachedsi;
49         HKEY    xhkey=0,hkey;
50
51         if (cache) {
52                 memcpy(si,&cachedsi,sizeof(*si));
53                 return;
54         }
55         memset(PF,0,sizeof(PF));
56
57         /* choose sensible defaults ...
58          * FIXME: perhaps overrideable with precompiler flags?
59          */
60         cachedsi.u.s.wProcessorArchitecture     = PROCESSOR_ARCHITECTURE_INTEL;
61         cachedsi.dwPageSize                     = getpagesize();
62
63         /* FIXME: the two entries below should be computed somehow... */
64         cachedsi.lpMinimumApplicationAddress    = (void *)0x00010000;
65         cachedsi.lpMaximumApplicationAddress    = (void *)0x7FFFFFFF;
66         cachedsi.dwActiveProcessorMask          = 1;
67         cachedsi.dwNumberOfProcessors           = 1;
68         cachedsi.dwProcessorType                = PROCESSOR_INTEL_386;
69         cachedsi.dwAllocationGranularity        = 0x10000;
70         cachedsi.wProcessorLevel                = 3; /* 386 */
71         cachedsi.wProcessorRevision             = 0;
72
73         cache = 1; /* even if there is no more info, we now have a cacheentry */
74         memcpy(si,&cachedsi,sizeof(*si));
75
76         /* Hmm, reasonable processor feature defaults? */
77
78         /* Create these registry keys for all systems
79          * FPU entry is often empty on Windows, so we don't care either */
80         if ( (RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System\\FloatingPointProcessor",&hkey)!=ERROR_SUCCESS)
81           || (RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System\\CentralProcessor",&hkey)!=ERROR_SUCCESS) )
82         {
83             WARN("Unable to write FPU/CPU info to registry\n");
84         }
85
86 #ifdef linux
87         {
88         char buf[20];
89         char line[200];
90         FILE *f = fopen ("/proc/cpuinfo", "r");
91
92         if (!f)
93                 return;
94         xhkey = 0;
95         while (fgets(line,200,f)!=NULL) {
96                 char    *s,*value;
97
98                 /* NOTE: the ':' is the only character we can rely on */
99                 if (!(value = strchr(line,':')))
100                         continue;
101                 /* terminate the valuename */
102                 *value++ = '\0';
103                 /* skip any leading spaces */
104                 while (*value==' ') value++;
105                 if ((s=strchr(value,'\n')))
106                         *s='\0';
107
108                 /* 2.1 method */
109                 if (!strncasecmp(line, "cpu family",strlen("cpu family"))) {
110                         if (isdigit (value[0])) {
111                                 switch (value[0] - '0') {
112                                 case 3: cachedsi.dwProcessorType = PROCESSOR_INTEL_386;
113                                         cachedsi.wProcessorLevel= 3;
114                                         break;
115                                 case 4: cachedsi.dwProcessorType = PROCESSOR_INTEL_486;
116                                         cachedsi.wProcessorLevel= 4;
117                                         break;
118                                 case 5:
119                                 case 6: /* PPro/2/3 has same info as P1 */
120                                         cachedsi.dwProcessorType = PROCESSOR_INTEL_PENTIUM;
121                                         cachedsi.wProcessorLevel= 5;
122                                         break;
123                                 case 1: /* two-figure levels */
124                                     if (value[1] == '5')
125                                     {
126                                         cachedsi.dwProcessorType = PROCESSOR_INTEL_PENTIUM;
127                                         cachedsi.wProcessorLevel= 5;
128                                         break;
129                                     }
130                                     /* fall through */
131                                 default:
132                                         FIXME("unknown cpu family '%s', please report ! (-> setting to 386)\n", value);
133                                         break;
134                                 }
135                         }
136                         /* set the CPU type of the current processor */
137                         sprintf(buf,"CPU %ld",cachedsi.dwProcessorType);
138                         if (xhkey)
139                                 RegSetValueExA(xhkey,"Identifier",0,REG_SZ,buf,strlen(buf));
140                         continue;
141                 }
142                 /* old 2.0 method */
143                 if (!strncasecmp(line, "cpu",strlen("cpu"))) {
144                         if (    isdigit (value[0]) && value[1] == '8' && 
145                                 value[2] == '6' && value[3] == 0
146                         ) {
147                                 switch (value[0] - '0') {
148                                 case 3: cachedsi.dwProcessorType = PROCESSOR_INTEL_386;
149                                         cachedsi.wProcessorLevel= 3;
150                                         break;
151                                 case 4: cachedsi.dwProcessorType = PROCESSOR_INTEL_486;
152                                         cachedsi.wProcessorLevel= 4;
153                                         break;
154                                 case 5:
155                                 case 6: /* PPro/2/3 has same info as P1 */
156                                         cachedsi.dwProcessorType = PROCESSOR_INTEL_PENTIUM;
157                                         cachedsi.wProcessorLevel= 5;
158                                         break;
159                                 default:
160                                         FIXME("unknown Linux 2.0 cpu family '%s', please report ! (-> setting to 386)\n", value);
161                                         break;
162                                 }
163                         }
164                         /* set the CPU type of the current processor
165                          * FIXME: someone reported P4 as being set to
166                          * "              Intel(R) Pentium(R) 4 CPU 1500MHz"
167                          * Do we need to do the same ?
168                          * */
169                         sprintf(buf,"CPU %ld",cachedsi.dwProcessorType);
170                         if (xhkey)
171                                 RegSetValueExA(xhkey,"Identifier",0,REG_SZ,buf,strlen(buf));
172                         continue;
173                 }
174                 if (!strncasecmp(line,"fdiv_bug",strlen("fdiv_bug"))) {
175                         if (!strncasecmp(value,"yes",3))
176                                 PF[PF_FLOATING_POINT_PRECISION_ERRATA] = TRUE;
177
178                         continue;
179                 }
180                 if (!strncasecmp(line,"fpu",strlen("fpu"))) {
181                         if (!strncasecmp(value,"no",2))
182                                 PF[PF_FLOATING_POINT_EMULATED] = TRUE;
183
184                         continue;
185                 }
186                 if (!strncasecmp(line,"processor",strlen("processor"))) {
187                         /* processor number counts up... */
188                         unsigned int x;
189
190                         if (sscanf(value,"%d",&x))
191                                 if (x+1>cachedsi.dwNumberOfProcessors)
192                                         cachedsi.dwNumberOfProcessors=x+1;
193
194                         /* Create a new processor subkey on a multiprocessor
195                          * system
196                          */
197                         sprintf(buf,"%d",x);
198                         if (xhkey)
199                                 RegCloseKey(xhkey);
200                         RegCreateKeyA(hkey,buf,&xhkey);
201                 }
202                 if (!strncasecmp(line,"stepping",strlen("stepping"))) {
203                         int     x;
204
205                         if (sscanf(value,"%d",&x))
206                                 cachedsi.wProcessorRevision = x;
207                 }
208                 if (    !strncasecmp(line,"flags",strlen("flags"))      ||
209                         !strncasecmp(line,"features",strlen("features"))
210                 ) {
211                         if (strstr(value,"cx8"))
212                                 PF[PF_COMPARE_EXCHANGE_DOUBLE] = TRUE;
213                         if (strstr(value,"mmx"))
214                                 PF[PF_MMX_INSTRUCTIONS_AVAILABLE] = TRUE;
215                         if (strstr(value,"tsc"))
216                                 PF[PF_RDTSC_INSTRUCTION_AVAILABLE] = TRUE;
217
218                 }
219         }
220         fclose (f);
221         }
222         memcpy(si,&cachedsi,sizeof(*si));
223 #else  /* linux */
224         /* FIXME: how do we do this on other systems? */
225
226         RegCreateKeyA(hkey,"0",&xhkey);
227         RegSetValueExA(xhkey,"Identifier",0,REG_SZ,"CPU 386",strlen("CPU 386"));
228 #endif  /* !linux */
229         if (xhkey)
230                 RegCloseKey(xhkey);
231         if (hkey)
232                 RegCloseKey(hkey);
233 }
234
235
236 /***********************************************************************
237  *                      IsProcessorFeaturePresent       [KERNEL32.@]
238  * RETURNS:
239  *      TRUE if processor feature present
240  *      FALSE otherwise
241  */
242 BOOL WINAPI IsProcessorFeaturePresent (
243         DWORD feature   /* [in] feature number, see PF_ defines */
244 ) {
245   SYSTEM_INFO si;
246   GetSystemInfo (&si); /* To ensure the information is loaded and cached */
247
248   if (feature < 64)
249     return PF[feature];
250   else
251     return FALSE;
252 }