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