/* * Copyright (c) 2007 Hypertriton, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Obtain information about architecture extensions. */ #include #include #include #include #if defined(__APPLE__) || defined(__MACOSX__) # include # if defined(__ppc__) && !defined(MAC_OS_X_VERSION_10_4) # include # endif #elif defined(__AMIGAOS4__) #include #include #include #endif #if defined(HAVE_ALTIVEC) && defined(HAVE_SIGNAL) && defined(HAVE_SETJMP) #include #include static jmp_buf jmpbuf; #endif struct cpuid_regs { Uint32 a; Uint32 b; Uint32 c; Uint32 d; } PACKED_ATTRIBUTE; AG_CPUInfo agCPU; #if defined(__GNUC__) && (defined(__i386__) || defined(i386) || defined(__x86_64__)) static int X86_HaveCPUID(void) { int rv = 0; #if defined(__i386__) || defined(i386) __asm( "pushfl \n" "popl %%eax \n" "movl %%eax, %%ecx \n" "xorl $0x200000,%%eax \n" /* ID Bit */ "pushl %%eax \n" "popfl \n" "pushfl \n" "popl %%eax \n" "xorl %%ecx, %%eax \n" "jz 1f \n" "movl $1,%0 \n" "1: \n" : "=m" (rv) : : "%eax", "%ecx"); #elif defined(__x86_64__) __asm( "pushfq \n" "popq %%rax \n" "movq %%rax, %%rcx \n" "xorl $0x200000,%%eax \n" /* ID Bit */ "pushq %%rax \n" "popfq \n" "pushfq \n" "popq %%rax \n" "xorl %%ecx, %%eax \n" "jz 1f \n" "movl $1,%0 \n" "1: \n" : "=m" (rv) : : "%rax", "%rcx"); #endif return (rv); } static struct cpuid_regs X86_GetCPUID(int fn) { struct cpuid_regs regs; #if defined(__i386__) || defined(i386) __asm( "mov %%ebx, %%esi\n" ".byte 0x0f, 0xa2\n" "xchg %%esi, %%ebx\n" : "=a" (regs.a), "=S" (regs.b), "=c" (regs.c), "=d" (regs.d) : "0" (fn)); #elif defined(__x86_64__) __asm( "mov %%rbx, %%rsi\n" ".byte 0x0f, 0xa2\n" "xchg %%rsi, %%rbx\n" : "=a" (regs.a), "=S" (regs.b), "=c" (regs.c), "=d" (regs.d) : "0" (fn)); #endif return (regs); } #endif /* __GNUC__ && (__i386__ || __x86_64__) */ /* For decoding vendor ID string */ static __inline__ void Conv32(char *d, unsigned int v) { d[0] = v & 0xff; d[1] = (v >> 8) & 0xff; d[2] = (v >> 16) & 0xff; d[3] = (v >> 24) & 0xff; } #if defined(HAVE_SIGNAL) && defined(HAVE_SETJMP) /* SIGILL handler for AltiVec test */ static void IllegalInsn(int sig) { longjmp(jmpbuf, 1); } #endif /* Initialize the CPUInfo structure. */ void AG_GetCPUInfo(AG_CPUInfo *cpu) { #if defined(__i386__) || defined(i386) || defined(__x86_64__) struct cpuid_regs r, rExt; Uint maxFns, maxExt; #endif cpu->vendorID[0] = '\0'; cpu->ext = 0; #if defined(__alpha__) cpu->arch = "alpha"; #elif defined(__x86_64__) || defined(__amd64__) || defined(_M_X64) cpu->arch = "amd64"; #elif defined(__arm__) || defined(__arm32__) cpu->arch = "arm"; #elif defined(__hppa64__) cpu->arch = "hppa64"; #elif defined(__hppa__) cpu->arch = "hppa"; #elif defined(__i386__) || defined(i386) || defined(_M_IX86) cpu->arch = "i386"; #elif defined(__ia64__) || defined(ia64) cpu->arch = "itanium"; #elif defined(__m68010__) cpu->arch = "m68010"; #elif defined(__m68k__) cpu->arch = "m68k"; #elif defined(__mips64__) cpu->arch = "mips64"; #elif defined(__mips__) cpu->arch = "mips"; #elif defined(__ns32k__) cpu->arch = "ns32k"; #elif defined(__ppc__) || defined(__macppc__) || defined(__powerpc__) cpu->arch = "powerpc"; #elif defined(__sh3__) cpu->arch = "sh3"; #elif defined(__sparc64__) cpu->arch = "sparc64"; #elif defined(__sparc__) cpu->arch = "sparc"; #elif defined(__vax__) cpu->arch = "vax"; #else cpu->arch = "unknown"; #endif #if defined(__i386__) || defined(i386) || defined(__x86_64__) if (X86_HaveCPUID() == 0) { return; } cpu->ext |= AG_EXT_CPUID; /* Standard Level 0 */ r = X86_GetCPUID(0x00000000); maxFns = (Uint)r.a; /* Maximum supported standard level */ Conv32(&cpu->vendorID[0], r.b); Conv32(&cpu->vendorID[4], r.d); Conv32(&cpu->vendorID[8], r.c); cpu->vendorID[12] = '\0'; /* Extended Level */ rExt = X86_GetCPUID(0x80000000); maxExt = rExt.a; if (maxExt >= 0x80000001) { rExt = X86_GetCPUID(0x80000001); if (rExt.d & 0x80000000) cpu->ext |= AG_EXT_3DNOW; if (rExt.d & 0x40000000) cpu->ext |= AG_EXT_3DNOW_EXT; if (rExt.d & 0x20000000) cpu->ext |= AG_EXT_LONG_MODE; if (rExt.d & 0x08000000) cpu->ext |= AG_EXT_RDTSCP; if (rExt.d & 0x02000000) cpu->ext |= AG_EXT_FXSR; if (rExt.d & 0x00400000) cpu->ext |= AG_EXT_MMX_EXT; if (rExt.d & 0x00100000) cpu->ext |= AG_EXT_PAGE_NX; if (rExt.c & 0x00000800) cpu->ext |= AG_EXT_SSE5A; if (rExt.c & 0x00000100) cpu->ext |= AG_EXT_3DNOW_PREFETCH; if (rExt.c & 0x00000080) cpu->ext |= AG_EXT_SSE_MISALIGNED; if (rExt.c & 0x00000040) cpu->ext |= AG_EXT_SSE4A; } if (maxFns >= 1) { rExt = X86_GetCPUID(1); if (rExt.d & 0x00000001) cpu->ext |= AG_EXT_ONCHIP_FPU; if (rExt.d & 0x00000010) cpu->ext |= AG_EXT_TSC; if (rExt.d & 0x00008000) cpu->ext |= AG_EXT_CMOV; if (rExt.d & 0x00080000) cpu->ext |= AG_EXT_CLFLUSH; if (rExt.d & 0x00800000) cpu->ext |= AG_EXT_MMX; if (rExt.d & 0x01000000) cpu->ext |= AG_EXT_FXSR; if (rExt.d & 0x02000000) cpu->ext |= AG_EXT_SSE; if (rExt.d & 0x04000000) cpu->ext |= AG_EXT_SSE2; if (rExt.d & 0x10000000) cpu->ext |= AG_EXT_HTT; if (rExt.c & 0x00000001) cpu->ext |= AG_EXT_SSE3; if (rExt.c & 0x00000008) cpu->ext |= AG_EXT_MON; if (rExt.c & 0x00000020) cpu->ext |= AG_EXT_VMX; if (rExt.c & 0x00000200) cpu->ext |= AG_EXT_SSSE3; if (rExt.c & 0x00080000) cpu->ext |= AG_EXT_SSE41; if (rExt.c & 0x00100000) cpu->ext |= AG_EXT_SSE42; } #endif /* i386 or x86_64 */ #if (defined(__APPLE__) || defined(__MACOSX__)) && defined(__ppc__) && \ !defined(MAC_OS_X_VERSION_10_4) { int selectors[2] = { CTL_HW, HW_VECTORUNIT }; int flag = 0; size_t length = sizeof(flag); if (sysctl(selectors, 2, &flag, &length, NULL, 0) == 0) { if (flag != 0) cpu->ext |= AG_EXT_ALTIVEC; } } #elif (defined(__APPLE__) || defined(__MACOSX__)) && defined(__ppc__) && \ defined(MAC_OS_X_VERSION_10_4) { /* XXX sysctl.h issues */ cpu->ext |= AG_EXT_ALTIVEC; } #elif defined(__AMIGAOS4__) { extern struct ExecIFace *IExec; Ulong rv = 0; IExec->GetCPUInfoTags(GCIT_VectorUnit, &rv, TAG_DONE); if (rv == VECTORTYPE_ALTIVEC) cpu->ext |= AG_EXT_ALTIVEC; } #elif defined(HAVE_ALTIVEC) && defined(HAVE_SIGNAL) && defined(HAVE_SETJMP) { volatile int hasAltiVec = 0; void (*fn)(int); fn = signal(SIGILL, IllegalInsn); if (setjmp(jmpbuf) == 0) { __asm volatile ( "mtspr 256, %0 \n" "vand %%v0, %%v0, %%v0\n" : : "r" (-1)); hasAltiVec = 1; } signal(SIGILL, fn); if (hasAltiVec) cpu->ext |= AG_EXT_ALTIVEC; } #endif /* HAVE_ALTIVEC and HAVE_SIGNAL and HAVE_SETJMP */ }