/[svn]/misc/trunk/win32_installer/cpudesc/cpudesc.c
ViewVC logotype

Contents of /misc/trunk/win32_installer/cpudesc/cpudesc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1891 - (show annotations) (download)
Tue Apr 28 14:13:06 2009 UTC (14 years, 11 months ago) by schoenebeck
File MIME type: text/plain
File size: 16815 byte(s)
* preparations for the next installer, carrying one native 64 bit
  and two native 32 bit binary types (686 SSE and 686 no SSE)

1 /* NSIS plug-in for getting a bit of CPU information.
2 Version 1.2, July 2003.
3 Typed and clicked by Peter Mason, CSIRO DEM MMTG. mailto://peter.mason@csiro.au.
4 The MHz timing was done using code that was pretty much copied from Pavlos Touboulidis' CPUTEST code.
5 The lean exception handler wrapped around the timing code was done using Jeremy Gordon's tutorial on writing
6 lightweight win32 exception handlers in assembler. (His web-page is www.GoDevTool.com.)
7 The CPUID stuff was done using Intel and AMD's manuals on what CPUID means in their respective microcosms.
8 Best viewed with <TAB> == 2 spaces.
9
10 There's only one routine here - tell() - and its output is a string on the NSIS stack.
11 This string always has the same fields in exactly the same place. (Easier to extract values that way.) It looks like this:
12
13 INTELP=d AMD=add PPRO=b MMX=d SSE=b SSE2=b 3DNOW=d ARCH=dd LEVEL=dd NCPU=dd MHZ=ddddd RAM=dddd
14
15 Here, "d" means a decimal digit (0..9), "a" means an alphabetic character (A..Z) and "b" means a boolean digit (0 or 1).
16 ITELP: Values range [0..4].
17 0: Not a genuine Intel CPU (or a very, VERY old one).
18 1: Pentium or Pentium with MMX. (Check the MMX field if you want to know about the CPU's MMX support.)
19 2: Pentium Pro, II or Celeron. (May or may not have MMX - PPros don't, the others do. Check the MMX field.)
20 3: Pentium III or P3 (old) Xeon. (Always has MMX and SSE.)
21 4: Pentium IV or (new) Xeon. (Always has MMX, SSE and SSE2.)
22 AMD: A bit more complicated...
23 000: Not an authentic AMD CPU (or a very old one).
24 Kdd: An old K-series. "dd" is either 05 for a K5 or 06 for a K6.
25 (Pentium compatible. K5s have no MMX or 3DNOW. K6s have standard MMX, and later models have basic 3DNOW.)
26 Add: An Athlon or a Duron. "dd" is the model number (goes from 01 to 10).
27 (Pentium II compatible. All of these have extended MMX and extended 3DNOW. None have any SSE.)
28 Odd: An opteron. "dd" gives the model number.
29 (Pentium IV compatible. This CPU's got everything, it seems.)
30 PPRO: Values range [0..1].
31 0: Not compatible with the Intel Pentium Pro processor.
32 1: Compatible with the Intel Pentium Pro processor.
33 MMX: Values range [0..2].
34 0: No MMX support.
35 1: Standard Intel MMX support.
36 2: Standard MMX support plus AMD MMX extensions.
37 SSE: Values range [0..1].
38 0: No SSE support.
39 1: Supports SSE (Intel's Streaming SIMD extensions, P3-style).
40 SSE2: Values range [0..1].
41 0: No SSE2 support.
42 1: Supports SSE2 (Intel's Streaming SIMD extensions 2, P4-style).
43 3DNOW: Values range [0..2].
44 0: No 3DNOW support.
45 1: Standard AMD 3DNOW support.
46 2: Standard 3DNOW support plus AMD 3DNOW extensions.
47 ARCH: Values range [00..10].
48 00: 32-bit Intel or compatible
49 01: MIPS (did NT 3.5, apparently)
50 02: DEC Alpha. (Yes, DEC. I can't bring myself to call it COMPAQ.)
51 03: PowerPC
52 04: SHX (?)
53 05: ARM (Acorn / Advanced Risc Machine, I presume. I don't think anyone's going to see this running Windows?)
54 06: 64-bit Intel.
55 07: 64-bit Alpha
56 08: MSIL (?)
57 09: 64-bit AMD
58 10: 32-bit Intel doing Win64 (?)
59 LEVEL: "Processor level", like what you see in the main processor environment variable. Sort-of useless, really.
60 NCPU: The number of processors available. (Affected by that "Hyper" business that the new XEONs can do, I think.)
61 MHZ: The CPU's internal clock speed in MHz (approx).
62 RAM: The amount of RAM (physical memory) in megabytes (rounded).
63
64
65 Compilation:
66 /nologo /MT /W3 /vd0 /Og /Os /Oy /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "EXDLL_EXPORTS" /Fo"Release/" /Fd"Release/" /Zl /FD /c
67 Linking:
68 kernel32.lib user32.lib advapi32.lib /nologo /entry:"_DllMainCRTStartup" /dll /incremental:no /pdb:"Release/cpudesc.pdb" /machine:I386 /nodefaultlib /out:"Release/cpudesc.dll" /implib:"Release/cpudesc.lib" /MERGE:.rdata=.text /MERGE:.text=.text /MERGE:.reloc=.text /OPT:REF /FILEALIGN:512
69
70 */
71 #include <windows.h>
72 #include "../ExDLL/exdll.h"
73
74 /*****************************************/
75 // Gets the MHz timing stored by Windows in the registry. Returns 0 MHz if there's a problem reading the expected registry value.
76 // This is used as a fall-back for when the timer routine can't be run. I don't know if this registry value is stored consistently
77 // for different versions of Windows.
78 static int mhzfromreg(void)
79 {
80 HKEY k;
81 DWORD drv, ndrv=4;
82 int rv=0;
83 if( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", 0, KEY_READ, &k ) ) {
84 if( ERROR_SUCCESS == RegQueryValueEx( k, "~MHz", 0, NULL, (LPBYTE)(&drv), &ndrv ) ) rv=(int)drv;
85 RegCloseKey(k);
86 }
87 return rv;
88 }
89 /*****************************************/
90 // This lot pretty much lifted from Pavlos Touboulidis' CPUTEST code.
91 // (Note _cdecl in case this isn't the default compilation setting. The assembly calling routine expects this protocol.)
92 static void _cdecl delay(int foroverhead)
93 {
94 LARGE_INTEGER c1, c2;
95 __int64 x, y;
96 if( !QueryPerformanceFrequency(&c1) ) return;
97 //x=c1.QuadPart>>4; //hard coded for 62.5ms interval
98 __asm { //avoid bringing in the CRTL for dividing 64-bit c1 by 16 (to get an interval of 62.5 ms)
99 push eax
100 mov eax,c1.HighPart
101 shrd c1.LowPart,eax,4
102 shr eax,4 //yes, the SHRD above won't have changed EAX.
103 mov c1.HighPart,eax
104 pop eax
105 }
106 x=c1.QuadPart;
107 QueryPerformanceCounter(&c1);
108 do {
109 QueryPerformanceCounter(&c2);
110 y=c2.QuadPart-c1.QuadPart;
111 } while( foroverhead ? (y==x) : (y<x) );
112 return;
113 }
114 /**************/
115 /* Unfortunately, there's no decent way (in non-privileged mode) to tell if the CPU is allowed to execute the RDTSC
116 instruction in non-privileged mode. The relevant status bit is bit 2 in "CR4" (control register 4), and we can't
117 even look at CR4 in user mode.
118 So I have resorted to the indiscrete way of letting it f.. er, throw an exception if it must...
119 In the event of an exception (due to disallowed use of RDTSC), the exception will get caught and the MHz value
120 pulled out of the registry earlier will silently get returned instead of a timed value. The exception handler
121 is very simple-minded. It doesn't check what sort of exception occurred, it just "handles" it by jumping to the
122 end of the timer code. (If it gets called, it's assumed that it was due to a disallowed call to RDTSC.)
123 BTW, this code seems to produce a good answer on a dual CPU PC.
124 */
125 static void mhzfromtimer(int *mhz)
126 {
127 HANDLE hproc=GetCurrentProcess(), hthr=GetCurrentThread();
128 DWORD oldpc=GetPriorityClass(hproc); // old priority class
129 DWORD eax0, edx0, tmhz=*mhz;
130 int oldtp=GetThreadPriority(hthr); // old thread priority
131 //if( GetProcessAffinityMask( hproc, &pam, &sam ) && (pam>1) ) {
132 // tam=SetThreadAffinityMask( hthr, 1 ); //lock onto the primary CPU if 2+ CPUs
133 // Sleep(1); //maybe we weren't on the primary CPU? Hopefully, we will be after this. (I don't know if SetThreadAffinityMask() does the necessary.)
134 //}
135 SetPriorityClass( hproc, HIGH_PRIORITY_CLASS ); //that should be sufficient
136 SetThreadPriority( hthr, THREAD_PRIORITY_TIME_CRITICAL ); //...and that
137 __asm {
138 pushad //just save all general registers
139 push offset term //"safe place" for exception handler's return (our own addition to the structure)
140 push offset exh //exception handler's start (expected in structure)
141 push dword ptr fs:[0] //becomes "next handler" (expected in structure)
142 mov fs:[0], esp //rig the top ERR structure to be for our handler (we've just built its structure here on the stack)
143 RDTSC //(Start of the timing job - what we came here for)
144 mov esi,eax
145 mov edi,edx
146 push 0
147 call delay //delay(0)
148 pop ecx
149 RDTSC
150 sub eax,esi
151 sbb edx,edi
152 mov eax0,eax
153 mov edx0,edx //that's the main count in edx0:eax0
154 RDTSC
155 mov esi,eax
156 mov edi,edx
157 push 1
158 call delay //delay(1)
159 pop ecx
160 RDTSC
161 sub eax,esi
162 sbb edx,edi
163 sub eax0,eax
164 sbb edx0,edx //that's the overhead count subtracted from edx0:eax0
165 mov eax,eax0
166 mov edx,edx0
167 mov ecx,62500 //timed over 62.5 ms
168 div ecx
169 mov tmhz,eax //..and THERE'S OUR RESULT
170 term: pop dword ptr fs:[0] //reinstate old top handler
171 add esp,8 //(chuck away addresses pushed onto the stack for our handler)
172 popad //restore all general registers
173 jmp fin
174 exh: push edi //Start of exception handler
175 push esi
176 mov edi, [esp+10h] //our ERR structure
177 mov esi, [esp+14h] //context structure
178 mov [esi+0C4h], edi //insert new esp into the context structure
179 mov eax, [edi+8] //address of safe place "term" that we stored in our ERR structure
180 mov [esi+0B8h], eax //insert new eip
181 xor eax, eax //return code 0 means we've handled it
182 pop esi
183 pop edi
184 ret //End of exception handler
185 fin:
186 }
187 *mhz=tmhz;
188 SetThreadPriority( hthr, oldtp ); //restore it
189 SetPriorityClass( hproc, oldpc ); //...and it
190 //if(tam) SetThreadAffinityMask( hthr, tam ); //..and it
191 return;
192 }
193 /*****************************************/
194 void __declspec(dllexport) tell(HWND hwndParent, int string_size, char *variables, stack_t **stacktop)
195 {
196 EXDLL_INIT();
197 {
198 char ostr[256];
199 SYSTEM_INFO si;
200 MEMORYSTATUS ms;
201 unsigned int mhz=0, ram=0;
202 char arch=0, level=0, pprocompat=0, hasmmx=0, has3dnow=0, hassse=0, hassse2=0, intelpentium=0, amd=0, amdlet='0', ncpu=1, tsc=0;
203 GlobalMemoryStatus(&ms);
204 if( 0xfff90000 <= (ram=ms.dwTotalPhys) ) ram=4*1024; //Just say it's 4GB!
205 else ram = (ram+512*1024) / (1024*1024); //report megabytes (rounded)
206 mhz=mhzfromreg();
207 GetSystemInfo( &si );
208 arch =(char)si.wProcessorArchitecture; //type code (e.g., 0==intel)
209 level =(char)si.wProcessorLevel; //CPU "level"
210 ncpu =(char)si.dwNumberOfProcessors;
211 if(!ncpu) ncpu=1;
212 hasmmx =IsProcessorFeaturePresent( PF_MMX_INSTRUCTIONS_AVAILABLE ); //just about everything these days?
213 has3dnow=IsProcessorFeaturePresent( PF_3DNOW_INSTRUCTIONS_AVAILABLE ); //for some AMDs?
214 hassse =IsProcessorFeaturePresent( PF_XMMI_INSTRUCTIONS_AVAILABLE ); //Pentium III or better?
215 hassse2 =IsProcessorFeaturePresent( PF_XMMI64_INSTRUCTIONS_AVAILABLE ); //umm? (This doesn't seem to work for P4s)
216 if( (0==arch) || (6==arch) || (7==arch) || (9==arch) || (10==arch) ) { //Intel compatible architecture - let's get some better detail
217 __asm {
218 push eax
219 push ebx
220 push ecx
221 push edx
222 mov ebx,200000h
223 pushfd
224 pop eax
225 mov ecx,eax ;save the flags reg's original contents
226 xor eax,ebx ;try to change bit 21 in the flags register
227 push eax
228 popfd ;(that's our attempt to change the flags reg)
229 pushfd
230 pop eax ;now let's see if it really got changed
231 xor eax,ecx
232 and eax,ebx ;test just for a change of bit 21 (being a.r. here)
233 jz bye ;if our change didn't stick then we can't execute the CPUID instruction
234 xor eax,eax
235 cpuid ;is it a real Intel or AMD CPU?
236 cmp ebx,756E6547h ;"Genu"
237 jnz ckamd
238 cmp edx,49656E69h ;"ineI"
239 jnz bye
240 cmp ecx,6C65746Eh ;"ntel"
241 jnz bye
242 mov byte ptr intelpentium, 1 ;assume vanilla pentium for now
243 jmp cont1
244 ckamd: cmp ebx,68747541h ;"Auth"
245 jnz bye
246 cmp edx,69746E65h ;"enti"
247 jnz bye
248 cmp ecx,444D4163h ;"cAMD"
249 jnz bye
250 mov byte ptr amd, 5
251 mov byte ptr amdlet, 'K' ;assume a K5 for the time being
252 cont1: xor eax,eax
253 mov byte ptr hasmmx, al
254 mov byte ptr has3dnow, al
255 mov byte ptr hassse, al
256 mov byte ptr hassse2, al ;we'll be revising our opinions of mmx, 3dnow, sse and sse2
257 inc eax
258 cpuid ;get some detail
259 mov cl,10h ;this bit is set if the CPU has the RDTSC instruction
260 and cl,dl
261 jz notsc
262 ;mov ecx,CR4 ;can't do it - it's a privileged instruction
263 ;and cl,4
264 ;jnz notsc
265 inc byte ptr tsc ;MAYBE - won't work if bit 2 of CR4 is set, but we could only check that if we were running at privilege level 0
266 notsc: mov ecx,800000h ;this bit is set if the CPU does MMX
267 and ecx,edx
268 jz nommx
269 inc byte ptr hasmmx
270 nommx: xor bl,bl
271 mov ecx,2000000h ;this bit is set if the CPU does SSE
272 and ecx,edx
273 jz nosse1
274 inc bl
275 inc byte ptr hassse ;P3-style SSE
276 nosse1: mov ecx,4000000h ;this bit is set if the CPU does SSE2
277 and ecx,edx
278 jz nosse2
279 inc bl
280 inc byte ptr hassse2 ;P4-style SSE2
281 nosse2: cmp byte ptr amd, 0
282 jnz amd2 ;further AMD-specific checks elsewhere
283 shr eax,8 ;further Intel-specific cheks here...
284 and al,0Fh ;get the family number
285 cmp al,6h
286 jb bye
287 inc byte ptr intelpentium ;so far it looks like an old PPro, old Celeron, old P2 or better
288 inc byte ptr pprocompat
289 cmp bl,0
290 jz bye ;no SSE - it's an old thing and we're done
291 inc byte ptr intelpentium ;if it's got some SSE support then so far it looks like a P3 or better
292 cmp al,0Fh
293 jnz bye
294 inc byte ptr intelpentium ;you know, it looks like a P4
295 jmp bye
296 amd2: mov ecx,1000000h ;this bit is set if the CPU does FXSR
297 and ecx,edx ;dunno about Intel, but with AMD we should apparently check both bits 24 and 25 for SSE.
298 jnz yessse
299 mov byte ptr hassse, 0 ;turn off SSE if the CPU doesn't do FXSR (in addition to SSE)
300 yessse: mov dl,0fh ;further AMD checks...
301 mov ebx,eax
302 shr eax,4
303 and al,dl
304 cmp al,dl
305 jnz noxm ;model number is complete as-is
306 and ah,0f0h ;(extended model number)
307 add al,ah ;full model number now in AL
308 noxm: shr ebx,8
309 and bl,dl
310 cmp bl,dl
311 jnz noxf ;family number is complete as-is
312 mov edx,ebx
313 shr edx,12
314 and dl,0ffh ;(extended family number)
315 add bl,dl ;full family number now in bl
316 noxf: cmp bl,5
317 ja amda ;looks like an athlon or better
318 cmp al,6
319 jb bye ;looks like a k5 - we're done
320 mov byte ptr amd, 6 ;looks like a k6
321 jmp amd4
322 amda: inc byte ptr pprocompat ;athlons / durons are Pentium II compatible
323 cmp bl,14
324 ja amdo ;looks like an opteron
325 mov byte ptr amdlet, 'A' ;looks like an athlon / duron
326 jmp amd3
327 amdo: mov byte ptr amdlet, 'O' ;(looks like an opteron)
328 amd3: mov byte ptr amd, al ;report the model number for athlons / durons / opterons
329 amd4: mov eax,80000001h
330 cpuid ;get some AMD specifics
331 mov ecx,40000h
332 and ecx,edx
333 jz amd5
334 inc byte ptr hasmmx ;it's got AMD MMX extensions
335 amd5: mov ecx,8000000h
336 mov ebx,ecx
337 and ecx,edx
338 jz bye
339 inc byte ptr has3dnow ;it's got basic 3DNOW
340 shr ebx,1
341 and ebx,edx
342 jz bye
343 inc byte ptr has3dnow ;it's got extended 3DNOW
344 bye: pop edx
345 pop ecx
346 pop ebx
347 pop eax
348 }
349 if(tsc) mhzfromtimer(&mhz); //get CPU MHz via timing if it appears that we can execute the RDTSC instruction
350 }
351 wsprintf( ostr, "INTELP=%1d AMD=%c%2.2d PPRO=%1d MMX=%1d SSE=%1d SSE2=%1d 3DNOW=%1d ARCH=%2.2d LEVEL=%2.2d NCPU=%2.2d MHZ=%5.5d RAM=%4.4d",
352 intelpentium, amdlet, amd, pprocompat, hasmmx, hassse, hassse2, has3dnow, arch, level, ncpu, mhz, ram );
353 pushstring(ostr);
354 }
355 return;
356 }
357 /*****************************************/
358 BOOL WINAPI _DllMainCRTStartup(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved)
359 {
360 hInst=hInst; ul_reason_for_call=ul_reason_for_call; lpReserved=lpReserved;
361 return TRUE;
362 }
363 /*****************************************/

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC