VirtualBox

root/trunk/include/iprt/asm.h

Revision 15216, 157.0 kB (checked in by vboxsync, 1 month ago)

iprt/asm.h: Fixed buggy ASMIsIntelCpu and ASMIsIntelCpuEx; the latter was OR'ing instead of ANDing, while the former was reading the wrong leaf.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /** @file
2  * IPRT - Assembly Functions.
3  */
4
5 /*
6  * Copyright (C) 2006-2007 Sun Microsystems, Inc.
7  *
8  * This file is part of VirtualBox Open Source Edition (OSE), as
9  * available from http://www.virtualbox.org. This file is free software;
10  * you can redistribute it and/or modify it under the terms of the GNU
11  * General Public License (GPL) as published by the Free Software
12  * Foundation, in version 2 as it comes in the "COPYING" file of the
13  * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14  * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15  *
16  * The contents of this file may alternatively be used under the terms
17  * of the Common Development and Distribution License Version 1.0
18  * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
19  * VirtualBox OSE distribution, in which case the provisions of the
20  * CDDL are applicable instead of those of the GPL.
21  *
22  * You may elect to license modified versions of this file under the
23  * terms and conditions of either the GPL or the CDDL or both.
24  *
25  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
26  * Clara, CA 95054 USA or visit http://www.sun.com if you need
27  * additional information or have any questions.
28  */
29
30 #ifndef ___iprt_asm_h
31 #define ___iprt_asm_h
32
33 #include <iprt/cdefs.h>
34 #include <iprt/types.h>
35 #include <iprt/assert.h>
36 /** @todo #include <iprt/param.h> for PAGE_SIZE. */
37 /** @def RT_INLINE_ASM_USES_INTRIN
38  * Defined as 1 if we're using a _MSC_VER 1400.
39  * Otherwise defined as 0.
40  */
41
42 #ifdef _MSC_VER
43 # if _MSC_VER >= 1400
44 define RT_INLINE_ASM_USES_INTRIN 1
45 include <intrin.h>
46    /* Emit the intrinsics at all optimization levels. */
47 pragma intrinsic(_ReadWriteBarrier)
48 pragma intrinsic(__cpuid)
49 pragma intrinsic(_enable)
50 pragma intrinsic(_disable)
51 pragma intrinsic(__rdtsc)
52 pragma intrinsic(__readmsr)
53 pragma intrinsic(__writemsr)
54 pragma intrinsic(__outbyte)
55 pragma intrinsic(__outword)
56 pragma intrinsic(__outdword)
57 pragma intrinsic(__inbyte)
58 pragma intrinsic(__inword)
59 pragma intrinsic(__indword)
60 pragma intrinsic(__invlpg)
61 pragma intrinsic(__stosd)
62 pragma intrinsic(__stosw)
63 pragma intrinsic(__stosb)
64 pragma intrinsic(__readcr0)
65 pragma intrinsic(__readcr2)
66 pragma intrinsic(__readcr3)
67 pragma intrinsic(__readcr4)
68 pragma intrinsic(__writecr0)
69 pragma intrinsic(__writecr3)
70 pragma intrinsic(__writecr4)
71 pragma intrinsic(__readdr)
72 pragma intrinsic(__writedr)
73 pragma intrinsic(_BitScanForward)
74 pragma intrinsic(_BitScanReverse)
75 pragma intrinsic(_bittest)
76 pragma intrinsic(_bittestandset)
77 pragma intrinsic(_bittestandreset)
78 pragma intrinsic(_bittestandcomplement)
79 pragma intrinsic(_byteswap_ushort)
80 pragma intrinsic(_byteswap_ulong)
81 pragma intrinsic(_interlockedbittestandset)
82 pragma intrinsic(_interlockedbittestandreset)
83 pragma intrinsic(_InterlockedAnd)
84 pragma intrinsic(_InterlockedOr)
85 pragma intrinsic(_InterlockedIncrement)
86 pragma intrinsic(_InterlockedDecrement)
87 pragma intrinsic(_InterlockedExchange)
88 pragma intrinsic(_InterlockedExchangeAdd)
89 pragma intrinsic(_InterlockedCompareExchange)
90 pragma intrinsic(_InterlockedCompareExchange64)
91 ifdef RT_ARCH_AMD64
92 #   pragma intrinsic(__stosq)
93 #   pragma intrinsic(__readcr8)
94 #   pragma intrinsic(__writecr8)
95 #   pragma intrinsic(_byteswap_uint64)
96 #   pragma intrinsic(_InterlockedExchange64)
97 endif
98 # endif
99 #endif
100 #ifndef RT_INLINE_ASM_USES_INTRIN
101 # define RT_INLINE_ASM_USES_INTRIN 0
102 #endif
103
104
105
106 /** @defgroup grp_asm       ASM - Assembly Routines
107  * @ingroup grp_rt
108  *
109  * @remarks The difference between ordered and unordered atomic operations are that
110  *          the former will complete outstanding reads and writes before continuing
111  *          while the latter doesn't make any promisses about the order. Ordered
112  *          operations doesn't, it seems, make any 100% promise wrt to whether
113  *          the operation will complete before any subsequent memory access.
114  *          (please, correct if wrong.)
115  *
116  *          ASMAtomicSomething operations are all ordered, while ASMAtomicUoSomething
117  *          are unordered (note the Uo).
118  *
119  * @remarks Some remarks about __volatile__: Without this keyword gcc is allowed to reorder
120  *          or even optimize assembler instructions away. For instance, in the following code
121  *          the second rdmsr instruction is optimized away because gcc treats that instruction
122  *          as deterministic:
123  *
124  *            @code
125  *            static inline uint64_t rdmsr_low(int idx)
126  *            {
127  *              uint32_t low;
128  *              __asm__ ("rdmsr" : "=a"(low) : "c"(idx) : "edx");
129  *            }
130  *            ...
131  *            uint32_t msr1 = rdmsr_low(1);
132  *            foo(msr1);
133  *            msr1 = rdmsr_low(1);
134  *            bar(msr1);
135  *            @endcode
136  *
137  *          The input parameter of rdmsr_low is the same for both calls and therefore gcc will
138  *          use the result of the first call as input parameter for bar() as well. For rdmsr this
139  *          is not acceptable as this instruction is _not_ deterministic. This applies to reading
140  *          machine status information in general.
141  *
142  * @{
143  */
144
145 /** @def RT_INLINE_ASM_EXTERNAL
146  * Defined as 1 if the compiler does not support inline assembly.
147  * The ASM* functions will then be implemented in an external .asm file.
148  *
149  * @remark  At the present time it's unconfirmed whether or not Microsoft skipped
150  *          inline assembly in their AMD64 compiler.
151  */
152 #if defined(_MSC_VER) && defined(RT_ARCH_AMD64)
153 # define RT_INLINE_ASM_EXTERNAL 1
154 #else
155 # define RT_INLINE_ASM_EXTERNAL 0
156 #endif
157
158 /** @def RT_INLINE_ASM_GNU_STYLE
159  * Defined as 1 if the compiler understands GNU style inline assembly.
160  */
161 #if defined(_MSC_VER)
162 # define RT_INLINE_ASM_GNU_STYLE 0
163 #else
164 # define RT_INLINE_ASM_GNU_STYLE 1
165 #endif
166
167
168 /** @todo find a more proper place for this structure? */
169 #pragma pack(1)
170 /** IDTR */
171 typedef struct RTIDTR
172 {
173     /** Size of the IDT. */
174     uint16_t    cbIdt;
175     /** Address of the IDT. */
176     uintptr_t   pIdt;
177 } RTIDTR, *PRTIDTR;
178 #pragma pack()
179
180 #pragma pack(1)
181 /** GDTR */
182 typedef struct RTGDTR
183 {
184     /** Size of the GDT. */
185     uint16_t    cbGdt;
186     /** Address of the GDT. */
187     uintptr_t   pGdt;
188 } RTGDTR, *PRTGDTR;
189 #pragma pack()
190
191
192 /** @def ASMReturnAddress
193  * Gets the return address of the current (or calling if you like) function or method.
194  */
195 #ifdef _MSC_VER
196 # ifdef __cplusplus
197 extern "C"
198 # endif
199 void * _ReturnAddress(void);
200 # pragma intrinsic(_ReturnAddress)
201 # define ASMReturnAddress() _ReturnAddress()
202 #elif defined(__GNUC__) || defined(DOXYGEN_RUNNING)
203 # define ASMReturnAddress() __builtin_return_address(0)
204 #else
205 # error "Unsupported compiler."
206 #endif
207
208
209 /**
210  * Gets the content of the IDTR CPU register.
211  * @param   pIdtr   Where to store the IDTR contents.
212  */
213 #if RT_INLINE_ASM_EXTERNAL
214 DECLASM(void) ASMGetIDTR(PRTIDTR pIdtr);
215 #else
216 DECLINLINE(void) ASMGetIDTR(PRTIDTR pIdtr)
217 {
218 # if RT_INLINE_ASM_GNU_STYLE
219     __asm__ __volatile__ ("sidt %0" : "=m" (*pIdtr));
220 # else
221     __asm
222     {
223 ifdef RT_ARCH_AMD64
224         mov     rax, [pIdtr]
225         sidt    [rax]
226 else
227         mov     eax, [pIdtr]
228         sidt    [eax]
229 endif
230     }
231 # endif
232 }
233 #endif
234
235
236 /**
237  * Sets the content of the IDTR CPU register.
238  * @param   pIdtr   Where to load the IDTR contents from
239  */
240 #if RT_INLINE_ASM_EXTERNAL
241 DECLASM(void) ASMSetIDTR(const RTIDTR *pIdtr);
242 #else
243 DECLINLINE(void) ASMSetIDTR(const RTIDTR *pIdtr)
244 {
245 # if RT_INLINE_ASM_GNU_STYLE
246     __asm__ __volatile__ ("lidt %0" : : "m" (*pIdtr));
247 # else
248     __asm
249     {
250 ifdef RT_ARCH_AMD64
251         mov     rax, [pIdtr]
252         lidt    [rax]
253 else
254         mov     eax, [pIdtr]
255         lidt    [eax]
256 endif
257     }
258 # endif
259 }
260 #endif
261
262
263 /**
264  * Gets the content of the GDTR CPU register.
265  * @param   pGdtr   Where to store the GDTR contents.
266  */
267 #if RT_INLINE_ASM_EXTERNAL
268 DECLASM(void) ASMGetGDTR(PRTGDTR pGdtr);
269 #else
270 DECLINLINE(void) ASMGetGDTR(PRTGDTR pGdtr)
271 {
272 # if RT_INLINE_ASM_GNU_STYLE
273     __asm__ __volatile__ ("sgdt %0" : "=m" (*pGdtr));
274 # else
275     __asm
276     {
277 ifdef RT_ARCH_AMD64
278         mov     rax, [pGdtr]
279         sgdt    [rax]
280 else
281         mov     eax, [pGdtr]
282         sgdt    [eax]
283 endif
284     }
285 # endif
286 }
287 #endif
288
289 /**
290  * Get the cs register.
291  * @returns cs.
292  */
293 #if RT_INLINE_ASM_EXTERNAL
294 DECLASM(RTSEL) ASMGetCS(void);
295 #else
296 DECLINLINE(RTSEL) ASMGetCS(void)
297 {
298     RTSEL SelCS;
299 # if RT_INLINE_ASM_GNU_STYLE
300     __asm__ __volatile__("movw  %%cs, %0\n\t" : "=r" (SelCS));
301 # else
302     __asm
303     {
304         mov     ax, cs
305         mov     [SelCS], ax
306     }
307 # endif
308     return SelCS;
309 }
310 #endif
311
312
313 /**
314  * Get the DS register.
315  * @returns DS.
316  */
317 #if RT_INLINE_ASM_EXTERNAL
318 DECLASM(RTSEL) ASMGetDS(void);
319 #else
320 DECLINLINE(RTSEL) ASMGetDS(void)
321 {
322     RTSEL SelDS;
323 # if RT_INLINE_ASM_GNU_STYLE
324     __asm__ __volatile__("movw  %%ds, %0\n\t" : "=r" (SelDS));
325 # else
326     __asm
327     {
328         mov     ax, ds
329         mov     [SelDS], ax
330     }
331 # endif
332     return SelDS;
333 }
334 #endif
335
336
337 /**
338  * Get the ES register.
339  * @returns ES.
340  */
341 #if RT_INLINE_ASM_EXTERNAL
342 DECLASM(RTSEL) ASMGetES(void);
343 #else
344 DECLINLINE(RTSEL) ASMGetES(void)
345 {
346     RTSEL SelES;
347 # if RT_INLINE_ASM_GNU_STYLE
348     __asm__ __volatile__("movw  %%es, %0\n\t" : "=r" (SelES));
349 # else
350     __asm
351     {
352         mov     ax, es
353         mov     [SelES], ax
354     }
355 # endif
356     return SelES;
357 }
358 #endif
359
360
361 /**
362  * Get the FS register.
363  * @returns FS.
364  */
365 #if RT_INLINE_ASM_EXTERNAL
366 DECLASM(RTSEL) ASMGetFS(void);
367 #else
368 DECLINLINE(RTSEL) ASMGetFS(void)
369 {
370     RTSEL SelFS;
371 # if RT_INLINE_ASM_GNU_STYLE
372     __asm__ __volatile__("movw  %%fs, %0\n\t" : "=r" (SelFS));
373 # else
374     __asm
375     {
376         mov     ax, fs
377         mov     [SelFS], ax
378     }
379 # endif
380     return SelFS;
381 }
382 # endif
383
384
385 /**
386  * Get the GS register.
387  * @returns GS.
388  */
389 #if RT_INLINE_ASM_EXTERNAL
390 DECLASM(RTSEL) ASMGetGS(void);
391 #else
392 DECLINLINE(RTSEL) ASMGetGS(void)
393 {
394     RTSEL SelGS;
395 # if RT_INLINE_ASM_GNU_STYLE
396     __asm__ __volatile__("movw  %%gs, %0\n\t" : "=r" (SelGS));
397 # else
398     __asm
399     {
400         mov     ax, gs
401         mov     [SelGS], ax
402     }
403 # endif
404     return SelGS;
405 }
406 #endif
407
408
409 /**
410  * Get the SS register.
411  * @returns SS.
412  */
413 #if RT_INLINE_ASM_EXTERNAL
414 DECLASM(RTSEL) ASMGetSS(void);
415 #else
416 DECLINLINE(RTSEL) ASMGetSS(void)
417 {
418     RTSEL SelSS;
419 # if RT_INLINE_ASM_GNU_STYLE
420     __asm__ __volatile__("movw  %%ss, %0\n\t" : "=r" (SelSS));
421 # else
422     __asm
423     {
424         mov     ax, ss
425         mov     [SelSS], ax
426     }
427 # endif
428     return SelSS;
429 }
430 #endif
431
432
433 /**
434  * Get the TR register.
435  * @returns TR.
436  */
437 #if RT_INLINE_ASM_EXTERNAL
438 DECLASM(RTSEL) ASMGetTR(void);
439 #else
440 DECLINLINE(RTSEL) ASMGetTR(void)
441 {
442     RTSEL SelTR;
443 # if RT_INLINE_ASM_GNU_STYLE
444     __asm__ __volatile__("str %w0\n\t" : "=r" (SelTR));
445 # else
446     __asm
447     {
448         str     ax
449         mov     [SelTR], ax
450     }
451 # endif
452     return SelTR;
453 }
454 #endif
455
456
457 /**
458  * Get the [RE]FLAGS register.
459  * @returns [RE]FLAGS.
460  */
461 #if RT_INLINE_ASM_EXTERNAL
462 DECLASM(RTCCUINTREG) ASMGetFlags(void);
463 #else
464 DECLINLINE(RTCCUINTREG) ASMGetFlags(void)
465 {
466     RTCCUINTREG uFlags;
467 # if RT_INLINE_ASM_GNU_STYLE
468 ifdef RT_ARCH_AMD64
469     __asm__ __volatile__("pushfq\n\t"
470                          "popq  %0\n\t"
471                          : "=g" (uFlags));
472 else
473     __asm__ __volatile__("pushfl\n\t"
474                          "popl  %0\n\t"
475                          : "=g" (uFlags));
476 endif
477 # else
478     __asm
479     {
480 ifdef RT_ARCH_AMD64
481         pushfq
482         pop  [uFlags]
483 else
484         pushfd
485         pop  [uFlags]
486 endif
487     }
488 # endif
489     return uFlags;
490 }
491 #endif
492
493
494 /**
495  * Set the [RE]FLAGS register.
496  * @param   uFlags      The new [RE]FLAGS value.
497  */
498 #if RT_INLINE_ASM_EXTERNAL
499 DECLASM(void) ASMSetFlags(RTCCUINTREG uFlags);
500 #else
501 DECLINLINE(void) ASMSetFlags(RTCCUINTREG uFlags)
502 {
503 # if RT_INLINE_ASM_GNU_STYLE
504 ifdef RT_ARCH_AMD64
505     __asm__ __volatile__("pushq %0\n\t"
506                          "popfq\n\t"
507                          : : "g" (uFlags));
508 else
509     __asm__ __volatile__("pushl %0\n\t"
510                          "popfl\n\t"
511                          : : "g" (uFlags));
512 endif
513 # else
514     __asm
515     {
516 ifdef RT_ARCH_AMD64
517         push    [uFlags]
518         popfq
519 else
520         push    [uFlags]
521         popfd
522 endif
523     }
524 # endif
525 }
526 #endif
527
528
529 /**
530  * Gets the content of the CPU timestamp counter register.
531  *
532  * @returns TSC.
533  */
534 #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN
535 DECLASM(uint64_t) ASMReadTSC(void);
536 #else
537 DECLINLINE(uint64_t) ASMReadTSC(void)
538 {
539     RTUINT64U u;
540 # if RT_INLINE_ASM_GNU_STYLE
541     __asm__ __volatile__ ("rdtsc\n\t" : "=a" (u.s.Lo), "=d" (u.s.Hi));
542 # else
543 if RT_INLINE_ASM_USES_INTRIN
544     u.u = __rdtsc();
545 else
546     __asm
547     {
548         rdtsc
549         mov     [u.s.Lo], eax
550         mov     [u.s.Hi], edx
551     }
552 endif
553 # endif
554     return u.u;
555 }
556 #endif
557
558
559 /**
560  * Performs the cpuid instruction returning all registers.
561  *
562  * @param   uOperator   CPUID operation (eax).
563  * @param   pvEAX       Where to store eax.
564  * @param   pvEBX       Where to store ebx.
565  * @param   pvECX       Where to store ecx.
566  * @param   pvEDX       Where to store edx.
567  * @remark  We're using void pointers to ease the use of special bitfield structures and such.
568  */
569 #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN
570 DECLASM(void) ASMCpuId(uint32_t uOperator, void *pvEAX, void *pvEBX, void *pvECX, void *pvEDX);
571 #else
572 DECLINLINE(void) ASMCpuId(uint32_t uOperator, void *pvEAX, void *pvEBX, void *pvECX, void *pvEDX)
573 {
574 # if RT_INLINE_ASM_GNU_STYLE
575 ifdef RT_ARCH_AMD64
576     RTCCUINTREG uRAX, uRBX, uRCX, uRDX;
577     __asm__ ("cpuid\n\t"
578              : "=a" (uRAX),
579                "=b" (uRBX),
580                "=c" (uRCX),
581                "=d" (uRDX)
582              : "0" (uOperator));
583     *(uint32_t *)pvEAX = (uint32_t)uRAX;
584     *(uint32_t *)pvEBX = (uint32_t)uRBX;
585     *(uint32_t *)pvECX = (uint32_t)uRCX;
586     *(uint32_t *)pvEDX = (uint32_t)uRDX;
587 else
588     __asm__ ("xchgl %%ebx, %1\n\t"
589              "cpuid\n\t"
590              "xchgl %%ebx, %1\n\t"
591              : "=a" (*(uint32_t *)pvEAX),
592                "=r" (*(uint32_t *)pvEBX),
593                "=c" (*(uint32_t *)pvECX),
594                "=d" (*(uint32_t *)pvEDX)
595              : "0" (uOperator));
596 endif
597
598 # elif RT_INLINE_ASM_USES_INTRIN
599     int aInfo[4];
600     __cpuid(aInfo, uOperator);
601     *(uint32_t *)pvEAX = aInfo[0];
602     *(uint32_t *)pvEBX = aInfo[1];
603     *(uint32_t *)pvECX = aInfo[2];
604     *(uint32_t *)pvEDX = aInfo[3];
605
606 # else
607     uint32_t    uEAX;
608     uint32_t    uEBX;
609     uint32_t    uECX;
610     uint32_t    uEDX;
611     __asm
612     {
613         push    ebx
614         mov     eax, [uOperator]
615         cpuid
616         mov     [uEAX], eax
617         mov     [uEBX], ebx
618         mov     [uECX], ecx
619         mov     [uEDX], edx
620         pop     ebx
621     }
622     *(uint32_t *)pvEAX = uEAX;
623     *(uint32_t *)pvEBX = uEBX;
624     *(uint32_t *)pvECX = uECX;
625     *(uint32_t *)pvEDX = uEDX;
626 # endif
627 }
628 #endif
629
630
631 /**
632  * Performs the cpuid instruction returning all registers.
633  * Some subfunctions of cpuid take ECX as additional parameter (currently known for EAX=4)
634  *
635  * @param   uOperator   CPUID operation (eax).
636  * @param   uIdxECX     ecx index
637  * @param   pvEAX       Where to store eax.
638  * @param   pvEBX       Where to store ebx.
639  * @param   pvECX       Where to store ecx.
640  * @param   pvEDX       Where to store edx.
641  * @remark  We're using void pointers to ease the use of special bitfield structures and such.
642  */
643 #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN
644 DECLASM(void) ASMCpuId_Idx_ECX(uint32_t uOperator, uint32_t uIdxECX, void *pvEAX, void *pvEBX, void *pvECX, void *pvEDX);
645 #else
646 DECLINLINE(void) ASMCpuId_Idx_ECX(uint32_t uOperator, uint32_t uIdxECX, void *pvEAX, void *pvEBX, void *pvECX, void *pvEDX)
647 {
648 # if RT_INLINE_ASM_GNU_STYLE
649 ifdef RT_ARCH_AMD64
650     RTCCUINTREG uRAX, uRBX, uRCX, uRDX;
651     __asm__ ("cpuid\n\t"
652              : "=a" (uRAX),
653                "=b" (uRBX),
654                "=c" (uRCX),
655                "=d" (uRDX)
656              : "0" (uOperator),
657                "2" (uIdxECX));
658     *(uint32_t *)pvEAX = (uint32_t)uRAX;
659     *(uint32_t *)pvEBX = (uint32_t)uRBX;
660     *(uint32_t *)pvECX = (uint32_t)uRCX;
661     *(uint32_t *)pvEDX = (uint32_t)uRDX;
662 else
663     __asm__ ("xchgl %%ebx, %1\n\t"
664              "cpuid\n\t"
665              "xchgl %%ebx, %1\n\t"
666              : "=a" (*(uint32_t *)pvEAX),
667                "=r" (*(uint32_t *)pvEBX),
668                "=c" (*(uint32_t *)pvECX),
669                "=d" (*(uint32_t *)pvEDX)
670              : "0" (uOperator),
671                "2" (uIdxECX));
672 endif
673
674 # elif RT_INLINE_ASM_USES_INTRIN
675     int aInfo[4];
676     /* ??? another intrinsic ??? */
677     __cpuid(aInfo, uOperator);
678     *(uint32_t *)pvEAX = aInfo[0];
679     *(uint32_t *)pvEBX = aInfo[1];
680     *(uint32_t *)pvECX = aInfo[2];
681     *(uint32_t *)pvEDX = aInfo[3];
682
683 # else
684     uint32_t    uEAX;
685     uint32_t    uEBX;
686     uint32_t    uECX;
687     uint32_t    uEDX;
688     __asm
689     {
690         push    ebx
691         mov     eax, [uOperator]
692         mov     ecx, [uIdxECX]
693         cpuid
694         mov     [uEAX], eax
695         mov     [uEBX], ebx
696         mov     [uECX], ecx
697         mov     [uEDX], edx
698         pop     ebx
699     }
700     *(uint32_t *)pvEAX = uEAX;
701     *(uint32_t *)pvEBX = uEBX;
702     *(uint32_t *)pvECX = uECX;
703     *(uint32_t *)pvEDX = uEDX;
704 # endif
705 }
706 #endif
707
708
709 /**
710  * Performs the cpuid instruction returning ecx and edx.
711  *
712  * @param   uOperator   CPUID operation (eax).
713  * @param   pvECX       Where to store ecx.
714  * @param   pvEDX       Where to store edx.
715  * @remark  We're using void pointers to ease the use of special bitfield structures and such.
716  */
717 #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN
718 DECLASM(void) ASMCpuId_ECX_EDX(uint32_t uOperator, void *pvECX, void *pvEDX);
719 #else
720 DECLINLINE(void) ASMCpuId_ECX_EDX(uint32_t uOperator, void *pvECX, void *pvEDX)
721 {
722     uint32_t uEBX;
723     ASMCpuId(uOperator, &uOperator, &uEBX, pvECX, pvEDX);
724 }
725 #endif
726
727
728 /**
729  * Performs the cpuid instruction returning edx.
730  *
731  * @param   uOperator   CPUID operation (eax).
732  * @returns EDX after cpuid operation.
733  */
734 #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN
735 DECLASM(uint32_t) ASMCpuId_EDX(uint32_t uOperator);
736 #else
737 DECLINLINE(uint32_t) ASMCpuId_EDX(uint32_t uOperator)
738 {
739     RTCCUINTREG xDX;
740 # if RT_INLINE_ASM_GNU_STYLE
741 ifdef RT_ARCH_AMD64
742     RTCCUINTREG uSpill;
743     __asm__ ("cpuid"
744              : "=a" (uSpill),
745                "=d" (xDX)
746              : "0" (uOperator)
747              : "rbx", "rcx");
748 elif (defined(PIC) || defined(__PIC__)) && defined(__i386__)
749     __asm__ ("push  %%ebx\n\t"
750              "cpuid\n\t"
751              "pop   %%ebx\n\t"
752              : "=a" (uOperator),
753                "=d" (xDX)
754              : "0" (uOperator)
755              : "ecx");
756 else
757     __asm__ ("cpuid"
758              : "=a" (uOperator),
759                "=d" (xDX)
760              : "0" (uOperator)
761              : "ebx", "ecx");
762 endif
763
764 # elif RT_INLINE_ASM_USES_INTRIN
765     int aInfo[4];
766     __cpuid(aInfo, uOperator);
767     xDX = aInfo[3];
768
769 # else
770     __asm
771     {
772         push    ebx
773         mov     eax, [uOperator]
774         cpuid
775         mov     [xDX], edx
776         pop     ebx
777     }
778 # endif
779     return (uint32_t)xDX;
780 }
781 #endif
782
783
784 /**
785  * Performs the cpuid instruction returning ecx.
786  *
787  * @param   uOperator   CPUID operation (eax).
788  * @returns ECX after cpuid operation.
789  */
790 #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN
791 DECLASM(uint32_t) ASMCpuId_ECX(uint32_t uOperator);
792 #else
793 DECLINLINE(uint32_t) ASMCpuId_ECX(uint32_t uOperator)
794 {
795     RTCCUINTREG xCX;
796 # if RT_INLINE_ASM_GNU_STYLE
797 ifdef RT_ARCH_AMD64
798     RTCCUINTREG uSpill;
799     __asm__ ("cpuid"
800              : "=a" (uSpill),
801                "=c" (xCX)
802              : "0" (uOperator)
803              : "rbx", "rdx");
804 elif (defined(PIC) || defined(__PIC__)) && defined(__i386__)
805     __asm__ ("push  %%ebx\n\t"
806              "cpuid\n\t"
807              "pop   %%ebx\n\t"
808              : "=a" (uOperator),
809                "=c" (xCX)
810              : "0" (uOperator)
811              : "edx");
812 else
813     __asm__ ("cpuid"
814              : "=a" (uOperator),
815                "=c" (xCX)
816              : "0" (uOperator)
817              : "ebx", "edx");
818
819 endif
820
821 # elif RT_INLINE_ASM_USES_INTRIN
822     int aInfo[4];
823     __cpuid(aInfo, uOperator);
824     xCX = aInfo[2];
825
826 # else
827     __asm
828     {
829         push    ebx
830         mov     eax, [uOperator]
831         cpuid
832         mov     [xCX], ecx
833         pop     ebx
834     }
835 # endif
836     return (uint32_t)xCX;
837 }
838 #endif
839
840
841 /**
842  * Checks if the current CPU supports CPUID.
843  *
844  * @returns true if CPUID is supported.
845  */
846 DECLINLINE(bool) ASMHasCpuId(void)
847 {
848 #ifdef RT_ARCH_AMD64
849     return true; /* ASSUME that all amd64 compatible CPUs have cpuid. */
850 #else /* !RT_ARCH_AMD64 */
851     bool        fRet = false;
852 # if RT_INLINE_ASM_GNU_STYLE
853     uint32_t    u1;
854     uint32_t    u2;
855     __asm__ ("pushf\n\t"
856              "pop   %1\n\t"
857              "mov   %1, %2\n\t"
858              "xorl  $0x200000, %1\n\t"
859              "push  %1\n\t"
860              "popf\n\t"
861              "pushf\n\t"
862              "pop   %1\n\t"
863              "cmpl  %1, %2\n\t"
864              "setne %0\n\t"
865              "push  %2\n\t"
866              "popf\n\t"
867              : "=m" (fRet), "=r" (u1), "=r" (u2));
868 # else
869     __asm
870     {
871         pushfd
872         pop     eax
873         mov     ebx, eax
874         xor     eax, 0200000h
875         push    eax
876         popfd
877         pushfd
878         pop     eax
879         cmp     eax, ebx
880         setne   fRet
881         push    ebx
882         popfd
883     }
884 # endif
885     return fRet;
886 #endif /* !RT_ARCH_AMD64 */
887 }
888
889
890 /**
891  * Gets the APIC ID of the current CPU.
892  *
893  * @returns the APIC ID.
894  */
895 #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN
896 DECLASM(uint8_t) ASMGetApicId(void);
897 #else
898 DECLINLINE(uint8_t) ASMGetApicId(void)
899 {
900     RTCCUINTREG xBX;
901 # if RT_INLINE_ASM_GNU_STYLE
902 ifdef RT_ARCH_AMD64
903     RTCCUINTREG uSpill;
904     __asm__ ("cpuid"
905              : "=a" (uSpill),
906                "=b" (xBX)
907              : "0" (1)
908              : "rcx", "rdx");
909 elif (defined(PIC) || defined(__PIC__)) && defined(__i386__)
910     RTCCUINTREG uSpill;
911     __asm__ ("mov   %%ebx,%1\n\t"
912              "cpuid\n\t"
913              "xchgl %%ebx,%1\n\t"
914              : "=a" (uSpill),
915                "=r" (xBX)
916              : "0" (1)
917              : "ecx", "edx");
918 else
919     RTCCUINTREG uSpill;
9