456 lines
9.5 KiB
C
456 lines
9.5 KiB
C
|
/*********************************************************
|
||
|
* Copyright (C) 2003-2015 VMware, Inc. All rights reserved.
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify it
|
||
|
* under the terms of the GNU Lesser General Public License as published
|
||
|
* by the Free Software Foundation version 2.1 and no later version.
|
||
|
*
|
||
|
* This program is distributed in the hope that it will be useful, but
|
||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public
|
||
|
* License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU Lesser General Public License
|
||
|
* along with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
* 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
*
|
||
|
*********************************************************/
|
||
|
|
||
|
/*********************************************************
|
||
|
* The contents of this file are subject to the terms of the Common
|
||
|
* Development and Distribution License (the "License") version 1.0
|
||
|
* and no later version. You may not use this file except in
|
||
|
* compliance with the License.
|
||
|
*
|
||
|
* You can obtain a copy of the License at
|
||
|
* http://www.opensource.org/licenses/cddl1.php
|
||
|
*
|
||
|
* See the License for the specific language governing permissions
|
||
|
* and limitations under the License.
|
||
|
*
|
||
|
*********************************************************/
|
||
|
|
||
|
/*
|
||
|
* x86cpuid_asm.h
|
||
|
*
|
||
|
* CPUID-related assembly functions.
|
||
|
*/
|
||
|
|
||
|
#ifndef _X86CPUID_ASM_H_
|
||
|
#define _X86CPUID_ASM_H_
|
||
|
|
||
|
#include "vm_basic_asm.h"
|
||
|
#include "x86cpuid.h"
|
||
|
|
||
|
|
||
|
/*
|
||
|
* x86-64 windows doesn't support inline asm so we have to use these
|
||
|
* intrinsic functions defined in the compiler. Not all of these are well
|
||
|
* documented. There is an array in the compiler dll (c1.dll) which has
|
||
|
* an array of the names of all the intrinsics minus the leading
|
||
|
* underscore. Searching around in the ntddk.h file can also be helpful.
|
||
|
*
|
||
|
* The declarations for the intrinsic functions were taken from the DDK.
|
||
|
* Our declarations must match the ddk's otherwise the 64-bit c++ compiler
|
||
|
* will complain about second linkage of the intrinsic functions.
|
||
|
* We define the intrinsic using the basic types corresponding to the
|
||
|
* Windows typedefs. This avoids having to include windows header files
|
||
|
* to get to the windows types.
|
||
|
*/
|
||
|
#ifdef _MSC_VER
|
||
|
#ifdef __cplusplus
|
||
|
extern "C" {
|
||
|
#endif
|
||
|
#ifdef VM_X86_64
|
||
|
/*
|
||
|
* intrinsic functions only supported by x86-64 windows as of 2k3sp1
|
||
|
*/
|
||
|
void __cpuid(int regs[4], int eax);
|
||
|
#pragma intrinsic(__cpuid)
|
||
|
#endif /* VM_X86_64 */
|
||
|
|
||
|
#ifdef __cplusplus
|
||
|
}
|
||
|
#endif
|
||
|
#endif /* _MSC_VER */
|
||
|
|
||
|
|
||
|
#ifdef __GNUC__ // {
|
||
|
|
||
|
/*
|
||
|
* Checked against the Intel manual and GCC --hpreg
|
||
|
*
|
||
|
* Need __volatile__ and "memory" since CPUID has a synchronizing effect.
|
||
|
* The CPUID may also change at runtime (APIC flag, etc).
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* %ebx is reserved on i386 PIC. Apple's gcc-5493 (gcc 4.0) compiling
|
||
|
* for x86_64 incorrectly errors out saying %ebx is reserved. This is
|
||
|
* Apple bug 7304232.
|
||
|
*/
|
||
|
#if vm_x86_64 ? (defined __APPLE_CC__ && __APPLE_CC__ == 5493) : defined __PIC__
|
||
|
#if vm_x86_64
|
||
|
/*
|
||
|
* Note that this generates movq %rbx,%rbx; cpuid; xchgq %rbx,%rbx ...
|
||
|
* Unfortunately Apple's assembler does not have .ifnes, and I cannot
|
||
|
* figure out how to do that with .if. If we ever enable this code
|
||
|
* on other 64bit systems, both movq & xchgq should be surrounded by
|
||
|
* .ifnes \"%%rbx\", \"%q1\" & .endif
|
||
|
*/
|
||
|
#define VM_CPUID_BLOCK "movq %%rbx, %q1\n\t" \
|
||
|
"cpuid\n\t" \
|
||
|
"xchgq %%rbx, %q1\n\t"
|
||
|
#define VM_EBX_OUT(reg) "=&r"(reg)
|
||
|
#else
|
||
|
#define VM_CPUID_BLOCK "movl %%ebx, %1\n\t" \
|
||
|
"cpuid\n\t" \
|
||
|
"xchgl %%ebx, %1\n\t"
|
||
|
#define VM_EBX_OUT(reg) "=&rm"(reg)
|
||
|
#endif
|
||
|
#else
|
||
|
#define VM_CPUID_BLOCK "cpuid"
|
||
|
#define VM_EBX_OUT(reg) "=b"(reg)
|
||
|
#endif
|
||
|
|
||
|
static INLINE void
|
||
|
__GET_CPUID(int eax, // IN
|
||
|
CPUIDRegs *regs) // OUT
|
||
|
{
|
||
|
__asm__ __volatile__(
|
||
|
VM_CPUID_BLOCK
|
||
|
: "=a" (regs->eax), VM_EBX_OUT(regs->ebx), "=c" (regs->ecx), "=d" (regs->edx)
|
||
|
: "a" (eax)
|
||
|
: "memory"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
static INLINE void
|
||
|
__GET_CPUID2(int eax, // IN
|
||
|
int ecx, // IN
|
||
|
CPUIDRegs *regs) // OUT
|
||
|
{
|
||
|
__asm__ __volatile__(
|
||
|
VM_CPUID_BLOCK
|
||
|
: "=a" (regs->eax), VM_EBX_OUT(regs->ebx), "=c" (regs->ecx), "=d" (regs->edx)
|
||
|
: "a" (eax), "c" (ecx)
|
||
|
: "memory"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_EAX_FROM_CPUID(int eax) // IN
|
||
|
{
|
||
|
uint32 ebx;
|
||
|
|
||
|
__asm__ __volatile__(
|
||
|
VM_CPUID_BLOCK
|
||
|
: "=a" (eax), VM_EBX_OUT(ebx)
|
||
|
: "a" (eax)
|
||
|
: "memory", "%ecx", "%edx"
|
||
|
);
|
||
|
|
||
|
return eax;
|
||
|
}
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_EBX_FROM_CPUID(int eax) // IN
|
||
|
{
|
||
|
uint32 ebx;
|
||
|
|
||
|
__asm__ __volatile__(
|
||
|
VM_CPUID_BLOCK
|
||
|
: "=a" (eax), VM_EBX_OUT(ebx)
|
||
|
: "a" (eax)
|
||
|
: "memory", "%ecx", "%edx"
|
||
|
);
|
||
|
|
||
|
return ebx;
|
||
|
}
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_ECX_FROM_CPUID(int eax) // IN
|
||
|
{
|
||
|
uint32 ecx;
|
||
|
uint32 ebx;
|
||
|
|
||
|
__asm__ __volatile__(
|
||
|
VM_CPUID_BLOCK
|
||
|
: "=a" (eax), VM_EBX_OUT(ebx), "=c" (ecx)
|
||
|
: "a" (eax)
|
||
|
: "memory", "%edx"
|
||
|
);
|
||
|
|
||
|
return ecx;
|
||
|
}
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_EDX_FROM_CPUID(int eax) // IN
|
||
|
{
|
||
|
uint32 edx;
|
||
|
uint32 ebx;
|
||
|
|
||
|
__asm__ __volatile__(
|
||
|
VM_CPUID_BLOCK
|
||
|
: "=a" (eax), VM_EBX_OUT(ebx), "=d" (edx)
|
||
|
: "a" (eax)
|
||
|
: "memory", "%ecx"
|
||
|
);
|
||
|
|
||
|
return edx;
|
||
|
}
|
||
|
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_EAX_FROM_CPUID4(int ecx) // IN
|
||
|
{
|
||
|
uint32 eax;
|
||
|
uint32 ebx;
|
||
|
|
||
|
__asm__ __volatile__(
|
||
|
VM_CPUID_BLOCK
|
||
|
: "=a" (eax), VM_EBX_OUT(ebx), "=c" (ecx)
|
||
|
: "a" (4), "c" (ecx)
|
||
|
: "memory", "%edx"
|
||
|
);
|
||
|
|
||
|
return eax;
|
||
|
}
|
||
|
|
||
|
#undef VM_CPUID_BLOCK
|
||
|
#undef VM_EBX_OUT
|
||
|
|
||
|
#elif defined(_MSC_VER) // } {
|
||
|
|
||
|
static INLINE void
|
||
|
__GET_CPUID(int input, CPUIDRegs *regs)
|
||
|
{
|
||
|
#ifdef VM_X86_64
|
||
|
__cpuid((int *)regs, input);
|
||
|
#else
|
||
|
__asm push esi
|
||
|
__asm push ebx
|
||
|
__asm push ecx
|
||
|
__asm push edx
|
||
|
|
||
|
__asm mov eax, input
|
||
|
__asm mov esi, regs
|
||
|
__asm _emit 0x0f __asm _emit 0xa2
|
||
|
__asm mov 0x0[esi], eax
|
||
|
__asm mov 0x4[esi], ebx
|
||
|
__asm mov 0x8[esi], ecx
|
||
|
__asm mov 0xC[esi], edx
|
||
|
|
||
|
__asm pop edx
|
||
|
__asm pop ecx
|
||
|
__asm pop ebx
|
||
|
__asm pop esi
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#ifdef VM_X86_64
|
||
|
|
||
|
/*
|
||
|
* No inline assembly in Win64. Implemented in bora/lib/misc in
|
||
|
* cpuidMasm64.asm.
|
||
|
*/
|
||
|
|
||
|
extern void
|
||
|
__GET_CPUID2(int inputEax, int inputEcx, CPUIDRegs *regs);
|
||
|
|
||
|
#else // VM_X86_64
|
||
|
|
||
|
static INLINE void
|
||
|
__GET_CPUID2(int inputEax, int inputEcx, CPUIDRegs *regs)
|
||
|
{
|
||
|
__asm push esi
|
||
|
__asm push ebx
|
||
|
__asm push ecx
|
||
|
__asm push edx
|
||
|
|
||
|
__asm mov eax, inputEax
|
||
|
__asm mov ecx, inputEcx
|
||
|
__asm mov esi, regs
|
||
|
__asm _emit 0x0f __asm _emit 0xa2
|
||
|
__asm mov 0x0[esi], eax
|
||
|
__asm mov 0x4[esi], ebx
|
||
|
__asm mov 0x8[esi], ecx
|
||
|
__asm mov 0xC[esi], edx
|
||
|
|
||
|
__asm pop edx
|
||
|
__asm pop ecx
|
||
|
__asm pop ebx
|
||
|
__asm pop esi
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_EAX_FROM_CPUID(int input)
|
||
|
{
|
||
|
#ifdef VM_X86_64
|
||
|
CPUIDRegs regs;
|
||
|
__cpuid((int *)®s, input);
|
||
|
return regs.eax;
|
||
|
#else
|
||
|
uint32 output;
|
||
|
|
||
|
//NOT_TESTED();
|
||
|
__asm push ebx
|
||
|
__asm push ecx
|
||
|
__asm push edx
|
||
|
|
||
|
__asm mov eax, input
|
||
|
__asm _emit 0x0f __asm _emit 0xa2
|
||
|
__asm mov output, eax
|
||
|
|
||
|
__asm pop edx
|
||
|
__asm pop ecx
|
||
|
__asm pop ebx
|
||
|
|
||
|
return output;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_EBX_FROM_CPUID(int input)
|
||
|
{
|
||
|
#ifdef VM_X86_64
|
||
|
CPUIDRegs regs;
|
||
|
__cpuid((int *)®s, input);
|
||
|
return regs.ebx;
|
||
|
#else
|
||
|
uint32 output;
|
||
|
|
||
|
//NOT_TESTED();
|
||
|
__asm push ebx
|
||
|
__asm push ecx
|
||
|
__asm push edx
|
||
|
|
||
|
__asm mov eax, input
|
||
|
__asm _emit 0x0f __asm _emit 0xa2
|
||
|
__asm mov output, ebx
|
||
|
|
||
|
__asm pop edx
|
||
|
__asm pop ecx
|
||
|
__asm pop ebx
|
||
|
|
||
|
return output;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_ECX_FROM_CPUID(int input)
|
||
|
{
|
||
|
#ifdef VM_X86_64
|
||
|
CPUIDRegs regs;
|
||
|
__cpuid((int *)®s, input);
|
||
|
return regs.ecx;
|
||
|
#else
|
||
|
uint32 output;
|
||
|
|
||
|
//NOT_TESTED();
|
||
|
__asm push ebx
|
||
|
__asm push ecx
|
||
|
__asm push edx
|
||
|
|
||
|
__asm mov eax, input
|
||
|
__asm _emit 0x0f __asm _emit 0xa2
|
||
|
__asm mov output, ecx
|
||
|
|
||
|
__asm pop edx
|
||
|
__asm pop ecx
|
||
|
__asm pop ebx
|
||
|
|
||
|
return output;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_EDX_FROM_CPUID(int input)
|
||
|
{
|
||
|
#ifdef VM_X86_64
|
||
|
CPUIDRegs regs;
|
||
|
__cpuid((int *)®s, input);
|
||
|
return regs.edx;
|
||
|
#else
|
||
|
uint32 output;
|
||
|
|
||
|
//NOT_TESTED();
|
||
|
__asm push ebx
|
||
|
__asm push ecx
|
||
|
__asm push edx
|
||
|
|
||
|
__asm mov eax, input
|
||
|
__asm _emit 0x0f __asm _emit 0xa2
|
||
|
__asm mov output, edx
|
||
|
|
||
|
__asm pop edx
|
||
|
__asm pop ecx
|
||
|
__asm pop ebx
|
||
|
|
||
|
return output;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#ifdef VM_X86_64
|
||
|
|
||
|
/*
|
||
|
* No inline assembly in Win64. Implemented in bora/lib/misc in
|
||
|
* cpuidMasm64.asm.
|
||
|
*/
|
||
|
|
||
|
extern uint32
|
||
|
__GET_EAX_FROM_CPUID4(int inputEcx);
|
||
|
|
||
|
#else // VM_X86_64
|
||
|
|
||
|
static INLINE uint32
|
||
|
__GET_EAX_FROM_CPUID4(int inputEcx)
|
||
|
{
|
||
|
uint32 output;
|
||
|
|
||
|
//NOT_TESTED();
|
||
|
__asm push ebx
|
||
|
__asm push ecx
|
||
|
__asm push edx
|
||
|
|
||
|
__asm mov eax, 4
|
||
|
__asm mov ecx, inputEcx
|
||
|
__asm _emit 0x0f __asm _emit 0xa2
|
||
|
__asm mov output, eax
|
||
|
|
||
|
__asm pop edx
|
||
|
__asm pop ecx
|
||
|
__asm pop ebx
|
||
|
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
#endif // VM_X86_64
|
||
|
|
||
|
#else // }
|
||
|
#error
|
||
|
#endif
|
||
|
|
||
|
#define CPUID_FOR_SIDE_EFFECTS() ((void)__GET_EAX_FROM_CPUID(0))
|
||
|
|
||
|
static INLINE void
|
||
|
__GET_CPUID4(int inputEcx, CPUIDRegs *regs)
|
||
|
{
|
||
|
__GET_CPUID2(4, inputEcx, regs);
|
||
|
}
|
||
|
|
||
|
/* The first parameter is used as an rvalue and then as an lvalue. */
|
||
|
#define GET_CPUID(_ax, _bx, _cx, _dx) { \
|
||
|
CPUIDRegs regs; \
|
||
|
__GET_CPUID(_ax, ®s); \
|
||
|
_ax = regs.eax; \
|
||
|
_bx = regs.ebx; \
|
||
|
_cx = regs.ecx; \
|
||
|
_dx = regs.edx; \
|
||
|
}
|
||
|
|
||
|
|
||
|
#endif
|