/** @file * IPRT - AMD64 and x86 Specific Assembly Functions. */ /* * Copyright (C) 2006-2015 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. */ #ifndef ___iprt_asm_amd64_x86_h #define ___iprt_asm_amd64_x86_h #include #include #if !defined(RT_ARCH_AMD64) && !defined(RT_ARCH_X86) # error "Not on AMD64 or x86" #endif #if defined(_MSC_VER) && RT_INLINE_ASM_USES_INTRIN # include /* Emit the intrinsics at all optimization levels. */ # pragma intrinsic(_ReadWriteBarrier) # pragma intrinsic(__cpuid) # pragma intrinsic(_enable) # pragma intrinsic(_disable) # pragma intrinsic(__rdtsc) # pragma intrinsic(__readmsr) # pragma intrinsic(__writemsr) # pragma intrinsic(__outbyte) # pragma intrinsic(__outbytestring) # pragma intrinsic(__outword) # pragma intrinsic(__outwordstring) # pragma intrinsic(__outdword) # pragma intrinsic(__outdwordstring) # pragma intrinsic(__inbyte) # pragma intrinsic(__inbytestring) # pragma intrinsic(__inword) # pragma intrinsic(__inwordstring) # pragma intrinsic(__indword) # pragma intrinsic(__indwordstring) # pragma intrinsic(__invlpg) # pragma intrinsic(__wbinvd) # pragma intrinsic(__readcr0) # pragma intrinsic(__readcr2) # pragma intrinsic(__readcr3) # pragma intrinsic(__readcr4) # pragma intrinsic(__writecr0) # pragma intrinsic(__writecr3) # pragma intrinsic(__writecr4) # pragma intrinsic(__readdr) # pragma intrinsic(__writedr) # ifdef RT_ARCH_AMD64 # pragma intrinsic(__readcr8) # pragma intrinsic(__writecr8) # endif # if RT_INLINE_ASM_USES_INTRIN >= 15 # pragma intrinsic(__readeflags) # pragma intrinsic(__writeeflags) # pragma intrinsic(__rdtscp) # endif #endif /** @defgroup grp_rt_asm_amd64_x86 AMD64 and x86 Specific ASM Routines * @ingroup grp_rt_asm * @{ */ /** @todo find a more proper place for these structures? */ #pragma pack(1) /** IDTR */ typedef struct RTIDTR { /** Size of the IDT. */ uint16_t cbIdt; /** Address of the IDT. */ uintptr_t pIdt; } RTIDTR, *PRTIDTR; #pragma pack() #pragma pack(1) /** @internal */ typedef struct RTIDTRALIGNEDINT { /** Alignment padding. */ uint8_t au16Padding[ARCH_BITS == 64 ? 3 : 1]; /** The IDTR structure. */ RTIDTR Idtr; } RTIDTRALIGNEDINT; #pragma pack() /** Wrapped RTIDTR for preventing misalignment exceptions. */ typedef union RTIDTRALIGNED { /** Try make sure this structure has optimal alignment. */ uint64_t auAlignmentHack[ARCH_BITS == 64 ? 2 : 1]; /** Aligned structure. */ RTIDTRALIGNEDINT s; } RTIDTRALIGNED; AssertCompileSize(RTIDTRALIGNED, ARCH_BITS * 2 / 8); /** Pointer to a an RTIDTR alignment wrapper. */ typedef RTIDTRALIGNED *PRIDTRALIGNED; #pragma pack(1) /** GDTR */ typedef struct RTGDTR { /** Size of the GDT. */ uint16_t cbGdt; /** Address of the GDT. */ uintptr_t pGdt; } RTGDTR, *PRTGDTR; #pragma pack() #pragma pack(1) /** @internal */ typedef struct RTGDTRALIGNEDINT { /** Alignment padding. */ uint8_t au16Padding[ARCH_BITS == 64 ? 3 : 1]; /** The GDTR structure. */ RTGDTR Gdtr; } RTGDTRALIGNEDINT; #pragma pack() /** Wrapped RTGDTR for preventing misalignment exceptions. */ typedef union RTGDTRALIGNED { /** Try make sure this structure has optimal alignment. */ uint64_t auAlignmentHack[ARCH_BITS == 64 ? 2 : 1]; /** Aligned structure. */ RTGDTRALIGNEDINT s; } RTGDTRALIGNED; AssertCompileSize(RTGDTRALIGNED, ARCH_BITS * 2 / 8); /** Pointer to a an RTGDTR alignment wrapper. */ typedef RTGDTRALIGNED *PRGDTRALIGNED; /** * Gets the content of the IDTR CPU register. * @param pIdtr Where to store the IDTR contents. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(void) ASMGetIDTR(PRTIDTR pIdtr); #else DECLINLINE(void) ASMGetIDTR(PRTIDTR pIdtr) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("sidt %0" : "=m" (*pIdtr)); # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [pIdtr] sidt [rax] # else mov eax, [pIdtr] sidt [eax] # endif } # endif } #endif /** * Gets the content of the IDTR.LIMIT CPU register. * @returns IDTR limit. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(uint16_t) ASMGetIdtrLimit(void); #else DECLINLINE(uint16_t) ASMGetIdtrLimit(void) { RTIDTRALIGNED TmpIdtr; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("sidt %0" : "=m" (TmpIdtr.s.Idtr)); # else __asm { sidt [TmpIdtr.s.Idtr] } # endif return TmpIdtr.s.Idtr.cbIdt; } #endif /** * Sets the content of the IDTR CPU register. * @param pIdtr Where to load the IDTR contents from */ #if RT_INLINE_ASM_EXTERNAL DECLASM(void) ASMSetIDTR(const RTIDTR *pIdtr); #else DECLINLINE(void) ASMSetIDTR(const RTIDTR *pIdtr) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("lidt %0" : : "m" (*pIdtr)); # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [pIdtr] lidt [rax] # else mov eax, [pIdtr] lidt [eax] # endif } # endif } #endif /** * Gets the content of the GDTR CPU register. * @param pGdtr Where to store the GDTR contents. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(void) ASMGetGDTR(PRTGDTR pGdtr); #else DECLINLINE(void) ASMGetGDTR(PRTGDTR pGdtr) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("sgdt %0" : "=m" (*pGdtr)); # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [pGdtr] sgdt [rax] # else mov eax, [pGdtr] sgdt [eax] # endif } # endif } #endif /** * Sets the content of the GDTR CPU register. * @param pIdtr Where to load the GDTR contents from */ #if RT_INLINE_ASM_EXTERNAL DECLASM(void) ASMSetGDTR(const RTGDTR *pGdtr); #else DECLINLINE(void) ASMSetGDTR(const RTGDTR *pGdtr) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("lgdt %0" : : "m" (*pGdtr)); # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [pGdtr] lgdt [rax] # else mov eax, [pGdtr] lgdt [eax] # endif } # endif } #endif /** * Get the cs register. * @returns cs. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(RTSEL) ASMGetCS(void); #else DECLINLINE(RTSEL) ASMGetCS(void) { RTSEL SelCS; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("movw %%cs, %0\n\t" : "=r" (SelCS)); # else __asm { mov ax, cs mov [SelCS], ax } # endif return SelCS; } #endif /** * Get the DS register. * @returns DS. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(RTSEL) ASMGetDS(void); #else DECLINLINE(RTSEL) ASMGetDS(void) { RTSEL SelDS; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("movw %%ds, %0\n\t" : "=r" (SelDS)); # else __asm { mov ax, ds mov [SelDS], ax } # endif return SelDS; } #endif /** * Get the ES register. * @returns ES. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(RTSEL) ASMGetES(void); #else DECLINLINE(RTSEL) ASMGetES(void) { RTSEL SelES; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("movw %%es, %0\n\t" : "=r" (SelES)); # else __asm { mov ax, es mov [SelES], ax } # endif return SelES; } #endif /** * Get the FS register. * @returns FS. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(RTSEL) ASMGetFS(void); #else DECLINLINE(RTSEL) ASMGetFS(void) { RTSEL SelFS; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("movw %%fs, %0\n\t" : "=r" (SelFS)); # else __asm { mov ax, fs mov [SelFS], ax } # endif return SelFS; } # endif /** * Get the GS register. * @returns GS. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(RTSEL) ASMGetGS(void); #else DECLINLINE(RTSEL) ASMGetGS(void) { RTSEL SelGS; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("movw %%gs, %0\n\t" : "=r" (SelGS)); # else __asm { mov ax, gs mov [SelGS], ax } # endif return SelGS; } #endif /** * Get the SS register. * @returns SS. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(RTSEL) ASMGetSS(void); #else DECLINLINE(RTSEL) ASMGetSS(void) { RTSEL SelSS; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("movw %%ss, %0\n\t" : "=r" (SelSS)); # else __asm { mov ax, ss mov [SelSS], ax } # endif return SelSS; } #endif /** * Get the TR register. * @returns TR. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(RTSEL) ASMGetTR(void); #else DECLINLINE(RTSEL) ASMGetTR(void) { RTSEL SelTR; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("str %w0\n\t" : "=r" (SelTR)); # else __asm { str ax mov [SelTR], ax } # endif return SelTR; } #endif /** * Get the LDTR register. * @returns LDTR. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(RTSEL) ASMGetLDTR(void); #else DECLINLINE(RTSEL) ASMGetLDTR(void) { RTSEL SelLDTR; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("sldt %w0\n\t" : "=r" (SelLDTR)); # else __asm { sldt ax mov [SelLDTR], ax } # endif return SelLDTR; } #endif /** * Get the access rights for the segment selector. * * @returns The access rights on success or UINT32_MAX on failure. * @param uSel The selector value. * * @remarks Using UINT32_MAX for failure is chosen because valid access rights * always have bits 0:7 as 0 (on both Intel & AMD). */ #if RT_INLINE_ASM_EXTERNAL DECLASM(uint32_t) ASMGetSegAttr(uint32_t uSel); #else DECLINLINE(uint32_t) ASMGetSegAttr(uint32_t uSel) { uint32_t uAttr; /* LAR only accesses 16-bit of the source operand, but eax for the destination operand is required for getting the full 32-bit access rights. */ # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("lar %1, %%eax\n\t" "jz done%=\n\t" "movl $0xffffffff, %%eax\n\t" "done%=:\n\t" "movl %%eax, %0\n\t" : "=r" (uAttr) : "r" (uSel) : "cc", "%eax"); # else __asm { lar eax, [uSel] jz done mov eax, 0ffffffffh done: mov [uAttr], eax } # endif return uAttr; } #endif /** * Get the [RE]FLAGS register. * @returns [RE]FLAGS. */ #if RT_INLINE_ASM_EXTERNAL && RT_INLINE_ASM_USES_INTRIN < 15 DECLASM(RTCCUINTREG) ASMGetFlags(void); #else DECLINLINE(RTCCUINTREG) ASMGetFlags(void) { RTCCUINTREG uFlags; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("pushfq\n\t" "popq %0\n\t" : "=r" (uFlags)); # else __asm__ __volatile__("pushfl\n\t" "popl %0\n\t" : "=r" (uFlags)); # endif # elif RT_INLINE_ASM_USES_INTRIN >= 15 uFlags = __readeflags(); # else __asm { # ifdef RT_ARCH_AMD64 pushfq pop [uFlags] # else pushfd pop [uFlags] # endif } # endif return uFlags; } #endif /** * Set the [RE]FLAGS register. * @param uFlags The new [RE]FLAGS value. */ #if RT_INLINE_ASM_EXTERNAL && RT_INLINE_ASM_USES_INTRIN < 15 DECLASM(void) ASMSetFlags(RTCCUINTREG uFlags); #else DECLINLINE(void) ASMSetFlags(RTCCUINTREG uFlags) { # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("pushq %0\n\t" "popfq\n\t" : : "g" (uFlags)); # else __asm__ __volatile__("pushl %0\n\t" "popfl\n\t" : : "g" (uFlags)); # endif # elif RT_INLINE_ASM_USES_INTRIN >= 15 __writeeflags(uFlags); # else __asm { # ifdef RT_ARCH_AMD64 push [uFlags] popfq # else push [uFlags] popfd # endif } # endif } #endif /** * Modifies the [RE]FLAGS register. * @returns Original value. * @param fAndEfl Flags to keep (applied first). * @param fOrEfl Flags to be set. */ #if RT_INLINE_ASM_EXTERNAL && RT_INLINE_ASM_USES_INTRIN < 15 DECLASM(RTCCUINTREG) ASMChangeFlags(RTCCUINTREG fAndEfl, RTCCUINTREG fOrEfl); #else DECLINLINE(RTCCUINTREG) ASMChangeFlags(RTCCUINTREG fAndEfl, RTCCUINTREG fOrEfl) { RTCCUINTREG fOldEfl; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("pushfq\n\t" "movq (%%rsp), %0\n\t" "andq %0, %1\n\t" "orq %3, %1\n\t" "mov %1, (%%rsp)\n\t" "popfq\n\t" : "=r" (fOldEfl), "=g" (fAndEfl) : "1" (fAndEfl), "g" (fOrEfl) ); # else __asm__ __volatile__("pushfl\n\t" "movl (%%esp), %0\n\t" "andl %1, (%%esp)\n\t" "orl %2, (%%esp)\n\t" "popfl\n\t" : "=r" (fOldEfl) : "g" (fAndEfl), "g" (fOrEfl) ); # endif # elif RT_INLINE_ASM_USES_INTRIN >= 15 fOldEfl = __readeflags(); __writeeflags((fOldEfl & fAndEfl) | fOrEfl); # else __asm { # ifdef RT_ARCH_AMD64 mov rdx, [fAndEfl] mov rcx, [fOrEfl] pushfq mov rax, [rsp] and rdx, rax or rdx, rcx mov [rsp], rdx popfq mov [fOldEfl], rax # else mov edx, [fAndEfl] mov ecx, [fOrEfl] pushfd mov eax, [esp] and edx, eax or edx, ecx mov [esp], edx popfd mov [fOldEfl], eax # endif } # endif return fOldEfl; } #endif /** * Modifies the [RE]FLAGS register by ORing in one or more flags. * @returns Original value. * @param fOrEfl The flags to be set (ORed in). */ #if RT_INLINE_ASM_EXTERNAL && RT_INLINE_ASM_USES_INTRIN < 15 DECLASM(RTCCUINTREG) ASMAddFlags(RTCCUINTREG fOrEfl); #else DECLINLINE(RTCCUINTREG) ASMAddFlags(RTCCUINTREG fOrEfl) { RTCCUINTREG fOldEfl; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("pushfq\n\t" "movq (%%rsp), %0\n\t" "orq %1, (%%rsp)\n\t" "popfq\n\t" : "=r" (fOldEfl) : "g" (fOrEfl) ); # else __asm__ __volatile__("pushfl\n\t" "movl (%%esp), %0\n\t" "orl %1, (%%esp)\n\t" "popfl\n\t" : "=r" (fOldEfl) : "g" (fOrEfl) ); # endif # elif RT_INLINE_ASM_USES_INTRIN >= 15 fOldEfl = __readeflags(); __writeeflags(fOldEfl | fOrEfl); # else __asm { # ifdef RT_ARCH_AMD64 mov rcx, [fOrEfl] pushfq mov rdx, [rsp] or [rsp], rcx popfq mov [fOldEfl], rax # else mov ecx, [fOrEfl] pushfd mov edx, [esp] or [esp], ecx popfd mov [fOldEfl], eax # endif } # endif return fOldEfl; } #endif /** * Modifies the [RE]FLAGS register by AND'ing out one or more flags. * @returns Original value. * @param fAndEfl The flags to keep. */ #if RT_INLINE_ASM_EXTERNAL && RT_INLINE_ASM_USES_INTRIN < 15 DECLASM(RTCCUINTREG) ASMClearFlags(RTCCUINTREG fAndEfl); #else DECLINLINE(RTCCUINTREG) ASMClearFlags(RTCCUINTREG fAndEfl) { RTCCUINTREG fOldEfl; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("pushfq\n\t" "movq (%%rsp), %0\n\t" "andq %1, (%%rsp)\n\t" "popfq\n\t" : "=r" (fOldEfl) : "g" (fAndEfl) ); # else __asm__ __volatile__("pushfl\n\t" "movl (%%esp), %0\n\t" "andl %1, (%%esp)\n\t" "popfl\n\t" : "=r" (fOldEfl) : "g" (fAndEfl) ); # endif # elif RT_INLINE_ASM_USES_INTRIN >= 15 fOldEfl = __readeflags(); __writeeflags(fOldEfl & fAndEfl); # else __asm { # ifdef RT_ARCH_AMD64 mov rdx, [fAndEfl] pushfq mov rdx, [rsp] and [rsp], rdx popfq mov [fOldEfl], rax # else mov edx, [fAndEfl] pushfd mov edx, [esp] and [esp], edx popfd mov [fOldEfl], eax # endif } # endif return fOldEfl; } #endif /** * Gets the content of the CPU timestamp counter register. * * @returns TSC. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint64_t) ASMReadTSC(void); #else DECLINLINE(uint64_t) ASMReadTSC(void) { RTUINT64U u; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rdtsc\n\t" : "=a" (u.s.Lo), "=d" (u.s.Hi)); # else # if RT_INLINE_ASM_USES_INTRIN u.u = __rdtsc(); # else __asm { rdtsc mov [u.s.Lo], eax mov [u.s.Hi], edx } # endif # endif return u.u; } #endif /** * Gets the content of the CPU timestamp counter register and the * assoicated AUX value. * * @returns TSC. * @param puAux Where to store the AUX value. */ #if RT_INLINE_ASM_EXTERNAL && RT_INLINE_ASM_USES_INTRIN < 15 DECLASM(uint64_t) ASMReadTscWithAux(uint32_t *puAux); #else DECLINLINE(uint64_t) ASMReadTscWithAux(uint32_t *puAux) { RTUINT64U u; # if RT_INLINE_ASM_GNU_STYLE /* rdtscp is not supported by ancient linux build VM of course :-( */ /*__asm__ __volatile__("rdtscp\n\t" : "=a" (u.s.Lo), "=d" (u.s.Hi), "=c" (*puAux)); */ __asm__ __volatile__(".byte 0x0f,0x01,0xf9\n\t" : "=a" (u.s.Lo), "=d" (u.s.Hi), "=c" (*puAux)); # else # if RT_INLINE_ASM_USES_INTRIN >= 15 u.u = __rdtscp(puAux); # else __asm { rdtscp mov [u.s.Lo], eax mov [u.s.Hi], edx mov eax, [puAux] mov [eax], ecx } # endif # endif return u.u; } #endif /** * Performs the cpuid instruction returning all registers. * * @param uOperator CPUID operation (eax). * @param pvEAX Where to store eax. * @param pvEBX Where to store ebx. * @param pvECX Where to store ecx. * @param pvEDX Where to store edx. * @remark We're using void pointers to ease the use of special bitfield structures and such. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMCpuId(uint32_t uOperator, void *pvEAX, void *pvEBX, void *pvECX, void *pvEDX); #else DECLINLINE(void) ASMCpuId(uint32_t uOperator, void *pvEAX, void *pvEBX, void *pvECX, void *pvEDX) { # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 RTCCUINTREG uRAX, uRBX, uRCX, uRDX; __asm__ __volatile__ ("cpuid\n\t" : "=a" (uRAX), "=b" (uRBX), "=c" (uRCX), "=d" (uRDX) : "0" (uOperator), "2" (0)); *(uint32_t *)pvEAX = (uint32_t)uRAX; *(uint32_t *)pvEBX = (uint32_t)uRBX; *(uint32_t *)pvECX = (uint32_t)uRCX; *(uint32_t *)pvEDX = (uint32_t)uRDX; # else __asm__ __volatile__ ("xchgl %%ebx, %1\n\t" "cpuid\n\t" "xchgl %%ebx, %1\n\t" : "=a" (*(uint32_t *)pvEAX), "=r" (*(uint32_t *)pvEBX), "=c" (*(uint32_t *)pvECX), "=d" (*(uint32_t *)pvEDX) : "0" (uOperator), "2" (0)); # endif # elif RT_INLINE_ASM_USES_INTRIN int aInfo[4]; __cpuid(aInfo, uOperator); *(uint32_t *)pvEAX = aInfo[0]; *(uint32_t *)pvEBX = aInfo[1]; *(uint32_t *)pvECX = aInfo[2]; *(uint32_t *)pvEDX = aInfo[3]; # else uint32_t uEAX; uint32_t uEBX; uint32_t uECX; uint32_t uEDX; __asm { push ebx mov eax, [uOperator] cpuid mov [uEAX], eax mov [uEBX], ebx mov [uECX], ecx mov [uEDX], edx pop ebx } *(uint32_t *)pvEAX = uEAX; *(uint32_t *)pvEBX = uEBX; *(uint32_t *)pvECX = uECX; *(uint32_t *)pvEDX = uEDX; # endif } #endif /** * Performs the CPUID instruction with EAX and ECX input returning ALL output * registers. * * @param uOperator CPUID operation (eax). * @param uIdxECX ecx index * @param pvEAX Where to store eax. * @param pvEBX Where to store ebx. * @param pvECX Where to store ecx. * @param pvEDX Where to store edx. * @remark We're using void pointers to ease the use of special bitfield structures and such. */ #if RT_INLINE_ASM_EXTERNAL || RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMCpuId_Idx_ECX(uint32_t uOperator, uint32_t uIdxECX, void *pvEAX, void *pvEBX, void *pvECX, void *pvEDX); #else DECLINLINE(void) ASMCpuId_Idx_ECX(uint32_t uOperator, uint32_t uIdxECX, void *pvEAX, void *pvEBX, void *pvECX, void *pvEDX) { # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 RTCCUINTREG uRAX, uRBX, uRCX, uRDX; __asm__ ("cpuid\n\t" : "=a" (uRAX), "=b" (uRBX), "=c" (uRCX), "=d" (uRDX) : "0" (uOperator), "2" (uIdxECX)); *(uint32_t *)pvEAX = (uint32_t)uRAX; *(uint32_t *)pvEBX = (uint32_t)uRBX; *(uint32_t *)pvECX = (uint32_t)uRCX; *(uint32_t *)pvEDX = (uint32_t)uRDX; # else __asm__ ("xchgl %%ebx, %1\n\t" "cpuid\n\t" "xchgl %%ebx, %1\n\t" : "=a" (*(uint32_t *)pvEAX), "=r" (*(uint32_t *)pvEBX), "=c" (*(uint32_t *)pvECX), "=d" (*(uint32_t *)pvEDX) : "0" (uOperator), "2" (uIdxECX)); # endif # elif RT_INLINE_ASM_USES_INTRIN int aInfo[4]; __cpuidex(aInfo, uOperator, uIdxECX); *(uint32_t *)pvEAX = aInfo[0]; *(uint32_t *)pvEBX = aInfo[1]; *(uint32_t *)pvECX = aInfo[2]; *(uint32_t *)pvEDX = aInfo[3]; # else uint32_t uEAX; uint32_t uEBX; uint32_t uECX; uint32_t uEDX; __asm { push ebx mov eax, [uOperator] mov ecx, [uIdxECX] cpuid mov [uEAX], eax mov [uEBX], ebx mov [uECX], ecx mov [uEDX], edx pop ebx } *(uint32_t *)pvEAX = uEAX; *(uint32_t *)pvEBX = uEBX; *(uint32_t *)pvECX = uECX; *(uint32_t *)pvEDX = uEDX; # endif } #endif /** * CPUID variant that initializes all 4 registers before the CPUID instruction. * * @returns The EAX result value. * @param uOperator CPUID operation (eax). * @param uInitEBX The value to assign EBX prior to the CPUID instruction. * @param uInitECX The value to assign ECX prior to the CPUID instruction. * @param uInitEDX The value to assign EDX prior to the CPUID instruction. * @param pvEAX Where to store eax. Optional. * @param pvEBX Where to store ebx. Optional. * @param pvECX Where to store ecx. Optional. * @param pvEDX Where to store edx. Optional. */ DECLASM(uint32_t) ASMCpuIdExSlow(uint32_t uOperator, uint32_t uInitEBX, uint32_t uInitECX, uint32_t uInitEDX, void *pvEAX, void *pvEBX, void *pvECX, void *pvEDX); /** * Performs the cpuid instruction returning ecx and edx. * * @param uOperator CPUID operation (eax). * @param pvECX Where to store ecx. * @param pvEDX Where to store edx. * @remark We're using void pointers to ease the use of special bitfield structures and such. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMCpuId_ECX_EDX(uint32_t uOperator, void *pvECX, void *pvEDX); #else DECLINLINE(void) ASMCpuId_ECX_EDX(uint32_t uOperator, void *pvECX, void *pvEDX) { uint32_t uEBX; ASMCpuId(uOperator, &uOperator, &uEBX, pvECX, pvEDX); } #endif /** * Performs the cpuid instruction returning eax. * * @param uOperator CPUID operation (eax). * @returns EAX after cpuid operation. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint32_t) ASMCpuId_EAX(uint32_t uOperator); #else DECLINLINE(uint32_t) ASMCpuId_EAX(uint32_t uOperator) { RTCCUINTREG xAX; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ ("cpuid" : "=a" (xAX) : "0" (uOperator) : "rbx", "rcx", "rdx"); # elif (defined(PIC) || defined(__PIC__)) && defined(__i386__) __asm__ ("push %%ebx\n\t" "cpuid\n\t" "pop %%ebx\n\t" : "=a" (xAX) : "0" (uOperator) : "ecx", "edx"); # else __asm__ ("cpuid" : "=a" (xAX) : "0" (uOperator) : "edx", "ecx", "ebx"); # endif # elif RT_INLINE_ASM_USES_INTRIN int aInfo[4]; __cpuid(aInfo, uOperator); xAX = aInfo[0]; # else __asm { push ebx mov eax, [uOperator] cpuid mov [xAX], eax pop ebx } # endif return (uint32_t)xAX; } #endif /** * Performs the cpuid instruction returning ebx. * * @param uOperator CPUID operation (eax). * @returns EBX after cpuid operation. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint32_t) ASMCpuId_EBX(uint32_t uOperator); #else DECLINLINE(uint32_t) ASMCpuId_EBX(uint32_t uOperator) { RTCCUINTREG xBX; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 RTCCUINTREG uSpill; __asm__ ("cpuid" : "=a" (uSpill), "=b" (xBX) : "0" (uOperator) : "rdx", "rcx"); # elif (defined(PIC) || defined(__PIC__)) && defined(__i386__) __asm__ ("push %%ebx\n\t" "cpuid\n\t" "mov %%ebx, %%edx\n\t" "pop %%ebx\n\t" : "=a" (uOperator), "=d" (xBX) : "0" (uOperator) : "ecx"); # else __asm__ ("cpuid" : "=a" (uOperator), "=b" (xBX) : "0" (uOperator) : "edx", "ecx"); # endif # elif RT_INLINE_ASM_USES_INTRIN int aInfo[4]; __cpuid(aInfo, uOperator); xBX = aInfo[1]; # else __asm { push ebx mov eax, [uOperator] cpuid mov [xBX], ebx pop ebx } # endif return (uint32_t)xBX; } #endif /** * Performs the cpuid instruction returning ecx. * * @param uOperator CPUID operation (eax). * @returns ECX after cpuid operation. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint32_t) ASMCpuId_ECX(uint32_t uOperator); #else DECLINLINE(uint32_t) ASMCpuId_ECX(uint32_t uOperator) { RTCCUINTREG xCX; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 RTCCUINTREG uSpill; __asm__ ("cpuid" : "=a" (uSpill), "=c" (xCX) : "0" (uOperator) : "rbx", "rdx"); # elif (defined(PIC) || defined(__PIC__)) && defined(__i386__) __asm__ ("push %%ebx\n\t" "cpuid\n\t" "pop %%ebx\n\t" : "=a" (uOperator), "=c" (xCX) : "0" (uOperator) : "edx"); # else __asm__ ("cpuid" : "=a" (uOperator), "=c" (xCX) : "0" (uOperator) : "ebx", "edx"); # endif # elif RT_INLINE_ASM_USES_INTRIN int aInfo[4]; __cpuid(aInfo, uOperator); xCX = aInfo[2]; # else __asm { push ebx mov eax, [uOperator] cpuid mov [xCX], ecx pop ebx } # endif return (uint32_t)xCX; } #endif /** * Performs the cpuid instruction returning edx. * * @param uOperator CPUID operation (eax). * @returns EDX after cpuid operation. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint32_t) ASMCpuId_EDX(uint32_t uOperator); #else DECLINLINE(uint32_t) ASMCpuId_EDX(uint32_t uOperator) { RTCCUINTREG xDX; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 RTCCUINTREG uSpill; __asm__ ("cpuid" : "=a" (uSpill), "=d" (xDX) : "0" (uOperator) : "rbx", "rcx"); # elif (defined(PIC) || defined(__PIC__)) && defined(__i386__) __asm__ ("push %%ebx\n\t" "cpuid\n\t" "pop %%ebx\n\t" : "=a" (uOperator), "=d" (xDX) : "0" (uOperator) : "ecx"); # else __asm__ ("cpuid" : "=a" (uOperator), "=d" (xDX) : "0" (uOperator) : "ebx", "ecx"); # endif # elif RT_INLINE_ASM_USES_INTRIN int aInfo[4]; __cpuid(aInfo, uOperator); xDX = aInfo[3]; # else __asm { push ebx mov eax, [uOperator] cpuid mov [xDX], edx pop ebx } # endif return (uint32_t)xDX; } #endif /** * Checks if the current CPU supports CPUID. * * @returns true if CPUID is supported. */ DECLINLINE(bool) ASMHasCpuId(void) { #ifdef RT_ARCH_AMD64 return true; /* ASSUME that all amd64 compatible CPUs have cpuid. */ #else /* !RT_ARCH_AMD64 */ bool fRet = false; # if RT_INLINE_ASM_GNU_STYLE uint32_t u1; uint32_t u2; __asm__ ("pushf\n\t" "pop %1\n\t" "mov %1, %2\n\t" "xorl $0x200000, %1\n\t" "push %1\n\t" "popf\n\t" "pushf\n\t" "pop %1\n\t" "cmpl %1, %2\n\t" "setne %0\n\t" "push %2\n\t" "popf\n\t" : "=m" (fRet), "=r" (u1), "=r" (u2)); # else __asm { pushfd pop eax mov ebx, eax xor eax, 0200000h push eax popfd pushfd pop eax cmp eax, ebx setne fRet push ebx popfd } # endif return fRet; #endif /* !RT_ARCH_AMD64 */ } /** * Gets the APIC ID of the current CPU. * * @returns the APIC ID. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint8_t) ASMGetApicId(void); #else DECLINLINE(uint8_t) ASMGetApicId(void) { RTCCUINTREG xBX; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 RTCCUINTREG uSpill; __asm__ __volatile__ ("cpuid" : "=a" (uSpill), "=b" (xBX) : "0" (1) : "rcx", "rdx"); # elif (defined(PIC) || defined(__PIC__)) && defined(__i386__) RTCCUINTREG uSpill; __asm__ __volatile__ ("mov %%ebx,%1\n\t" "cpuid\n\t" "xchgl %%ebx,%1\n\t" : "=a" (uSpill), "=rm" (xBX) : "0" (1) : "ecx", "edx"); # else RTCCUINTREG uSpill; __asm__ __volatile__ ("cpuid" : "=a" (uSpill), "=b" (xBX) : "0" (1) : "ecx", "edx"); # endif # elif RT_INLINE_ASM_USES_INTRIN int aInfo[4]; __cpuid(aInfo, 1); xBX = aInfo[1]; # else __asm { push ebx mov eax, 1 cpuid mov [xBX], ebx pop ebx } # endif return (uint8_t)(xBX >> 24); } #endif /** * Tests if it a genuine Intel CPU based on the ASMCpuId(0) output. * * @returns true/false. * @param uEBX EBX return from ASMCpuId(0) * @param uECX ECX return from ASMCpuId(0) * @param uEDX EDX return from ASMCpuId(0) */ DECLINLINE(bool) ASMIsIntelCpuEx(uint32_t uEBX, uint32_t uECX, uint32_t uEDX) { return uEBX == UINT32_C(0x756e6547) && uECX == UINT32_C(0x6c65746e) && uEDX == UINT32_C(0x49656e69); } /** * Tests if this is a genuine Intel CPU. * * @returns true/false. * @remarks ASSUMES that cpuid is supported by the CPU. */ DECLINLINE(bool) ASMIsIntelCpu(void) { uint32_t uEAX, uEBX, uECX, uEDX; ASMCpuId(0, &uEAX, &uEBX, &uECX, &uEDX); return ASMIsIntelCpuEx(uEBX, uECX, uEDX); } /** * Tests if it an authentic AMD CPU based on the ASMCpuId(0) output. * * @returns true/false. * @param uEBX EBX return from ASMCpuId(0) * @param uECX ECX return from ASMCpuId(0) * @param uEDX EDX return from ASMCpuId(0) */ DECLINLINE(bool) ASMIsAmdCpuEx(uint32_t uEBX, uint32_t uECX, uint32_t uEDX) { return uEBX == UINT32_C(0x68747541) && uECX == UINT32_C(0x444d4163) && uEDX == UINT32_C(0x69746e65); } /** * Tests if this is an authentic AMD CPU. * * @returns true/false. * @remarks ASSUMES that cpuid is supported by the CPU. */ DECLINLINE(bool) ASMIsAmdCpu(void) { uint32_t uEAX, uEBX, uECX, uEDX; ASMCpuId(0, &uEAX, &uEBX, &uECX, &uEDX); return ASMIsAmdCpuEx(uEBX, uECX, uEDX); } /** * Tests if it a centaur hauling VIA CPU based on the ASMCpuId(0) output. * * @returns true/false. * @param uEBX EBX return from ASMCpuId(0). * @param uECX ECX return from ASMCpuId(0). * @param uEDX EDX return from ASMCpuId(0). */ DECLINLINE(bool) ASMIsViaCentaurCpuEx(uint32_t uEBX, uint32_t uECX, uint32_t uEDX) { return uEBX == UINT32_C(0x746e6543) && uECX == UINT32_C(0x736c7561) && uEDX == UINT32_C(0x48727561); } /** * Tests if this is a centaur hauling VIA CPU. * * @returns true/false. * @remarks ASSUMES that cpuid is supported by the CPU. */ DECLINLINE(bool) ASMIsViaCentaurCpu(void) { uint32_t uEAX, uEBX, uECX, uEDX; ASMCpuId(0, &uEAX, &uEBX, &uECX, &uEDX); return ASMIsAmdCpuEx(uEBX, uECX, uEDX); } /** * Checks whether ASMCpuId_EAX(0x00000000) indicates a valid range. * * * @returns true/false. * @param uEAX The EAX value of CPUID leaf 0x00000000. * * @note This only succeeds if there are at least two leaves in the range. * @remarks The upper range limit is just some half reasonable value we've * picked out of thin air. */ DECLINLINE(bool) ASMIsValidStdRange(uint32_t uEAX) { return uEAX >= UINT32_C(0x00000001) && uEAX <= UINT32_C(0x000fffff); } /** * Checks whether ASMCpuId_EAX(0x80000000) indicates a valid range. * * This only succeeds if there are at least two leaves in the range. * * @returns true/false. * @param uEAX The EAX value of CPUID leaf 0x80000000. * * @note This only succeeds if there are at least two leaves in the range. * @remarks The upper range limit is just some half reasonable value we've * picked out of thin air. */ DECLINLINE(bool) ASMIsValidExtRange(uint32_t uEAX) { return uEAX >= UINT32_C(0x80000001) && uEAX <= UINT32_C(0x800fffff); } /** * Extracts the CPU family from ASMCpuId(1) or ASMCpuId(0x80000001) * * @returns Family. * @param uEAX EAX return from ASMCpuId(1) or ASMCpuId(0x80000001). */ DECLINLINE(uint32_t) ASMGetCpuFamily(uint32_t uEAX) { return ((uEAX >> 8) & 0xf) == 0xf ? ((uEAX >> 20) & 0x7f) + 0xf : ((uEAX >> 8) & 0xf); } /** * Extracts the CPU model from ASMCpuId(1) or ASMCpuId(0x80000001), Intel variant. * * @returns Model. * @param uEAX EAX from ASMCpuId(1) or ASMCpuId(0x80000001). */ DECLINLINE(uint32_t) ASMGetCpuModelIntel(uint32_t uEAX) { return ((uEAX >> 8) & 0xf) == 0xf || (((uEAX >> 8) & 0xf) == 0x6) /* family! */ ? ((uEAX >> 4) & 0xf) | ((uEAX >> 12) & 0xf0) : ((uEAX >> 4) & 0xf); } /** * Extracts the CPU model from ASMCpuId(1) or ASMCpuId(0x80000001), AMD variant. * * @returns Model. * @param uEAX EAX from ASMCpuId(1) or ASMCpuId(0x80000001). */ DECLINLINE(uint32_t) ASMGetCpuModelAMD(uint32_t uEAX) { return ((uEAX >> 8) & 0xf) == 0xf ? ((uEAX >> 4) & 0xf) | ((uEAX >> 12) & 0xf0) : ((uEAX >> 4) & 0xf); } /** * Extracts the CPU model from ASMCpuId(1) or ASMCpuId(0x80000001) * * @returns Model. * @param uEAX EAX from ASMCpuId(1) or ASMCpuId(0x80000001). * @param fIntel Whether it's an intel CPU. Use ASMIsIntelCpuEx() or ASMIsIntelCpu(). */ DECLINLINE(uint32_t) ASMGetCpuModel(uint32_t uEAX, bool fIntel) { return ((uEAX >> 8) & 0xf) == 0xf || (((uEAX >> 8) & 0xf) == 0x6 && fIntel) /* family! */ ? ((uEAX >> 4) & 0xf) | ((uEAX >> 12) & 0xf0) : ((uEAX >> 4) & 0xf); } /** * Extracts the CPU stepping from ASMCpuId(1) or ASMCpuId(0x80000001) * * @returns Model. * @param uEAX EAX from ASMCpuId(1) or ASMCpuId(0x80000001). */ DECLINLINE(uint32_t) ASMGetCpuStepping(uint32_t uEAX) { return uEAX & 0xf; } /** * Get cr0. * @returns cr0. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetCR0(void); #else DECLINLINE(RTCCUINTREG) ASMGetCR0(void) { RTCCUINTREG uCR0; # if RT_INLINE_ASM_USES_INTRIN uCR0 = __readcr0(); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%cr0, %0\t\n" : "=r" (uCR0)); # else __asm__ __volatile__("movl %%cr0, %0\t\n" : "=r" (uCR0)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, cr0 mov [uCR0], rax # else mov eax, cr0 mov [uCR0], eax # endif } # endif return uCR0; } #endif /** * Sets the CR0 register. * @param uCR0 The new CR0 value. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMSetCR0(RTCCUINTREG uCR0); #else DECLINLINE(void) ASMSetCR0(RTCCUINTREG uCR0) { # if RT_INLINE_ASM_USES_INTRIN __writecr0(uCR0); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%cr0\n\t" :: "r" (uCR0)); # else __asm__ __volatile__("movl %0, %%cr0\n\t" :: "r" (uCR0)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uCR0] mov cr0, rax # else mov eax, [uCR0] mov cr0, eax # endif } # endif } #endif /** * Get cr2. * @returns cr2. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetCR2(void); #else DECLINLINE(RTCCUINTREG) ASMGetCR2(void) { RTCCUINTREG uCR2; # if RT_INLINE_ASM_USES_INTRIN uCR2 = __readcr2(); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%cr2, %0\t\n" : "=r" (uCR2)); # else __asm__ __volatile__("movl %%cr2, %0\t\n" : "=r" (uCR2)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, cr2 mov [uCR2], rax # else mov eax, cr2 mov [uCR2], eax # endif } # endif return uCR2; } #endif /** * Sets the CR2 register. * @param uCR2 The new CR0 value. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(void) ASMSetCR2(RTCCUINTREG uCR2); #else DECLINLINE(void) ASMSetCR2(RTCCUINTREG uCR2) { # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%cr2\n\t" :: "r" (uCR2)); # else __asm__ __volatile__("movl %0, %%cr2\n\t" :: "r" (uCR2)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uCR2] mov cr2, rax # else mov eax, [uCR2] mov cr2, eax # endif } # endif } #endif /** * Get cr3. * @returns cr3. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetCR3(void); #else DECLINLINE(RTCCUINTREG) ASMGetCR3(void) { RTCCUINTREG uCR3; # if RT_INLINE_ASM_USES_INTRIN uCR3 = __readcr3(); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%cr3, %0\t\n" : "=r" (uCR3)); # else __asm__ __volatile__("movl %%cr3, %0\t\n" : "=r" (uCR3)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, cr3 mov [uCR3], rax # else mov eax, cr3 mov [uCR3], eax # endif } # endif return uCR3; } #endif /** * Sets the CR3 register. * * @param uCR3 New CR3 value. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMSetCR3(RTCCUINTREG uCR3); #else DECLINLINE(void) ASMSetCR3(RTCCUINTREG uCR3) { # if RT_INLINE_ASM_USES_INTRIN __writecr3(uCR3); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%cr3\n\t" : : "r" (uCR3)); # else __asm__ __volatile__("movl %0, %%cr3\n\t" : : "r" (uCR3)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uCR3] mov cr3, rax # else mov eax, [uCR3] mov cr3, eax # endif } # endif } #endif /** * Reloads the CR3 register. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMReloadCR3(void); #else DECLINLINE(void) ASMReloadCR3(void) { # if RT_INLINE_ASM_USES_INTRIN __writecr3(__readcr3()); # elif RT_INLINE_ASM_GNU_STYLE RTCCUINTREG u; # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%cr3, %0\n\t" "movq %0, %%cr3\n\t" : "=r" (u)); # else __asm__ __volatile__("movl %%cr3, %0\n\t" "movl %0, %%cr3\n\t" : "=r" (u)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, cr3 mov cr3, rax # else mov eax, cr3 mov cr3, eax # endif } # endif } #endif /** * Get cr4. * @returns cr4. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetCR4(void); #else DECLINLINE(RTCCUINTREG) ASMGetCR4(void) { RTCCUINTREG uCR4; # if RT_INLINE_ASM_USES_INTRIN uCR4 = __readcr4(); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%cr4, %0\t\n" : "=r" (uCR4)); # else __asm__ __volatile__("movl %%cr4, %0\t\n" : "=r" (uCR4)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, cr4 mov [uCR4], rax # else push eax /* just in case */ /*mov eax, cr4*/ _emit 0x0f _emit 0x20 _emit 0xe0 mov [uCR4], eax pop eax # endif } # endif return uCR4; } #endif /** * Sets the CR4 register. * * @param uCR4 New CR4 value. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMSetCR4(RTCCUINTREG uCR4); #else DECLINLINE(void) ASMSetCR4(RTCCUINTREG uCR4) { # if RT_INLINE_ASM_USES_INTRIN __writecr4(uCR4); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%cr4\n\t" : : "r" (uCR4)); # else __asm__ __volatile__("movl %0, %%cr4\n\t" : : "r" (uCR4)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uCR4] mov cr4, rax # else mov eax, [uCR4] _emit 0x0F _emit 0x22 _emit 0xE0 /* mov cr4, eax */ # endif } # endif } #endif /** * Get cr8. * @returns cr8. * @remark The lock prefix hack for access from non-64-bit modes is NOT used and 0 is returned. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetCR8(void); #else DECLINLINE(RTCCUINTREG) ASMGetCR8(void) { # ifdef RT_ARCH_AMD64 RTCCUINTREG uCR8; # if RT_INLINE_ASM_USES_INTRIN uCR8 = __readcr8(); # elif RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("movq %%cr8, %0\t\n" : "=r" (uCR8)); # else __asm { mov rax, cr8 mov [uCR8], rax } # endif return uCR8; # else /* !RT_ARCH_AMD64 */ return 0; # endif /* !RT_ARCH_AMD64 */ } #endif /** * Get XCR0 (eXtended feature Control Register 0). * @returns xcr0. */ DECLASM(uint64_t) ASMGetXcr0(void); /** * Sets the XCR0 register. * @param uXcr0 The new XCR0 value. */ DECLASM(void) ASMSetXcr0(uint64_t uXcr0); struct X86XSAVEAREA; /** * Save extended CPU state. * @param pXStateArea Where to save the state. * @param fComponents Which state components to save. */ DECLASM(void) ASMXSave(struct X86XSAVEAREA *pXStateArea, uint64_t fComponents); /** * Loads extended CPU state. * @param pXStateArea Where to load the state from. * @param fComponents Which state components to load. */ DECLASM(void) ASMXRstor(struct X86XSAVEAREA const *pXStateArea, uint64_t fComponents); /** * Enables interrupts (EFLAGS.IF). */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMIntEnable(void); #else DECLINLINE(void) ASMIntEnable(void) { # if RT_INLINE_ASM_GNU_STYLE __asm("sti\n"); # elif RT_INLINE_ASM_USES_INTRIN _enable(); # else __asm sti # endif } #endif /** * Disables interrupts (!EFLAGS.IF). */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMIntDisable(void); #else DECLINLINE(void) ASMIntDisable(void) { # if RT_INLINE_ASM_GNU_STYLE __asm("cli\n"); # elif RT_INLINE_ASM_USES_INTRIN _disable(); # else __asm cli # endif } #endif /** * Disables interrupts and returns previous xFLAGS. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMIntDisableFlags(void); #else DECLINLINE(RTCCUINTREG) ASMIntDisableFlags(void) { RTCCUINTREG xFlags; # if RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("pushfq\n\t" "cli\n\t" "popq %0\n\t" : "=r" (xFlags)); # else __asm__ __volatile__("pushfl\n\t" "cli\n\t" "popl %0\n\t" : "=r" (xFlags)); # endif # elif RT_INLINE_ASM_USES_INTRIN && !defined(RT_ARCH_X86) xFlags = ASMGetFlags(); _disable(); # else __asm { pushfd cli pop [xFlags] } # endif return xFlags; } #endif /** * Are interrupts enabled? * * @returns true / false. */ DECLINLINE(bool) ASMIntAreEnabled(void) { RTCCUINTREG uFlags = ASMGetFlags(); return uFlags & 0x200 /* X86_EFL_IF */ ? true : false; } /** * Halts the CPU until interrupted. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(void) ASMHalt(void); #else DECLINLINE(void) ASMHalt(void) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("hlt\n\t"); # else __asm { hlt } # endif } #endif /** * Reads a machine specific register. * * @returns Register content. * @param uRegister Register to read. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint64_t) ASMRdMsr(uint32_t uRegister); #else DECLINLINE(uint64_t) ASMRdMsr(uint32_t uRegister) { RTUINT64U u; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rdmsr\n\t" : "=a" (u.s.Lo), "=d" (u.s.Hi) : "c" (uRegister)); # elif RT_INLINE_ASM_USES_INTRIN u.u = __readmsr(uRegister); # else __asm { mov ecx, [uRegister] rdmsr mov [u.s.Lo], eax mov [u.s.Hi], edx } # endif return u.u; } #endif /** * Writes a machine specific register. * * @returns Register content. * @param uRegister Register to write to. * @param u64Val Value to write. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMWrMsr(uint32_t uRegister, uint64_t u64Val); #else DECLINLINE(void) ASMWrMsr(uint32_t uRegister, uint64_t u64Val) { RTUINT64U u; u.u = u64Val; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("wrmsr\n\t" ::"a" (u.s.Lo), "d" (u.s.Hi), "c" (uRegister)); # elif RT_INLINE_ASM_USES_INTRIN __writemsr(uRegister, u.u); # else __asm { mov ecx, [uRegister] mov edx, [u.s.Hi] mov eax, [u.s.Lo] wrmsr } # endif } #endif /** * Reads a machine specific register, extended version (for AMD). * * @returns Register content. * @param uRegister Register to read. * @param uXDI RDI/EDI value. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(uint64_t) ASMRdMsrEx(uint32_t uRegister, RTCCUINTREG uXDI); #else DECLINLINE(uint64_t) ASMRdMsrEx(uint32_t uRegister, RTCCUINTREG uXDI) { RTUINT64U u; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rdmsr\n\t" : "=a" (u.s.Lo), "=d" (u.s.Hi) : "c" (uRegister), "D" (uXDI)); # else __asm { mov ecx, [uRegister] xchg edi, [uXDI] rdmsr mov [u.s.Lo], eax mov [u.s.Hi], edx xchg edi, [uXDI] } # endif return u.u; } #endif /** * Writes a machine specific register, extended version (for AMD). * * @returns Register content. * @param uRegister Register to write to. * @param uXDI RDI/EDI value. * @param u64Val Value to write. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(void) ASMWrMsrEx(uint32_t uRegister, RTCCUINTREG uXDI, uint64_t u64Val); #else DECLINLINE(void) ASMWrMsrEx(uint32_t uRegister, RTCCUINTREG uXDI, uint64_t u64Val) { RTUINT64U u; u.u = u64Val; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("wrmsr\n\t" ::"a" (u.s.Lo), "d" (u.s.Hi), "c" (uRegister), "D" (uXDI)); # else __asm { mov ecx, [uRegister] xchg edi, [uXDI] mov edx, [u.s.Hi] mov eax, [u.s.Lo] wrmsr xchg edi, [uXDI] } # endif } #endif /** * Reads low part of a machine specific register. * * @returns Register content. * @param uRegister Register to read. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint32_t) ASMRdMsr_Low(uint32_t uRegister); #else DECLINLINE(uint32_t) ASMRdMsr_Low(uint32_t uRegister) { uint32_t u32; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rdmsr\n\t" : "=a" (u32) : "c" (uRegister) : "edx"); # elif RT_INLINE_ASM_USES_INTRIN u32 = (uint32_t)__readmsr(uRegister); #else __asm { mov ecx, [uRegister] rdmsr mov [u32], eax } # endif return u32; } #endif /** * Reads high part of a machine specific register. * * @returns Register content. * @param uRegister Register to read. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint32_t) ASMRdMsr_High(uint32_t uRegister); #else DECLINLINE(uint32_t) ASMRdMsr_High(uint32_t uRegister) { uint32_t u32; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rdmsr\n\t" : "=d" (u32) : "c" (uRegister) : "eax"); # elif RT_INLINE_ASM_USES_INTRIN u32 = (uint32_t)(__readmsr(uRegister) >> 32); # else __asm { mov ecx, [uRegister] rdmsr mov [u32], edx } # endif return u32; } #endif /** * Gets dr0. * * @returns dr0. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetDR0(void); #else DECLINLINE(RTCCUINTREG) ASMGetDR0(void) { RTCCUINTREG uDR0; # if RT_INLINE_ASM_USES_INTRIN uDR0 = __readdr(0); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%dr0, %0\n\t" : "=r" (uDR0)); # else __asm__ __volatile__("movl %%dr0, %0\n\t" : "=r" (uDR0)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, dr0 mov [uDR0], rax # else mov eax, dr0 mov [uDR0], eax # endif } # endif return uDR0; } #endif /** * Gets dr1. * * @returns dr1. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetDR1(void); #else DECLINLINE(RTCCUINTREG) ASMGetDR1(void) { RTCCUINTREG uDR1; # if RT_INLINE_ASM_USES_INTRIN uDR1 = __readdr(1); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%dr1, %0\n\t" : "=r" (uDR1)); # else __asm__ __volatile__("movl %%dr1, %0\n\t" : "=r" (uDR1)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, dr1 mov [uDR1], rax # else mov eax, dr1 mov [uDR1], eax # endif } # endif return uDR1; } #endif /** * Gets dr2. * * @returns dr2. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetDR2(void); #else DECLINLINE(RTCCUINTREG) ASMGetDR2(void) { RTCCUINTREG uDR2; # if RT_INLINE_ASM_USES_INTRIN uDR2 = __readdr(2); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%dr2, %0\n\t" : "=r" (uDR2)); # else __asm__ __volatile__("movl %%dr2, %0\n\t" : "=r" (uDR2)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, dr2 mov [uDR2], rax # else mov eax, dr2 mov [uDR2], eax # endif } # endif return uDR2; } #endif /** * Gets dr3. * * @returns dr3. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetDR3(void); #else DECLINLINE(RTCCUINTREG) ASMGetDR3(void) { RTCCUINTREG uDR3; # if RT_INLINE_ASM_USES_INTRIN uDR3 = __readdr(3); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%dr3, %0\n\t" : "=r" (uDR3)); # else __asm__ __volatile__("movl %%dr3, %0\n\t" : "=r" (uDR3)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, dr3 mov [uDR3], rax # else mov eax, dr3 mov [uDR3], eax # endif } # endif return uDR3; } #endif /** * Gets dr6. * * @returns dr6. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetDR6(void); #else DECLINLINE(RTCCUINTREG) ASMGetDR6(void) { RTCCUINTREG uDR6; # if RT_INLINE_ASM_USES_INTRIN uDR6 = __readdr(6); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%dr6, %0\n\t" : "=r" (uDR6)); # else __asm__ __volatile__("movl %%dr6, %0\n\t" : "=r" (uDR6)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, dr6 mov [uDR6], rax # else mov eax, dr6 mov [uDR6], eax # endif } # endif return uDR6; } #endif /** * Reads and clears DR6. * * @returns DR6. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetAndClearDR6(void); #else DECLINLINE(RTCCUINTREG) ASMGetAndClearDR6(void) { RTCCUINTREG uDR6; # if RT_INLINE_ASM_USES_INTRIN uDR6 = __readdr(6); __writedr(6, 0xffff0ff0U); /* 31-16 and 4-11 are 1's, 12 and 63-31 are zero. */ # elif RT_INLINE_ASM_GNU_STYLE RTCCUINTREG uNewValue = 0xffff0ff0U;/* 31-16 and 4-11 are 1's, 12 and 63-31 are zero. */ # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%dr6, %0\n\t" "movq %1, %%dr6\n\t" : "=r" (uDR6) : "r" (uNewValue)); # else __asm__ __volatile__("movl %%dr6, %0\n\t" "movl %1, %%dr6\n\t" : "=r" (uDR6) : "r" (uNewValue)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, dr6 mov [uDR6], rax mov rcx, rax mov ecx, 0ffff0ff0h; /* 31-16 and 4-11 are 1's, 12 and 63-31 are zero. */ mov dr6, rcx # else mov eax, dr6 mov [uDR6], eax mov ecx, 0ffff0ff0h; /* 31-16 and 4-11 are 1's, 12 is zero. */ mov dr6, ecx # endif } # endif return uDR6; } #endif /** * Gets dr7. * * @returns dr7. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(RTCCUINTREG) ASMGetDR7(void); #else DECLINLINE(RTCCUINTREG) ASMGetDR7(void) { RTCCUINTREG uDR7; # if RT_INLINE_ASM_USES_INTRIN uDR7 = __readdr(7); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %%dr7, %0\n\t" : "=r" (uDR7)); # else __asm__ __volatile__("movl %%dr7, %0\n\t" : "=r" (uDR7)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, dr7 mov [uDR7], rax # else mov eax, dr7 mov [uDR7], eax # endif } # endif return uDR7; } #endif /** * Sets dr0. * * @param uDRVal Debug register value to write */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMSetDR0(RTCCUINTREG uDRVal); #else DECLINLINE(void) ASMSetDR0(RTCCUINTREG uDRVal) { # if RT_INLINE_ASM_USES_INTRIN __writedr(0, uDRVal); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%dr0\n\t" : : "r" (uDRVal)); # else __asm__ __volatile__("movl %0, %%dr0\n\t" : : "r" (uDRVal)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uDRVal] mov dr0, rax # else mov eax, [uDRVal] mov dr0, eax # endif } # endif } #endif /** * Sets dr1. * * @param uDRVal Debug register value to write */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMSetDR1(RTCCUINTREG uDRVal); #else DECLINLINE(void) ASMSetDR1(RTCCUINTREG uDRVal) { # if RT_INLINE_ASM_USES_INTRIN __writedr(1, uDRVal); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%dr1\n\t" : : "r" (uDRVal)); # else __asm__ __volatile__("movl %0, %%dr1\n\t" : : "r" (uDRVal)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uDRVal] mov dr1, rax # else mov eax, [uDRVal] mov dr1, eax # endif } # endif } #endif /** * Sets dr2. * * @param uDRVal Debug register value to write */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMSetDR2(RTCCUINTREG uDRVal); #else DECLINLINE(void) ASMSetDR2(RTCCUINTREG uDRVal) { # if RT_INLINE_ASM_USES_INTRIN __writedr(2, uDRVal); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%dr2\n\t" : : "r" (uDRVal)); # else __asm__ __volatile__("movl %0, %%dr2\n\t" : : "r" (uDRVal)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uDRVal] mov dr2, rax # else mov eax, [uDRVal] mov dr2, eax # endif } # endif } #endif /** * Sets dr3. * * @param uDRVal Debug register value to write */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMSetDR3(RTCCUINTREG uDRVal); #else DECLINLINE(void) ASMSetDR3(RTCCUINTREG uDRVal) { # if RT_INLINE_ASM_USES_INTRIN __writedr(3, uDRVal); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%dr3\n\t" : : "r" (uDRVal)); # else __asm__ __volatile__("movl %0, %%dr3\n\t" : : "r" (uDRVal)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uDRVal] mov dr3, rax # else mov eax, [uDRVal] mov dr3, eax # endif } # endif } #endif /** * Sets dr6. * * @param uDRVal Debug register value to write */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMSetDR6(RTCCUINTREG uDRVal); #else DECLINLINE(void) ASMSetDR6(RTCCUINTREG uDRVal) { # if RT_INLINE_ASM_USES_INTRIN __writedr(6, uDRVal); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%dr6\n\t" : : "r" (uDRVal)); # else __asm__ __volatile__("movl %0, %%dr6\n\t" : : "r" (uDRVal)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uDRVal] mov dr6, rax # else mov eax, [uDRVal] mov dr6, eax # endif } # endif } #endif /** * Sets dr7. * * @param uDRVal Debug register value to write */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMSetDR7(RTCCUINTREG uDRVal); #else DECLINLINE(void) ASMSetDR7(RTCCUINTREG uDRVal) { # if RT_INLINE_ASM_USES_INTRIN __writedr(7, uDRVal); # elif RT_INLINE_ASM_GNU_STYLE # ifdef RT_ARCH_AMD64 __asm__ __volatile__("movq %0, %%dr7\n\t" : : "r" (uDRVal)); # else __asm__ __volatile__("movl %0, %%dr7\n\t" : : "r" (uDRVal)); # endif # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [uDRVal] mov dr7, rax # else mov eax, [uDRVal] mov dr7, eax # endif } # endif } #endif /** * Writes a 8-bit unsigned integer to an I/O port, ordered. * * @param Port I/O port to write to. * @param u8 8-bit integer to write. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMOutU8(RTIOPORT Port, uint8_t u8); #else DECLINLINE(void) ASMOutU8(RTIOPORT Port, uint8_t u8) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("outb %b1, %w0\n\t" :: "Nd" (Port), "a" (u8)); # elif RT_INLINE_ASM_USES_INTRIN __outbyte(Port, u8); # else __asm { mov dx, [Port] mov al, [u8] out dx, al } # endif } #endif /** * Reads a 8-bit unsigned integer from an I/O port, ordered. * * @returns 8-bit integer. * @param Port I/O port to read from. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint8_t) ASMInU8(RTIOPORT Port); #else DECLINLINE(uint8_t) ASMInU8(RTIOPORT Port) { uint8_t u8; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("inb %w1, %b0\n\t" : "=a" (u8) : "Nd" (Port)); # elif RT_INLINE_ASM_USES_INTRIN u8 = __inbyte(Port); # else __asm { mov dx, [Port] in al, dx mov [u8], al } # endif return u8; } #endif /** * Writes a 16-bit unsigned integer to an I/O port, ordered. * * @param Port I/O port to write to. * @param u16 16-bit integer to write. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMOutU16(RTIOPORT Port, uint16_t u16); #else DECLINLINE(void) ASMOutU16(RTIOPORT Port, uint16_t u16) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("outw %w1, %w0\n\t" :: "Nd" (Port), "a" (u16)); # elif RT_INLINE_ASM_USES_INTRIN __outword(Port, u16); # else __asm { mov dx, [Port] mov ax, [u16] out dx, ax } # endif } #endif /** * Reads a 16-bit unsigned integer from an I/O port, ordered. * * @returns 16-bit integer. * @param Port I/O port to read from. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint16_t) ASMInU16(RTIOPORT Port); #else DECLINLINE(uint16_t) ASMInU16(RTIOPORT Port) { uint16_t u16; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("inw %w1, %w0\n\t" : "=a" (u16) : "Nd" (Port)); # elif RT_INLINE_ASM_USES_INTRIN u16 = __inword(Port); # else __asm { mov dx, [Port] in ax, dx mov [u16], ax } # endif return u16; } #endif /** * Writes a 32-bit unsigned integer to an I/O port, ordered. * * @param Port I/O port to write to. * @param u32 32-bit integer to write. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMOutU32(RTIOPORT Port, uint32_t u32); #else DECLINLINE(void) ASMOutU32(RTIOPORT Port, uint32_t u32) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("outl %1, %w0\n\t" :: "Nd" (Port), "a" (u32)); # elif RT_INLINE_ASM_USES_INTRIN __outdword(Port, u32); # else __asm { mov dx, [Port] mov eax, [u32] out dx, eax } # endif } #endif /** * Reads a 32-bit unsigned integer from an I/O port, ordered. * * @returns 32-bit integer. * @param Port I/O port to read from. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(uint32_t) ASMInU32(RTIOPORT Port); #else DECLINLINE(uint32_t) ASMInU32(RTIOPORT Port) { uint32_t u32; # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("inl %w1, %0\n\t" : "=a" (u32) : "Nd" (Port)); # elif RT_INLINE_ASM_USES_INTRIN u32 = __indword(Port); # else __asm { mov dx, [Port] in eax, dx mov [u32], eax } # endif return u32; } #endif /** * Writes a string of 8-bit unsigned integer items to an I/O port, ordered. * * @param Port I/O port to write to. * @param pau8 Pointer to the string buffer. * @param c The number of items to write. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMOutStrU8(RTIOPORT Port, uint8_t const *pau8, size_t c); #else DECLINLINE(void) ASMOutStrU8(RTIOPORT Port, uint8_t const *pau8, size_t c) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rep; outsb\n\t" : "+S" (pau8), "+c" (c) : "d" (Port)); # elif RT_INLINE_ASM_USES_INTRIN __outbytestring(Port, (unsigned char *)pau8, (unsigned long)c); # else __asm { mov dx, [Port] mov ecx, [c] mov eax, [pau8] xchg esi, eax rep outsb xchg esi, eax } # endif } #endif /** * Reads a string of 8-bit unsigned integer items from an I/O port, ordered. * * @param Port I/O port to read from. * @param pau8 Pointer to the string buffer (output). * @param c The number of items to read. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMInStrU8(RTIOPORT Port, uint8_t *pau8, size_t c); #else DECLINLINE(void) ASMInStrU8(RTIOPORT Port, uint8_t *pau8, size_t c) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rep; insb\n\t" : "+D" (pau8), "+c" (c) : "d" (Port)); # elif RT_INLINE_ASM_USES_INTRIN __inbytestring(Port, pau8, (unsigned long)c); # else __asm { mov dx, [Port] mov ecx, [c] mov eax, [pau8] xchg edi, eax rep insb xchg edi, eax } # endif } #endif /** * Writes a string of 16-bit unsigned integer items to an I/O port, ordered. * * @param Port I/O port to write to. * @param pau16 Pointer to the string buffer. * @param c The number of items to write. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMOutStrU16(RTIOPORT Port, uint16_t const *pau16, size_t c); #else DECLINLINE(void) ASMOutStrU16(RTIOPORT Port, uint16_t const *pau16, size_t c) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rep; outsw\n\t" : "+S" (pau16), "+c" (c) : "d" (Port)); # elif RT_INLINE_ASM_USES_INTRIN __outwordstring(Port, (unsigned short *)pau16, (unsigned long)c); # else __asm { mov dx, [Port] mov ecx, [c] mov eax, [pau16] xchg esi, eax rep outsw xchg esi, eax } # endif } #endif /** * Reads a string of 16-bit unsigned integer items from an I/O port, ordered. * * @param Port I/O port to read from. * @param pau16 Pointer to the string buffer (output). * @param c The number of items to read. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMInStrU16(RTIOPORT Port, uint16_t *pau16, size_t c); #else DECLINLINE(void) ASMInStrU16(RTIOPORT Port, uint16_t *pau16, size_t c) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rep; insw\n\t" : "+D" (pau16), "+c" (c) : "d" (Port)); # elif RT_INLINE_ASM_USES_INTRIN __inwordstring(Port, pau16, (unsigned long)c); # else __asm { mov dx, [Port] mov ecx, [c] mov eax, [pau16] xchg edi, eax rep insw xchg edi, eax } # endif } #endif /** * Writes a string of 32-bit unsigned integer items to an I/O port, ordered. * * @param Port I/O port to write to. * @param pau32 Pointer to the string buffer. * @param c The number of items to write. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMOutStrU32(RTIOPORT Port, uint32_t const *pau32, size_t c); #else DECLINLINE(void) ASMOutStrU32(RTIOPORT Port, uint32_t const *pau32, size_t c) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rep; outsl\n\t" : "+S" (pau32), "+c" (c) : "d" (Port)); # elif RT_INLINE_ASM_USES_INTRIN __outdwordstring(Port, (unsigned long *)pau32, (unsigned long)c); # else __asm { mov dx, [Port] mov ecx, [c] mov eax, [pau32] xchg esi, eax rep outsd xchg esi, eax } # endif } #endif /** * Reads a string of 32-bit unsigned integer items from an I/O port, ordered. * * @param Port I/O port to read from. * @param pau32 Pointer to the string buffer (output). * @param c The number of items to read. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMInStrU32(RTIOPORT Port, uint32_t *pau32, size_t c); #else DECLINLINE(void) ASMInStrU32(RTIOPORT Port, uint32_t *pau32, size_t c) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("rep; insl\n\t" : "+D" (pau32), "+c" (c) : "d" (Port)); # elif RT_INLINE_ASM_USES_INTRIN __indwordstring(Port, (unsigned long *)pau32, (unsigned long)c); # else __asm { mov dx, [Port] mov ecx, [c] mov eax, [pau32] xchg edi, eax rep insd xchg edi, eax } # endif } #endif /** * Invalidate page. * * @param pv Address of the page to invalidate. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMInvalidatePage(void *pv); #else DECLINLINE(void) ASMInvalidatePage(void *pv) { # if RT_INLINE_ASM_USES_INTRIN __invlpg(pv); # elif RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("invlpg %0\n\t" : : "m" (*(uint8_t *)pv)); # else __asm { # ifdef RT_ARCH_AMD64 mov rax, [pv] invlpg [rax] # else mov eax, [pv] invlpg [eax] # endif } # endif } #endif /** * Write back the internal caches and invalidate them. */ #if RT_INLINE_ASM_EXTERNAL && !RT_INLINE_ASM_USES_INTRIN DECLASM(void) ASMWriteBackAndInvalidateCaches(void); #else DECLINLINE(void) ASMWriteBackAndInvalidateCaches(void) { # if RT_INLINE_ASM_USES_INTRIN __wbinvd(); # elif RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("wbinvd"); # else __asm { wbinvd } # endif } #endif /** * Invalidate internal and (perhaps) external caches without first * flushing dirty cache lines. Use with extreme care. */ #if RT_INLINE_ASM_EXTERNAL DECLASM(void) ASMInvalidateInternalCaches(void); #else DECLINLINE(void) ASMInvalidateInternalCaches(void) { # if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__("invd"); # else __asm { invd } # endif } #endif /** * Memory load/store fence, waits for any pending writes and reads to complete. * Requires the X86_CPUID_FEATURE_EDX_SSE2 CPUID bit set. */ DECLINLINE(void) ASMMemoryFenceSSE2(void) { #if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__ (".byte 0x0f,0xae,0xf0\n\t"); #elif RT_INLINE_ASM_USES_INTRIN _mm_mfence(); #else __asm { _emit 0x0f _emit 0xae _emit 0xf0 } #endif } /** * Memory store fence, waits for any writes to complete. * Requires the X86_CPUID_FEATURE_EDX_SSE CPUID bit set. */ DECLINLINE(void) ASMWriteFenceSSE(void) { #if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__ (".byte 0x0f,0xae,0xf8\n\t"); #elif RT_INLINE_ASM_USES_INTRIN _mm_sfence(); #else __asm { _emit 0x0f _emit 0xae _emit 0xf8 } #endif } /** * Memory load fence, waits for any pending reads to complete. * Requires the X86_CPUID_FEATURE_EDX_SSE2 CPUID bit set. */ DECLINLINE(void) ASMReadFenceSSE2(void) { #if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__ (".byte 0x0f,0xae,0xe8\n\t"); #elif RT_INLINE_ASM_USES_INTRIN _mm_lfence(); #else __asm { _emit 0x0f _emit 0xae _emit 0xe8 } #endif } #if !defined(_MSC_VER) || !defined(RT_ARCH_AMD64) /* * Clear the AC bit in the EFLAGS register. * Requires the X86_CPUID_STEXT_FEATURE_EBX_SMAP CPUID bit set. * Requires to be executed in R0. */ DECLINLINE(void) ASMClearAC(void) { #if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__ (".byte 0x0f,0x01,0xca\n\t"); #else __asm { _emit 0x0f _emit 0x01 _emit 0xca } #endif } /* * Set the AC bit in the EFLAGS register. * Requires the X86_CPUID_STEXT_FEATURE_EBX_SMAP CPUID bit set. * Requires to be executed in R0. */ DECLINLINE(void) ASMSetAC(void) { #if RT_INLINE_ASM_GNU_STYLE __asm__ __volatile__ (".byte 0x0f,0x01,0xcb\n\t"); #else __asm { _emit 0x0f _emit 0x01 _emit 0xcb } #endif } #endif /* !_MSC_VER) || !RT_ARCH_AMD64 */ /** @} */ #endif