diff --git a/CMakeLists.txt b/CMakeLists.txt index 53b734d4e..8b554bd1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,10 +103,9 @@ add_definitions(/DCURL_STATICLIB) link_directories(${CURL_LIBRARIES}) if (WITH_LIBCPUID) - find_package(CPUID REQUIRED) - include_directories(${CPUID_INCLUDE_DIRS}) - link_directories(${CPUID_LIBRARIES}) + add_subdirectory(compat/libcpuid) + include_directories(compat/libcpuid) set(CPUID_LIB cpuid) set(SOURCES_CPUID cpu.c) else() diff --git a/cmake/FindCPUID.cmake b/cmake/FindCPUID.cmake deleted file mode 100644 index 3aa06a504..000000000 --- a/cmake/FindCPUID.cmake +++ /dev/null @@ -1,12 +0,0 @@ -find_path(CPUID_INCLUDE_DIR NAMES libcpuid.h) -find_library(CPUID_LIBRARY NAMES cpuid) - -mark_as_advanced(CPUID_INCLUDE_DIR CPUID_LIBRARY) - -include(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(CPUID DEFAULT_MSG CPUID_LIBRARY CPUID_INCLUDE_DIR) - -if(CURL_FOUND) - set(CPUID_LIBRARIES ${CPUID_LIBRARY}) - set(CPUID_INCLUDE_DIRS ${CPUID_INCLUDE_DIR}) -endif() \ No newline at end of file diff --git a/compat/libcpuid/CMakeLists.txt b/compat/libcpuid/CMakeLists.txt new file mode 100644 index 000000000..5c37492ab --- /dev/null +++ b/compat/libcpuid/CMakeLists.txt @@ -0,0 +1,30 @@ +cmake_minimum_required (VERSION 2.8) +project (cpuid C) + +add_definitions(/DVERSION="0.4.0") + +set(HEADERS + libcpuid.h + libcpuid_types.h + libcpuid_constants.h + libcpuid_internal.h +amd_code_t.h +intel_code_t.h +recog_amd.h +recog_intel.h +asm-bits.h +libcpuid_util.h + ) + +set(SOURCES + cpuid_main.c + asm-bits.c + recog_amd.c + recog_intel.c + libcpuid_util.c + ) + +add_library(cpuid STATIC + ${HEADERS} + ${SOURCES} + ) diff --git a/compat/libcpuid/amd_code_t.h b/compat/libcpuid/amd_code_t.h new file mode 100644 index 000000000..058e8957d --- /dev/null +++ b/compat/libcpuid/amd_code_t.h @@ -0,0 +1,39 @@ +/* + * Copyright 2016 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ + +/* + * This file contains a list of internal codes we use in detection. It is + * of no external use and isn't a complete list of AMD products. + */ + CODE2(OPTERON_800, 1000), + CODE(PHENOM), + CODE(PHENOM2), + CODE(FUSION_C), + CODE(FUSION_E), + CODE(FUSION_EA), + CODE(FUSION_Z), + CODE(FUSION_A), + diff --git a/compat/libcpuid/asm-bits.c b/compat/libcpuid/asm-bits.c new file mode 100644 index 000000000..b8e32284f --- /dev/null +++ b/compat/libcpuid/asm-bits.c @@ -0,0 +1,825 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ + +#include "libcpuid.h" +#include "asm-bits.h" + +int cpuid_exists_by_eflags(void) +{ +#if defined(PLATFORM_X64) + return 1; /* CPUID is always present on the x86_64 */ +#elif defined(PLATFORM_X86) +# if defined(COMPILER_GCC) + int result; + __asm __volatile( + " pushfl\n" + " pop %%eax\n" + " mov %%eax, %%ecx\n" + " xor $0x200000, %%eax\n" + " push %%eax\n" + " popfl\n" + " pushfl\n" + " pop %%eax\n" + " xor %%ecx, %%eax\n" + " mov %%eax, %0\n" + " push %%ecx\n" + " popfl\n" + : "=m"(result) + : :"eax", "ecx", "memory"); + return (result != 0); +# elif defined(COMPILER_MICROSOFT) + int result; + __asm { + pushfd + pop eax + mov ecx, eax + xor eax, 0x200000 + push eax + popfd + pushfd + pop eax + xor eax, ecx + mov result, eax + push ecx + popfd + }; + return (result != 0); +# else + return 0; +# endif /* COMPILER_MICROSOFT */ +#else + return 0; +#endif /* PLATFORM_X86 */ +} + +#ifdef INLINE_ASM_SUPPORTED +/* + * with MSVC/AMD64, the exec_cpuid() and cpu_rdtsc() functions + * are implemented in separate .asm files. Otherwise, use inline assembly + */ +void exec_cpuid(uint32_t *regs) +{ +#ifdef COMPILER_GCC +# ifdef PLATFORM_X64 + __asm __volatile( + " mov %0, %%rdi\n" + + " push %%rbx\n" + " push %%rcx\n" + " push %%rdx\n" + + " mov (%%rdi), %%eax\n" + " mov 4(%%rdi), %%ebx\n" + " mov 8(%%rdi), %%ecx\n" + " mov 12(%%rdi), %%edx\n" + + " cpuid\n" + + " movl %%eax, (%%rdi)\n" + " movl %%ebx, 4(%%rdi)\n" + " movl %%ecx, 8(%%rdi)\n" + " movl %%edx, 12(%%rdi)\n" + " pop %%rdx\n" + " pop %%rcx\n" + " pop %%rbx\n" + : + :"m"(regs) + :"memory", "eax", "rdi" + ); +# else + __asm __volatile( + " mov %0, %%edi\n" + + " push %%ebx\n" + " push %%ecx\n" + " push %%edx\n" + + " mov (%%edi), %%eax\n" + " mov 4(%%edi), %%ebx\n" + " mov 8(%%edi), %%ecx\n" + " mov 12(%%edi), %%edx\n" + + " cpuid\n" + + " mov %%eax, (%%edi)\n" + " mov %%ebx, 4(%%edi)\n" + " mov %%ecx, 8(%%edi)\n" + " mov %%edx, 12(%%edi)\n" + " pop %%edx\n" + " pop %%ecx\n" + " pop %%ebx\n" + : + :"m"(regs) + :"memory", "eax", "edi" + ); +# endif /* COMPILER_GCC */ +#else +# ifdef COMPILER_MICROSOFT + __asm { + push ebx + push ecx + push edx + push edi + mov edi, regs + + mov eax, [edi] + mov ebx, [edi+4] + mov ecx, [edi+8] + mov edx, [edi+12] + + cpuid + + mov [edi], eax + mov [edi+4], ebx + mov [edi+8], ecx + mov [edi+12], edx + + pop edi + pop edx + pop ecx + pop ebx + } +# else +# error "Unsupported compiler" +# endif /* COMPILER_MICROSOFT */ +#endif +} +#endif /* INLINE_ASSEMBLY_SUPPORTED */ + +#ifdef INLINE_ASM_SUPPORTED +void cpu_rdtsc(uint64_t* result) +{ + uint32_t low_part, hi_part; +#ifdef COMPILER_GCC + __asm __volatile ( + " rdtsc\n" + " mov %%eax, %0\n" + " mov %%edx, %1\n" + :"=m"(low_part), "=m"(hi_part)::"memory", "eax", "edx" + ); +#else +# ifdef COMPILER_MICROSOFT + __asm { + rdtsc + mov low_part, eax + mov hi_part, edx + }; +# else +# error "Unsupported compiler" +# endif /* COMPILER_MICROSOFT */ +#endif /* COMPILER_GCC */ + *result = (uint64_t)low_part + (((uint64_t) hi_part) << 32); +} +#endif /* INLINE_ASM_SUPPORTED */ + +#ifdef INLINE_ASM_SUPPORTED +void busy_sse_loop(int cycles) +{ +#ifdef COMPILER_GCC +#ifndef __APPLE__ +# define XALIGN ".balign 16\n" +#else +# define XALIGN ".align 4\n" +#endif + __asm __volatile ( + " xorps %%xmm0, %%xmm0\n" + " xorps %%xmm1, %%xmm1\n" + " xorps %%xmm2, %%xmm2\n" + " xorps %%xmm3, %%xmm3\n" + " xorps %%xmm4, %%xmm4\n" + " xorps %%xmm5, %%xmm5\n" + " xorps %%xmm6, %%xmm6\n" + " xorps %%xmm7, %%xmm7\n" + XALIGN + /* ".bsLoop:\n" */ + "1:\n" + // 0: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 1: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 2: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 3: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 4: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 5: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 6: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 7: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 8: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + // 9: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //10: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //11: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //12: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //13: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //14: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //15: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //16: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //17: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //18: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //19: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //20: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //21: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //22: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //23: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //24: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //25: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //26: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //27: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //28: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //29: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //30: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + //31: + " addps %%xmm1, %%xmm0\n" + " addps %%xmm2, %%xmm1\n" + " addps %%xmm3, %%xmm2\n" + " addps %%xmm4, %%xmm3\n" + " addps %%xmm5, %%xmm4\n" + " addps %%xmm6, %%xmm5\n" + " addps %%xmm7, %%xmm6\n" + " addps %%xmm0, %%xmm7\n" + + " dec %%eax\n" + /* "jnz .bsLoop\n" */ + " jnz 1b\n" + ::"a"(cycles) + ); +#else +# ifdef COMPILER_MICROSOFT + __asm { + mov eax, cycles + xorps xmm0, xmm0 + xorps xmm1, xmm1 + xorps xmm2, xmm2 + xorps xmm3, xmm3 + xorps xmm4, xmm4 + xorps xmm5, xmm5 + xorps xmm6, xmm6 + xorps xmm7, xmm7 + //-- + align 16 +bsLoop: + // 0: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 1: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 2: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 3: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 4: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 5: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 6: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 7: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 8: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 9: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 10: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 11: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 12: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 13: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 14: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 15: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 16: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 17: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 18: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 19: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 20: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 21: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 22: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 23: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 24: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 25: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 26: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 27: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 28: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 29: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 30: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + // 31: + addps xmm0, xmm1 + addps xmm1, xmm2 + addps xmm2, xmm3 + addps xmm3, xmm4 + addps xmm4, xmm5 + addps xmm5, xmm6 + addps xmm6, xmm7 + addps xmm7, xmm0 + //---------------------- + dec eax + jnz bsLoop + } +# else +# error "Unsupported compiler" +# endif /* COMPILER_MICROSOFT */ +#endif /* COMPILER_GCC */ +} +#endif /* INLINE_ASSEMBLY_SUPPORTED */ diff --git a/compat/libcpuid/asm-bits.h b/compat/libcpuid/asm-bits.h new file mode 100644 index 000000000..3a03e11ce --- /dev/null +++ b/compat/libcpuid/asm-bits.h @@ -0,0 +1,53 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +#ifndef __ASM_BITS_H__ +#define __ASM_BITS_H__ +#include "libcpuid.h" + +/* Determine Compiler: */ +#if defined(_MSC_VER) +# define COMPILER_MICROSOFT +#elif defined(__GNUC__) +# define COMPILER_GCC +#endif + +/* Determine Platform */ +#if defined(__x86_64__) || defined(_M_AMD64) +# define PLATFORM_X64 +#elif defined(__i386__) || defined(_M_IX86) +# define PLATFORM_X86 +#endif + +/* Under Windows/AMD64 with MSVC, inline assembly isn't supported */ +#if (defined(COMPILER_GCC) && defined(PLATFORM_X64)) || defined(PLATFORM_X86) +# define INLINE_ASM_SUPPORTED +#endif + +int cpuid_exists_by_eflags(void); +void exec_cpuid(uint32_t *regs); +void busy_sse_loop(int cycles); + +#endif /* __ASM_BITS_H__ */ diff --git a/compat/libcpuid/cpuid_main.c b/compat/libcpuid/cpuid_main.c new file mode 100644 index 000000000..64c4198b4 --- /dev/null +++ b/compat/libcpuid/cpuid_main.c @@ -0,0 +1,438 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +#include "libcpuid.h" +#include "libcpuid_internal.h" +#include "recog_intel.h" +#include "recog_amd.h" +#include "asm-bits.h" +#include "libcpuid_util.h" +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include + +/* Implementation: */ + +static int _libcpiud_errno = ERR_OK; + +int set_error(cpu_error_t err) +{ + _libcpiud_errno = (int) err; + return (int) err; +} + +static void raw_data_t_constructor(struct cpu_raw_data_t* raw) +{ + memset(raw, 0, sizeof(struct cpu_raw_data_t)); +} + +static void cpu_id_t_constructor(struct cpu_id_t* id) +{ + memset(id, 0, sizeof(struct cpu_id_t)); + id->l1_data_cache = id->l1_instruction_cache = id->l2_cache = id->l3_cache = id->l4_cache = -1; + id->l1_assoc = id->l2_assoc = id->l3_assoc = id->l4_assoc = -1; + id->l1_cacheline = id->l2_cacheline = id->l3_cacheline = id->l4_cacheline = -1; + id->sse_size = -1; +} + +static int parse_token(const char* expected_token, const char *token, + const char *value, uint32_t array[][4], int limit, int *recognized) +{ + char format[32]; + int veax, vebx, vecx, vedx; + int index; + + if (*recognized) return 1; /* already recognized */ + if (strncmp(token, expected_token, strlen(expected_token))) return 1; /* not what we search for */ + sprintf(format, "%s[%%d]", expected_token); + *recognized = 1; + if (1 == sscanf(token, format, &index) && index >=0 && index < limit) { + if (4 == sscanf(value, "%x%x%x%x", &veax, &vebx, &vecx, &vedx)) { + array[index][0] = veax; + array[index][1] = vebx; + array[index][2] = vecx; + array[index][3] = vedx; + return 1; + } + } + return 0; +} + +/* get_total_cpus() system specific code: uses OS routines to determine total number of CPUs */ +#ifdef __APPLE__ +#include +#include +#include +#include +static int get_total_cpus(void) +{ + kern_return_t kr; + host_basic_info_data_t basic_info; + host_info_t info = (host_info_t)&basic_info; + host_flavor_t flavor = HOST_BASIC_INFO; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + kr = host_info(mach_host_self(), flavor, info, &count); + if (kr != KERN_SUCCESS) return 1; + return basic_info.avail_cpus; +} +#define GET_TOTAL_CPUS_DEFINED +#endif + +#ifdef _WIN32 +#include +static int get_total_cpus(void) +{ + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + return system_info.dwNumberOfProcessors; +} +#define GET_TOTAL_CPUS_DEFINED +#endif + +#if defined linux || defined __linux__ || defined __sun +#include +#include + +static int get_total_cpus(void) +{ + return sysconf(_SC_NPROCESSORS_ONLN); +} +#define GET_TOTAL_CPUS_DEFINED +#endif + +#if defined __FreeBSD__ || defined __OpenBSD__ || defined __NetBSD__ || defined __bsdi__ || defined __QNX__ +#include +#include + +static int get_total_cpus(void) +{ + int mib[2] = { CTL_HW, HW_NCPU }; + int ncpus; + size_t len = sizeof(ncpus); + if (sysctl(mib, 2, &ncpus, &len, (void *) 0, 0) != 0) return 1; + return ncpus; +} +#define GET_TOTAL_CPUS_DEFINED +#endif + +#ifndef GET_TOTAL_CPUS_DEFINED +static int get_total_cpus(void) +{ + static int warning_printed = 0; + if (!warning_printed) { + warning_printed = 1; + warnf("Your system is not supported by libcpuid -- don't know how to detect the\n"); + warnf("total number of CPUs on your system. It will be reported as 1.\n"); + printf("Please use cpu_id_t.logical_cpus field instead.\n"); + } + return 1; +} +#endif /* GET_TOTAL_CPUS_DEFINED */ + + +static void load_features_common(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + const struct feature_map_t matchtable_edx1[] = { + { 0, CPU_FEATURE_FPU }, + { 1, CPU_FEATURE_VME }, + { 2, CPU_FEATURE_DE }, + { 3, CPU_FEATURE_PSE }, + { 4, CPU_FEATURE_TSC }, + { 5, CPU_FEATURE_MSR }, + { 6, CPU_FEATURE_PAE }, + { 7, CPU_FEATURE_MCE }, + { 8, CPU_FEATURE_CX8 }, + { 9, CPU_FEATURE_APIC }, + { 11, CPU_FEATURE_SEP }, + { 12, CPU_FEATURE_MTRR }, + { 13, CPU_FEATURE_PGE }, + { 14, CPU_FEATURE_MCA }, + { 15, CPU_FEATURE_CMOV }, + { 16, CPU_FEATURE_PAT }, + { 17, CPU_FEATURE_PSE36 }, + { 19, CPU_FEATURE_CLFLUSH }, + { 23, CPU_FEATURE_MMX }, + { 24, CPU_FEATURE_FXSR }, + { 25, CPU_FEATURE_SSE }, + { 26, CPU_FEATURE_SSE2 }, + { 28, CPU_FEATURE_HT }, + }; + const struct feature_map_t matchtable_ecx1[] = { + { 0, CPU_FEATURE_PNI }, + { 1, CPU_FEATURE_PCLMUL }, + { 3, CPU_FEATURE_MONITOR }, + { 9, CPU_FEATURE_SSSE3 }, + { 12, CPU_FEATURE_FMA3 }, + { 13, CPU_FEATURE_CX16 }, + { 19, CPU_FEATURE_SSE4_1 }, + { 20, CPU_FEATURE_SSE4_2 }, + { 22, CPU_FEATURE_MOVBE }, + { 23, CPU_FEATURE_POPCNT }, + { 25, CPU_FEATURE_AES }, + { 26, CPU_FEATURE_XSAVE }, + { 27, CPU_FEATURE_OSXSAVE }, + { 28, CPU_FEATURE_AVX }, + { 29, CPU_FEATURE_F16C }, + { 30, CPU_FEATURE_RDRAND }, + }; + const struct feature_map_t matchtable_ebx7[] = { + { 3, CPU_FEATURE_BMI1 }, + { 5, CPU_FEATURE_AVX2 }, + { 8, CPU_FEATURE_BMI2 }, + }; + const struct feature_map_t matchtable_edx81[] = { + { 11, CPU_FEATURE_SYSCALL }, + { 27, CPU_FEATURE_RDTSCP }, + { 29, CPU_FEATURE_LM }, + }; + const struct feature_map_t matchtable_ecx81[] = { + { 0, CPU_FEATURE_LAHF_LM }, + }; + const struct feature_map_t matchtable_edx87[] = { + { 8, CPU_FEATURE_CONSTANT_TSC }, + }; + if (raw->basic_cpuid[0][0] >= 1) { + match_features(matchtable_edx1, COUNT_OF(matchtable_edx1), raw->basic_cpuid[1][3], data); + match_features(matchtable_ecx1, COUNT_OF(matchtable_ecx1), raw->basic_cpuid[1][2], data); + } + if (raw->basic_cpuid[0][0] >= 7) { + match_features(matchtable_ebx7, COUNT_OF(matchtable_ebx7), raw->basic_cpuid[7][1], data); + } + if (raw->ext_cpuid[0][0] >= 0x80000001) { + match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data); + match_features(matchtable_ecx81, COUNT_OF(matchtable_ecx81), raw->ext_cpuid[1][2], data); + } + if (raw->ext_cpuid[0][0] >= 0x80000007) { + match_features(matchtable_edx87, COUNT_OF(matchtable_edx87), raw->ext_cpuid[7][3], data); + } + if (data->flags[CPU_FEATURE_SSE]) { + /* apply guesswork to check if the SSE unit width is 128 bit */ + switch (data->vendor) { + case VENDOR_AMD: + data->sse_size = (data->ext_family >= 16 && data->ext_family != 17) ? 128 : 64; + break; + case VENDOR_INTEL: + data->sse_size = (data->family == 6 && data->ext_model >= 15) ? 128 : 64; + break; + default: + break; + } + /* leave the CPU_FEATURE_128BIT_SSE_AUTH 0; the advanced per-vendor detection routines + * will set it accordingly if they detect the needed bit */ + } +} + +static cpu_vendor_t cpuid_vendor_identify(const uint32_t *raw_vendor, char *vendor_str) +{ + int i; + cpu_vendor_t vendor = VENDOR_UNKNOWN; + const struct { cpu_vendor_t vendor; char match[16]; } + matchtable[NUM_CPU_VENDORS] = { + /* source: http://www.sandpile.org/ia32/cpuid.htm */ + { VENDOR_INTEL , "GenuineIntel" }, + { VENDOR_AMD , "AuthenticAMD" }, + { VENDOR_CYRIX , "CyrixInstead" }, + { VENDOR_NEXGEN , "NexGenDriven" }, + { VENDOR_TRANSMETA , "GenuineTMx86" }, + { VENDOR_UMC , "UMC UMC UMC " }, + { VENDOR_CENTAUR , "CentaurHauls" }, + { VENDOR_RISE , "RiseRiseRise" }, + { VENDOR_SIS , "SiS SiS SiS " }, + { VENDOR_NSC , "Geode by NSC" }, + }; + + memcpy(vendor_str + 0, &raw_vendor[1], 4); + memcpy(vendor_str + 4, &raw_vendor[3], 4); + memcpy(vendor_str + 8, &raw_vendor[2], 4); + vendor_str[12] = 0; + + /* Determine vendor: */ + for (i = 0; i < NUM_CPU_VENDORS; i++) + if (!strcmp(vendor_str, matchtable[i].match)) { + vendor = matchtable[i].vendor; + break; + } + return vendor; +} + +static int cpuid_basic_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + int i, j, basic, xmodel, xfamily, ext; + char brandstr[64] = {0}; + data->vendor = cpuid_vendor_identify(raw->basic_cpuid[0], data->vendor_str); + + if (data->vendor == VENDOR_UNKNOWN) + return set_error(ERR_CPU_UNKN); + basic = raw->basic_cpuid[0][0]; + if (basic >= 1) { + data->family = (raw->basic_cpuid[1][0] >> 8) & 0xf; + data->model = (raw->basic_cpuid[1][0] >> 4) & 0xf; + data->stepping = raw->basic_cpuid[1][0] & 0xf; + xmodel = (raw->basic_cpuid[1][0] >> 16) & 0xf; + xfamily = (raw->basic_cpuid[1][0] >> 20) & 0xff; + if (data->vendor == VENDOR_AMD && data->family < 0xf) + data->ext_family = data->family; + else + data->ext_family = data->family + xfamily; + data->ext_model = data->model + (xmodel << 4); + } + ext = raw->ext_cpuid[0][0] - 0x8000000; + + /* obtain the brand string, if present: */ + if (ext >= 4) { + for (i = 0; i < 3; i++) + for (j = 0; j < 4; j++) + memcpy(brandstr + i * 16 + j * 4, + &raw->ext_cpuid[2 + i][j], 4); + brandstr[48] = 0; + i = 0; + while (brandstr[i] == ' ') i++; + strncpy(data->brand_str, brandstr + i, sizeof(data->brand_str)); + data->brand_str[48] = 0; + } + load_features_common(raw, data); + data->total_logical_cpus = get_total_cpus(); + return set_error(ERR_OK); +} + +static void make_list_from_string(const char* csv, struct cpu_list_t* list) +{ + int i, n, l, last; + l = (int) strlen(csv); + n = 0; + for (i = 0; i < l; i++) if (csv[i] == ',') n++; + n++; + list->num_entries = n; + list->names = (char**) malloc(sizeof(char*) * n); + last = -1; + n = 0; + for (i = 0; i <= l; i++) if (i == l || csv[i] == ',') { + list->names[n] = (char*) malloc(i - last); + memcpy(list->names[n], &csv[last + 1], i - last - 1); + list->names[n][i - last - 1] = '\0'; + n++; + last = i; + } +} + + +/* Interface: */ + +int cpuid_get_total_cpus(void) +{ + return get_total_cpus(); +} + +int cpuid_present(void) +{ + return cpuid_exists_by_eflags(); +} + +void cpu_exec_cpuid(uint32_t eax, uint32_t* regs) +{ + regs[0] = eax; + regs[1] = regs[2] = regs[3] = 0; + exec_cpuid(regs); +} + +void cpu_exec_cpuid_ext(uint32_t* regs) +{ + exec_cpuid(regs); +} + +int cpuid_get_raw_data(struct cpu_raw_data_t* data) +{ + unsigned i; + if (!cpuid_present()) + return set_error(ERR_NO_CPUID); + for (i = 0; i < 32; i++) + cpu_exec_cpuid(i, data->basic_cpuid[i]); + for (i = 0; i < 32; i++) + cpu_exec_cpuid(0x80000000 + i, data->ext_cpuid[i]); + for (i = 0; i < MAX_INTELFN4_LEVEL; i++) { + memset(data->intel_fn4[i], 0, sizeof(data->intel_fn4[i])); + data->intel_fn4[i][0] = 4; + data->intel_fn4[i][2] = i; + cpu_exec_cpuid_ext(data->intel_fn4[i]); + } + for (i = 0; i < MAX_INTELFN11_LEVEL; i++) { + memset(data->intel_fn11[i], 0, sizeof(data->intel_fn11[i])); + data->intel_fn11[i][0] = 11; + data->intel_fn11[i][2] = i; + cpu_exec_cpuid_ext(data->intel_fn11[i]); + } + for (i = 0; i < MAX_INTELFN12H_LEVEL; i++) { + memset(data->intel_fn12h[i], 0, sizeof(data->intel_fn12h[i])); + data->intel_fn12h[i][0] = 0x12; + data->intel_fn12h[i][2] = i; + cpu_exec_cpuid_ext(data->intel_fn12h[i]); + } + for (i = 0; i < MAX_INTELFN14H_LEVEL; i++) { + memset(data->intel_fn14h[i], 0, sizeof(data->intel_fn14h[i])); + data->intel_fn14h[i][0] = 0x14; + data->intel_fn14h[i][2] = i; + cpu_exec_cpuid_ext(data->intel_fn14h[i]); + } + return set_error(ERR_OK); +} + +int cpu_ident_internal(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) +{ + int r; + struct cpu_raw_data_t myraw; + if (!raw) { + if ((r = cpuid_get_raw_data(&myraw)) < 0) + return set_error(r); + raw = &myraw; + } + cpu_id_t_constructor(data); + if ((r = cpuid_basic_identify(raw, data)) < 0) + return set_error(r); + switch (data->vendor) { + case VENDOR_INTEL: + r = cpuid_identify_intel(raw, data, internal); + break; + case VENDOR_AMD: + r = cpuid_identify_amd(raw, data, internal); + break; + default: + break; + } + return set_error(r); +} + +int cpu_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + struct internal_id_info_t throwaway; + return cpu_ident_internal(raw, data, &throwaway); +} + +const char* cpuid_lib_version(void) +{ + return VERSION; +} diff --git a/compat/libcpuid/intel_code_t.h b/compat/libcpuid/intel_code_t.h new file mode 100644 index 000000000..718ca337b --- /dev/null +++ b/compat/libcpuid/intel_code_t.h @@ -0,0 +1,58 @@ +/* + * Copyright 2016 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ + +/* + * This file contains a list of internal codes we use in detection. It is + * of no external use and isn't a complete list of intel products. + */ + CODE2(PENTIUM, 2000), + + CODE(IRWIN), + CODE(POTOMAC), + CODE(GAINESTOWN), + CODE(WESTMERE), + + CODE(PENTIUM_M), + CODE(NOT_CELERON), + + CODE(CORE_SOLO), + CODE(MOBILE_CORE_SOLO), + CODE(CORE_DUO), + CODE(MOBILE_CORE_DUO), + + CODE(WOLFDALE), + CODE(MEROM), + CODE(PENRYN), + CODE(QUAD_CORE), + CODE(DUAL_CORE_HT), + CODE(QUAD_CORE_HT), + CODE(MORE_THAN_QUADCORE), + CODE(PENTIUM_D), + + CODE(SILVERTHORNE), + CODE(DIAMONDVILLE), + CODE(PINEVIEW), + CODE(CEDARVIEW), diff --git a/compat/libcpuid/libcpuid.h b/compat/libcpuid/libcpuid.h new file mode 100644 index 000000000..f99c0fe55 --- /dev/null +++ b/compat/libcpuid/libcpuid.h @@ -0,0 +1,1150 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +#ifndef __LIBCPUID_H__ +#define __LIBCPUID_H__ +/** + * \file libcpuid.h + * \author Veselin Georgiev + * \date Oct 2008 + * \version 0.4.0 + * + * Version history: + * + * * 0.1.0 (2008-10-15): initial adaptation from wxfractgui sources + * * 0.1.1 (2009-07-06): Added intel_fn11 fields to cpu_raw_data_t to handle + * new processor topology enumeration required on Core i7 + * * 0.1.2 (2009-09-26): Added support for MSR reading through self-extracting + * kernel driver on Win32. + * * 0.1.3 (2010-04-20): Added support for greater more accurate CPU clock + * measurements with cpu_clock_by_ic() + * * 0.2.0 (2011-10-11): Support for AMD Bulldozer CPUs, 128-bit SSE unit size + * checking. A backwards-incompatible change, since the + * sizeof cpu_id_t is now different. + * * 0.2.1 (2012-05-26): Support for Ivy Bridge, and detecting the presence of + * the RdRand instruction. + * * 0.2.2 (2015-11-04): Support for newer processors up to Haswell and Vishera. + * Fix clock detection in cpu_clock_by_ic() for Bulldozer. + * More entries supported in cpu_msrinfo(). + * *BSD and Solaris support (unofficial). + * * 0.3.0 (2016-07-09): Support for Skylake; MSR ops in FreeBSD; INFO_VOLTAGE + * for AMD CPUs. Level 4 cache support for Crystalwell + * (a backwards-incompatible change since the sizeof + * cpu_raw_data_t is now different). + * * 0.4.0 (2016-09-30): Better detection of AMD clock multiplier with msrinfo. + * Support for Intel SGX detection + * (a backwards-incompatible change since the sizeof + * cpu_raw_data_t and cpu_id_t is now different). + */ + +/** @mainpage A simple libcpuid introduction + * + * LibCPUID provides CPU identification and access to the CPUID and RDTSC + * instructions on the x86. + *

+ * To execute CPUID, use \ref cpu_exec_cpuid
+ * To execute RDTSC, use \ref cpu_rdtsc
+ * To fetch the CPUID info needed for CPU identification, use + * \ref cpuid_get_raw_data
+ * To make sense of that data (decode, extract features), use \ref cpu_identify
+ * To detect the CPU speed, use either \ref cpu_clock, \ref cpu_clock_by_os, + * \ref cpu_tsc_mark + \ref cpu_tsc_unmark + \ref cpu_clock_by_mark, + * \ref cpu_clock_measure or \ref cpu_clock_by_ic. + * Read carefully for pros/cons of each method.
+ * + * To read MSRs, use \ref cpu_msr_driver_open to get a handle, and then + * \ref cpu_rdmsr for querying abilities. Some MSR decoding is available on recent + * CPUs, and can be queried through \ref cpu_msrinfo; the various types of queries + * are described in \ref cpu_msrinfo_request_t. + *

+ */ + +/** @defgroup libcpuid LibCPUID + @{ */ + +/* Include some integer type specifications: */ +#include "libcpuid_types.h" + +/* Some limits and other constants */ +#include "libcpuid_constants.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief CPU vendor, as guessed from the Vendor String. + */ +typedef enum { + VENDOR_INTEL = 0, /*!< Intel CPU */ + VENDOR_AMD, /*!< AMD CPU */ + VENDOR_CYRIX, /*!< Cyrix CPU */ + VENDOR_NEXGEN, /*!< NexGen CPU */ + VENDOR_TRANSMETA, /*!< Transmeta CPU */ + VENDOR_UMC, /*!< x86 CPU by UMC */ + VENDOR_CENTAUR, /*!< x86 CPU by IDT */ + VENDOR_RISE, /*!< x86 CPU by Rise Technology */ + VENDOR_SIS, /*!< x86 CPU by SiS */ + VENDOR_NSC, /*!< x86 CPU by National Semiconductor */ + + NUM_CPU_VENDORS, /*!< Valid CPU vendor ids: 0..NUM_CPU_VENDORS - 1 */ + VENDOR_UNKNOWN = -1, +} cpu_vendor_t; +#define NUM_CPU_VENDORS NUM_CPU_VENDORS + +/** + * @brief Contains just the raw CPUID data. + * + * This contains only the most basic CPU data, required to do identification + * and feature recognition. Every processor should be identifiable using this + * data only. + */ +struct cpu_raw_data_t { + /** contains results of CPUID for eax = 0, 1, ...*/ + uint32_t basic_cpuid[MAX_CPUID_LEVEL][4]; + + /** contains results of CPUID for eax = 0x80000000, 0x80000001, ...*/ + uint32_t ext_cpuid[MAX_EXT_CPUID_LEVEL][4]; + + /** when the CPU is intel and it supports deterministic cache + information: this contains the results of CPUID for eax = 4 + and ecx = 0, 1, ... */ + uint32_t intel_fn4[MAX_INTELFN4_LEVEL][4]; + + /** when the CPU is intel and it supports leaf 0Bh (Extended Topology + enumeration leaf), this stores the result of CPUID with + eax = 11 and ecx = 0, 1, 2... */ + uint32_t intel_fn11[MAX_INTELFN11_LEVEL][4]; + + /** when the CPU is intel and supports leaf 12h (SGX enumeration leaf), + * this stores the result of CPUID with eax = 0x12 and + * ecx = 0, 1, 2... */ + uint32_t intel_fn12h[MAX_INTELFN12H_LEVEL][4]; + + /** when the CPU is intel and supports leaf 14h (Intel Processor Trace + * capabilities leaf). + * this stores the result of CPUID with eax = 0x12 and + * ecx = 0, 1, 2... */ + uint32_t intel_fn14h[MAX_INTELFN14H_LEVEL][4]; +}; + +/** + * @brief This contains information about SGX features of the processor + * Example usage: + * @code + * ... + * struct cpu_raw_data_t raw; + * struct cpu_id_t id; + * + * if (cpuid_get_raw_data(&raw) == 0 && cpu_identify(&raw, &id) == 0 && id.sgx.present) { + * printf("SGX is present.\n"); + * printf("SGX1 instructions: %s.\n", id.sgx.flags[INTEL_SGX1] ? "present" : "absent"); + * printf("SGX2 instructions: %s.\n", id.sgx.flags[INTEL_SGX2] ? "present" : "absent"); + * printf("Max 32-bit enclave size: 2^%d bytes.\n", id.sgx.max_enclave_32bit); + * printf("Max 64-bit enclave size: 2^%d bytes.\n", id.sgx.max_enclave_64bit); + * for (int i = 0; i < id.sgx.num_epc_sections; i++) { + * struct cpu_epc_t epc = cpuid_get_epc(i, NULL); + * printf("EPC section #%d: address = %x, size = %d bytes.\n", epc.address, epc.size); + * } + * } else { + * printf("SGX is not present.\n"); + * } + * @endcode + */ +struct cpu_sgx_t { + /** Whether SGX is present (boolean) */ + uint32_t present; + + /** Max enclave size in 32-bit mode. This is a power-of-two value: + * if it is "31", then the max enclave size is 2^31 bytes (2 GiB). + */ + uint8_t max_enclave_32bit; + + /** Max enclave size in 64-bit mode. This is a power-of-two value: + * if it is "36", then the max enclave size is 2^36 bytes (64 GiB). + */ + uint8_t max_enclave_64bit; + + /** + * contains SGX feature flags. See the \ref cpu_sgx_feature_t + * "INTEL_SGX*" macros below. + */ + uint8_t flags[SGX_FLAGS_MAX]; + + /** number of Enclave Page Cache (EPC) sections. Info for each + * section is available through the \ref cpuid_get_epc() function + */ + int num_epc_sections; + + /** bit vector of the supported extended features that can be written + * to the MISC region of the SSA (Save State Area) + */ + uint32_t misc_select; + + /** a bit vector of the attributes that can be set to SECS.ATTRIBUTES + * via ECREATE. Corresponds to bits 0-63 (incl.) of SECS.ATTRIBUTES. + */ + uint64_t secs_attributes; + + /** a bit vector of the bits that can be set in the XSAVE feature + * request mask; Corresponds to bits 64-127 of SECS.ATTRIBUTES. + */ + uint64_t secs_xfrm; +}; + +/** + * @brief This contains the recognized CPU features/info + */ +struct cpu_id_t { + /** contains the CPU vendor string, e.g. "GenuineIntel" */ + char vendor_str[VENDOR_STR_MAX]; + + /** contains the brand string, e.g. "Intel(R) Xeon(TM) CPU 2.40GHz" */ + char brand_str[BRAND_STR_MAX]; + + /** contains the recognized CPU vendor */ + cpu_vendor_t vendor; + + /** + * contain CPU flags. Used to test for features. See + * the \ref cpu_feature_t "CPU_FEATURE_*" macros below. + * @see Features + */ + uint8_t flags[CPU_FLAGS_MAX]; + + /** CPU family */ + int32_t family; + + /** CPU model */ + int32_t model; + + /** CPU stepping */ + int32_t stepping; + + /** CPU extended family */ + int32_t ext_family; + + /** CPU extended model */ + int32_t ext_model; + + /** Number of CPU cores on the current processor */ + int32_t num_cores; + + /** + * Number of logical processors on the current processor. + * Could be more than the number of physical cores, + * e.g. when the processor has HyperThreading. + */ + int32_t num_logical_cpus; + + /** + * The total number of logical processors. + * The same value is availabe through \ref cpuid_get_total_cpus. + * + * This is num_logical_cpus * {total physical processors in the system} + * (but only on a real system, under a VM this number may be lower). + * + * If you're writing a multithreaded program and you want to run it on + * all CPUs, this is the number of threads you need. + * + * @note in a VM, this will exactly match the number of CPUs set in + * the VM's configuration. + * + */ + int32_t total_logical_cpus; + + /** + * L1 data cache size in KB. Could be zero, if the CPU lacks cache. + * If the size cannot be determined, it will be -1. + */ + int32_t l1_data_cache; + + /** + * L1 instruction cache size in KB. Could be zero, if the CPU lacks + * cache. If the size cannot be determined, it will be -1. + * @note On some Intel CPUs, whose instruction cache is in fact + * a trace cache, the size will be expressed in K uOps. + */ + int32_t l1_instruction_cache; + + /** + * L2 cache size in KB. Could be zero, if the CPU lacks L2 cache. + * If the size of the cache could not be determined, it will be -1 + */ + int32_t l2_cache; + + /** L3 cache size in KB. Zero on most systems */ + int32_t l3_cache; + + /** L4 cache size in KB. Zero on most systems */ + int32_t l4_cache; + + /** Cache associativity for the L1 data cache. -1 if undetermined */ + int32_t l1_assoc; + + /** Cache associativity for the L2 cache. -1 if undetermined */ + int32_t l2_assoc; + + /** Cache associativity for the L3 cache. -1 if undetermined */ + int32_t l3_assoc; + + /** Cache associativity for the L4 cache. -1 if undetermined */ + int32_t l4_assoc; + + /** Cache-line size for L1 data cache. -1 if undetermined */ + int32_t l1_cacheline; + + /** Cache-line size for L2 cache. -1 if undetermined */ + int32_t l2_cacheline; + + /** Cache-line size for L3 cache. -1 if undetermined */ + int32_t l3_cacheline; + + /** Cache-line size for L4 cache. -1 if undetermined */ + int32_t l4_cacheline; + + /** + * The brief and human-friendly CPU codename, which was recognized.
+ * Examples: + * @code + * +--------+--------+-------+-------+-------+---------------------------------------+-----------------------+ + * | Vendor | Family | Model | Step. | Cache | Brand String | cpu_id_t.cpu_codename | + * +--------+--------+-------+-------+-------+---------------------------------------+-----------------------+ + * | AMD | 6 | 8 | 0 | 256 | (not available - will be ignored) | "K6-2" | + * | Intel | 15 | 2 | 5 | 512 | "Intel(R) Xeon(TM) CPU 2.40GHz" | "Xeon (Prestonia)" | + * | Intel | 6 | 15 | 11 | 4096 | "Intel(R) Core(TM)2 Duo CPU E6550..." | "Conroe (Core 2 Duo)" | + * | AMD | 15 | 35 | 2 | 1024 | "Dual Core AMD Opteron(tm) Proces..." | "Opteron (Dual Core)" | + * +--------+--------+-------+-------+-------+---------------------------------------+-----------------------+ + * @endcode + */ + char cpu_codename[64]; + + /** SSE execution unit size (64 or 128; -1 if N/A) */ + int32_t sse_size; + + /** + * contain miscellaneous detection information. Used to test about specifics of + * certain detected features. See \ref cpu_hint_t "CPU_HINT_*" macros below. + * @see Hints + */ + uint8_t detection_hints[CPU_HINTS_MAX]; + + /** contains information about SGX features if the processor, if present */ + struct cpu_sgx_t sgx; +}; + +/** + * @brief CPU feature identifiers + * + * Usage: + * @code + * ... + * struct cpu_raw_data_t raw; + * struct cpu_id_t id; + * if (cpuid_get_raw_data(&raw) == 0 && cpu_identify(&raw, &id) == 0) { + * if (id.flags[CPU_FEATURE_SSE2]) { + * // The CPU has SSE2... + * ... + * } else { + * // no SSE2 + * } + * } else { + * // processor cannot be determined. + * } + * @endcode + */ +typedef enum { + CPU_FEATURE_FPU = 0, /*!< Floating point unit */ + CPU_FEATURE_VME, /*!< Virtual mode extension */ + CPU_FEATURE_DE, /*!< Debugging extension */ + CPU_FEATURE_PSE, /*!< Page size extension */ + CPU_FEATURE_TSC, /*!< Time-stamp counter */ + CPU_FEATURE_MSR, /*!< Model-specific regsisters, RDMSR/WRMSR supported */ + CPU_FEATURE_PAE, /*!< Physical address extension */ + CPU_FEATURE_MCE, /*!< Machine check exception */ + CPU_FEATURE_CX8, /*!< CMPXCHG8B instruction supported */ + CPU_FEATURE_APIC, /*!< APIC support */ + CPU_FEATURE_MTRR, /*!< Memory type range registers */ + CPU_FEATURE_SEP, /*!< SYSENTER / SYSEXIT instructions supported */ + CPU_FEATURE_PGE, /*!< Page global enable */ + CPU_FEATURE_MCA, /*!< Machine check architecture */ + CPU_FEATURE_CMOV, /*!< CMOVxx instructions supported */ + CPU_FEATURE_PAT, /*!< Page attribute table */ + CPU_FEATURE_PSE36, /*!< 36-bit page address extension */ + CPU_FEATURE_PN, /*!< Processor serial # implemented (Intel P3 only) */ + CPU_FEATURE_CLFLUSH, /*!< CLFLUSH instruction supported */ + CPU_FEATURE_DTS, /*!< Debug store supported */ + CPU_FEATURE_ACPI, /*!< ACPI support (power states) */ + CPU_FEATURE_MMX, /*!< MMX instruction set supported */ + CPU_FEATURE_FXSR, /*!< FXSAVE / FXRSTOR supported */ + CPU_FEATURE_SSE, /*!< Streaming-SIMD Extensions (SSE) supported */ + CPU_FEATURE_SSE2, /*!< SSE2 instructions supported */ + CPU_FEATURE_SS, /*!< Self-snoop */ + CPU_FEATURE_HT, /*!< Hyper-threading supported (but might be disabled) */ + CPU_FEATURE_TM, /*!< Thermal monitor */ + CPU_FEATURE_IA64, /*!< IA64 supported (Itanium only) */ + CPU_FEATURE_PBE, /*!< Pending-break enable */ + CPU_FEATURE_PNI, /*!< PNI (SSE3) instructions supported */ + CPU_FEATURE_PCLMUL, /*!< PCLMULQDQ instruction supported */ + CPU_FEATURE_DTS64, /*!< 64-bit Debug store supported */ + CPU_FEATURE_MONITOR, /*!< MONITOR / MWAIT supported */ + CPU_FEATURE_DS_CPL, /*!< CPL Qualified Debug Store */ + CPU_FEATURE_VMX, /*!< Virtualization technology supported */ + CPU_FEATURE_SMX, /*!< Safer mode exceptions */ + CPU_FEATURE_EST, /*!< Enhanced SpeedStep */ + CPU_FEATURE_TM2, /*!< Thermal monitor 2 */ + CPU_FEATURE_SSSE3, /*!< SSSE3 instructionss supported (this is different from SSE3!) */ + CPU_FEATURE_CID, /*!< Context ID supported */ + CPU_FEATURE_CX16, /*!< CMPXCHG16B instruction supported */ + CPU_FEATURE_XTPR, /*!< Send Task Priority Messages disable */ + CPU_FEATURE_PDCM, /*!< Performance capabilities MSR supported */ + CPU_FEATURE_DCA, /*!< Direct cache access supported */ + CPU_FEATURE_SSE4_1, /*!< SSE 4.1 instructions supported */ + CPU_FEATURE_SSE4_2, /*!< SSE 4.2 instructions supported */ + CPU_FEATURE_SYSCALL, /*!< SYSCALL / SYSRET instructions supported */ + CPU_FEATURE_XD, /*!< Execute disable bit supported */ + CPU_FEATURE_MOVBE, /*!< MOVBE instruction supported */ + CPU_FEATURE_POPCNT, /*!< POPCNT instruction supported */ + CPU_FEATURE_AES, /*!< AES* instructions supported */ + CPU_FEATURE_XSAVE, /*!< XSAVE/XRSTOR/etc instructions supported */ + CPU_FEATURE_OSXSAVE, /*!< non-privileged copy of OSXSAVE supported */ + CPU_FEATURE_AVX, /*!< Advanced vector extensions supported */ + CPU_FEATURE_MMXEXT, /*!< AMD MMX-extended instructions supported */ + CPU_FEATURE_3DNOW, /*!< AMD 3DNow! instructions supported */ + CPU_FEATURE_3DNOWEXT, /*!< AMD 3DNow! extended instructions supported */ + CPU_FEATURE_NX, /*!< No-execute bit supported */ + CPU_FEATURE_FXSR_OPT, /*!< FFXSR: FXSAVE and FXRSTOR optimizations */ + CPU_FEATURE_RDTSCP, /*!< RDTSCP instruction supported (AMD-only) */ + CPU_FEATURE_LM, /*!< Long mode (x86_64/EM64T) supported */ + CPU_FEATURE_LAHF_LM, /*!< LAHF/SAHF supported in 64-bit mode */ + CPU_FEATURE_CMP_LEGACY, /*!< core multi-processing legacy mode */ + CPU_FEATURE_SVM, /*!< AMD Secure virtual machine */ + CPU_FEATURE_ABM, /*!< LZCNT instruction support */ + CPU_FEATURE_MISALIGNSSE,/*!< Misaligned SSE supported */ + CPU_FEATURE_SSE4A, /*!< SSE 4a from AMD */ + CPU_FEATURE_3DNOWPREFETCH, /*!< PREFETCH/PREFETCHW support */ + CPU_FEATURE_OSVW, /*!< OS Visible Workaround (AMD) */ + CPU_FEATURE_IBS, /*!< Instruction-based sampling */ + CPU_FEATURE_SSE5, /*!< SSE 5 instructions supported (deprecated, will never be 1) */ + CPU_FEATURE_SKINIT, /*!< SKINIT / STGI supported */ + CPU_FEATURE_WDT, /*!< Watchdog timer support */ + CPU_FEATURE_TS, /*!< Temperature sensor */ + CPU_FEATURE_FID, /*!< Frequency ID control */ + CPU_FEATURE_VID, /*!< Voltage ID control */ + CPU_FEATURE_TTP, /*!< THERMTRIP */ + CPU_FEATURE_TM_AMD, /*!< AMD-specified hardware thermal control */ + CPU_FEATURE_STC, /*!< Software thermal control */ + CPU_FEATURE_100MHZSTEPS,/*!< 100 MHz multiplier control */ + CPU_FEATURE_HWPSTATE, /*!< Hardware P-state control */ + CPU_FEATURE_CONSTANT_TSC, /*!< TSC ticks at constant rate */ + CPU_FEATURE_XOP, /*!< The XOP instruction set (same as the old CPU_FEATURE_SSE5) */ + CPU_FEATURE_FMA3, /*!< The FMA3 instruction set */ + CPU_FEATURE_FMA4, /*!< The FMA4 instruction set */ + CPU_FEATURE_TBM, /*!< Trailing bit manipulation instruction support */ + CPU_FEATURE_F16C, /*!< 16-bit FP convert instruction support */ + CPU_FEATURE_RDRAND, /*!< RdRand instruction */ + CPU_FEATURE_X2APIC, /*!< x2APIC, APIC_BASE.EXTD, MSRs 0000_0800h...0000_0BFFh 64-bit ICR (+030h but not +031h), no DFR (+00Eh), SELF_IPI (+040h) also see standard level 0000_000Bh */ + CPU_FEATURE_CPB, /*!< Core performance boost */ + CPU_FEATURE_APERFMPERF, /*!< MPERF/APERF MSRs support */ + CPU_FEATURE_PFI, /*!< Processor Feedback Interface support */ + CPU_FEATURE_PA, /*!< Processor accumulator */ + CPU_FEATURE_AVX2, /*!< AVX2 instructions */ + CPU_FEATURE_BMI1, /*!< BMI1 instructions */ + CPU_FEATURE_BMI2, /*!< BMI2 instructions */ + CPU_FEATURE_HLE, /*!< Hardware Lock Elision prefixes */ + CPU_FEATURE_RTM, /*!< Restricted Transactional Memory instructions */ + CPU_FEATURE_AVX512F, /*!< AVX-512 Foundation */ + CPU_FEATURE_AVX512DQ, /*!< AVX-512 Double/Quad granular insns */ + CPU_FEATURE_AVX512PF, /*!< AVX-512 Prefetch */ + CPU_FEATURE_AVX512ER, /*!< AVX-512 Exponential/Reciprocal */ + CPU_FEATURE_AVX512CD, /*!< AVX-512 Conflict detection */ + CPU_FEATURE_SHA_NI, /*!< SHA-1/SHA-256 instructions */ + CPU_FEATURE_AVX512BW, /*!< AVX-512 Byte/Word granular insns */ + CPU_FEATURE_AVX512VL, /*!< AVX-512 128/256 vector length extensions */ + CPU_FEATURE_SGX, /*!< SGX extensions. Non-autoritative, check cpu_id_t::sgx::present to verify presence */ + CPU_FEATURE_RDSEED, /*!< RDSEED instruction */ + CPU_FEATURE_ADX, /*!< ADX extensions (arbitrary precision) */ + /* termination: */ + NUM_CPU_FEATURES, +} cpu_feature_t; + +/** + * @brief CPU detection hints identifiers + * + * Usage: similar to the flags usage + */ +typedef enum { + CPU_HINT_SSE_SIZE_AUTH = 0, /*!< SSE unit size is authoritative (not only a Family/Model guesswork, but based on an actual CPUID bit) */ + /* termination */ + NUM_CPU_HINTS, +} cpu_hint_t; + +/** + * @brief SGX features flags + * \see cpu_sgx_t + * + * Usage: + * @code + * ... + * struct cpu_raw_data_t raw; + * struct cpu_id_t id; + * if (cpuid_get_raw_data(&raw) == 0 && cpu_identify(&raw, &id) == 0 && id.sgx.present) { + * if (id.sgx.flags[INTEL_SGX1]) + * // The CPU has SGX1 instructions support... + * ... + * } else { + * // no SGX + * } + * } else { + * // processor cannot be determined. + * } + * @endcode + */ + +typedef enum { + INTEL_SGX1, /*!< SGX1 instructions support */ + INTEL_SGX2, /*!< SGX2 instructions support */ + + /* termination: */ + NUM_SGX_FEATURES, +} cpu_sgx_feature_t; + +/** + * @brief Describes common library error codes + */ +typedef enum { + ERR_OK = 0, /*!< "No error" */ + ERR_NO_CPUID = -1, /*!< "CPUID instruction is not supported" */ + ERR_NO_RDTSC = -2, /*!< "RDTSC instruction is not supported" */ + ERR_NO_MEM = -3, /*!< "Memory allocation failed" */ + ERR_OPEN = -4, /*!< "File open operation failed" */ + ERR_BADFMT = -5, /*!< "Bad file format" */ + ERR_NOT_IMP = -6, /*!< "Not implemented" */ + ERR_CPU_UNKN = -7, /*!< "Unsupported processor" */ + ERR_NO_RDMSR = -8, /*!< "RDMSR instruction is not supported" */ + ERR_NO_DRIVER= -9, /*!< "RDMSR driver error (generic)" */ + ERR_NO_PERMS = -10, /*!< "No permissions to install RDMSR driver" */ + ERR_EXTRACT = -11, /*!< "Cannot extract RDMSR driver (read only media?)" */ + ERR_HANDLE = -12, /*!< "Bad handle" */ + ERR_INVMSR = -13, /*!< "Invalid MSR" */ + ERR_INVCNB = -14, /*!< "Invalid core number" */ + ERR_HANDLE_R = -15, /*!< "Error on handle read" */ + ERR_INVRANGE = -16, /*!< "Invalid given range" */ +} cpu_error_t; + +/** + * @brief Internal structure, used in cpu_tsc_mark, cpu_tsc_unmark and + * cpu_clock_by_mark + */ +struct cpu_mark_t { + uint64_t tsc; /*!< Time-stamp from RDTSC */ + uint64_t sys_clock; /*!< In microsecond resolution */ +}; + +/** + * @brief Returns the total number of logical CPU threads (even if CPUID is not present). + * + * Under VM, this number (and total_logical_cpus, since they are fetched with the same code) + * may be nonsensical, i.e. might not equal NumPhysicalCPUs*NumCoresPerCPU*HyperThreading. + * This is because no matter how many logical threads the host machine has, you may limit them + * in the VM to any number you like. **This** is the number returned by cpuid_get_total_cpus(). + * + * @returns Number of logical CPU threads available. Equals the \ref cpu_id_t::total_logical_cpus. + */ +int cpuid_get_total_cpus(void); + +/** + * @brief Checks if the CPUID instruction is supported + * @retval 1 if CPUID is present + * @retval 0 the CPU doesn't have CPUID. + */ +int cpuid_present(void); + +/** + * @brief Executes the CPUID instruction + * @param eax - the value of the EAX register when executing CPUID + * @param regs - the results will be stored here. regs[0] = EAX, regs[1] = EBX, ... + * @note CPUID will be executed with EAX set to the given value and EBX, ECX, + * EDX set to zero. + */ +void cpu_exec_cpuid(uint32_t eax, uint32_t* regs); + +/** + * @brief Executes the CPUID instruction with the given input registers + * @note This is just a bit more generic version of cpu_exec_cpuid - it allows + * you to control all the registers. + * @param regs - Input/output. Prior to executing CPUID, EAX, EBX, ECX and + * EDX will be set to regs[0], regs[1], regs[2] and regs[3]. + * After CPUID, this array will contain the results. + */ +void cpu_exec_cpuid_ext(uint32_t* regs); + +/** + * @brief Obtains the raw CPUID data from the current CPU + * @param data - a pointer to cpu_raw_data_t structure + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +int cpuid_get_raw_data(struct cpu_raw_data_t* data); + +/** + * @brief Writes the raw CPUID data to a text file + * @param data - a pointer to cpu_raw_data_t structure + * @param filename - the path of the file, where the serialized data should be + * written. If empty, stdout will be used. + * @note This is intended primarily for debugging. On some processor, which is + * not currently supported or not completely recognized by cpu_identify, + * one can still successfully get the raw data and write it to a file. + * libcpuid developers can later import this file and debug the detection + * code as if running on the actual hardware. + * The file is simple text format of "something=value" pairs. Version info + * is also written, but the format is not intended to be neither backward- + * nor forward compatible. + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +int cpuid_serialize_raw_data(struct cpu_raw_data_t* data, const char* filename); + +/** + * @brief Reads raw CPUID data from file + * @param data - a pointer to cpu_raw_data_t structure. The deserialized data will + * be written here. + * @param filename - the path of the file, containing the serialized raw data. + * If empty, stdin will be used. + * @note This function may fail, if the file is created by different version of + * the library. Also, see the notes on cpuid_serialize_raw_data. + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t +*/ +int cpuid_deserialize_raw_data(struct cpu_raw_data_t* data, const char* filename); + +/** + * @brief Identifies the CPU + * @param raw - Input - a pointer to the raw CPUID data, which is obtained + * either by cpuid_get_raw_data or cpuid_deserialize_raw_data. + * Can also be NULL, in which case the functions calls + * cpuid_get_raw_data itself. + * @param data - Output - the decoded CPU features/info is written here. + * @note The function will not fail, even if some of the information + * cannot be obtained. Even when the CPU is new and thus unknown to + * libcpuid, some generic info, such as "AMD K9 family CPU" will be + * written to data.cpu_codename, and most other things, such as the + * CPU flags, cache sizes, etc. should be detected correctly anyway. + * However, the function CAN fail, if the CPU is completely alien to + * libcpuid. + * @note While cpu_identify() and cpuid_get_raw_data() are fast for most + * purposes, running them several thousand times per second can hamper + * performance significantly. Specifically, avoid writing "cpu feature + * checker" wrapping function, which calls cpu_identify and returns the + * value of some flag, if that function is going to be called frequently. + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +int cpu_identify(struct cpu_raw_data_t* raw, struct cpu_id_t* data); + +/** + * @brief Returns the short textual representation of a CPU flag + * @param feature - the feature, whose textual representation is wanted. + * @returns a constant string like "fpu", "tsc", "sse2", etc. + * @note the names of the returned flags are compatible with those from + * /proc/cpuinfo in Linux, with the exception of `tm_amd' + */ +const char* cpu_feature_str(cpu_feature_t feature); + +/** + * @brief Returns textual description of the last error + * + * libcpuid stores an `errno'-style error status, whose description + * can be obtained with this function. + * @note This function is not thread-safe + * @see cpu_error_t + */ +const char* cpuid_error(void); + +/** + * @brief Executes RDTSC + * + * The RDTSC (ReaD Time Stamp Counter) instruction gives access to an + * internal 64-bit counter, which usually increments at each clock cycle. + * This can be used for various timing routines, and as a very precise + * clock source. It is set to zero on system startup. Beware that may not + * increment at the same frequency as the CPU. Consecutive calls of RDTSC + * are, however, guaranteed to return monotonically-increasing values. + * + * @param result - a pointer to a 64-bit unsigned integer, where the TSC value + * will be stored + * + * @note If 100% compatibility is a concern, you must first check if the + * RDTSC instruction is present (if it is not, your program will crash + * with "invalid opcode" exception). Only some very old processors (i486, + * early AMD K5 and some Cyrix CPUs) lack that instruction - they should + * have become exceedingly rare these days. To verify RDTSC presence, + * run cpu_identify() and check flags[CPU_FEATURE_TSC]. + * + * @note The monotonically increasing nature of the TSC may be violated + * on SMP systems, if their TSC clocks run at different rate. If the OS + * doesn't account for that, the TSC drift may become arbitrary large. + */ +void cpu_rdtsc(uint64_t* result); + +/** + * @brief Store TSC and timing info + * + * This function stores the current TSC value and current + * time info from a precise OS-specific clock source in the cpu_mark_t + * structure. The sys_clock field contains time with microsecond resolution. + * The values can later be used to measure time intervals, number of clocks, + * FPU frequency, etc. + * @see cpu_rdtsc + * + * @param mark [out] - a pointer to a cpu_mark_t structure + */ +void cpu_tsc_mark(struct cpu_mark_t* mark); + +/** + * @brief Calculate TSC and timing difference + * + * @param mark - input/output: a pointer to a cpu_mark_t sturcture, which has + * already been initialized by cpu_tsc_mark. The difference in + * TSC and time will be written here. + * + * This function calculates the TSC and time difference, by obtaining the + * current TSC and timing values and subtracting the contents of the `mark' + * structure from them. Results are written in the same structure. + * + * Example: + * @code + * ... + * struct cpu_mark_t mark; + * cpu_tsc_mark(&mark); + * foo(); + * cpu_tsc_unmark(&mark); + * printf("Foo finished. Executed in %llu cycles and %llu usecs\n", + * mark.tsc, mark.sys_clock); + * ... + * @endcode + */ +void cpu_tsc_unmark(struct cpu_mark_t* mark); + +/** + * @brief Calculates the CPU clock + * + * @param mark - pointer to a cpu_mark_t structure, which has been initialized + * with cpu_tsc_mark and later `stopped' with cpu_tsc_unmark. + * + * @note For reliable results, the marked time interval should be at least about + * 10 ms. + * + * @returns the CPU clock frequency, in MHz. Due to measurement error, it will + * differ from the true value in a few least-significant bits. Accuracy depends + * on the timing interval - the more, the better. If the timing interval is + * insufficient, the result is -1. Also, see the comment on cpu_clock_measure + * for additional issues and pitfalls in using RDTSC for CPU frequency + * measurements. + */ +int cpu_clock_by_mark(struct cpu_mark_t* mark); + +/** + * @brief Returns the CPU clock, as reported by the OS + * + * This function uses OS-specific functions to obtain the CPU clock. It may + * differ from the true clock for several reasons:

+ * + * i) The CPU might be in some power saving state, while the OS reports its + * full-power frequency, or vice-versa.
+ * ii) In some cases you can raise or lower the CPU frequency with overclocking + * utilities and the OS will not notice. + * + * @returns the CPU clock frequency in MHz. If the OS is not (yet) supported + * or lacks the necessary reporting machinery, the return value is -1 + */ +int cpu_clock_by_os(void); + +/** + * @brief Measure the CPU clock frequency + * + * @param millis - How much time to waste in the busy-wait cycle. In millisecs. + * Useful values 10 - 1000 + * @param quad_check - Do a more thorough measurement if nonzero + * (see the explanation). + * + * The function performs a busy-wait cycle for the given time and calculates + * the CPU frequency by the difference of the TSC values. The accuracy of the + * calculation depends on the length of the busy-wait cycle: more is better, + * but 100ms should be enough for most purposes. + * + * While this will calculate the CPU frequency correctly in most cases, there are + * several reasons why it might be incorrect:
+ * + * i) RDTSC doesn't guarantee it will run at the same clock as the CPU. + * Apparently there aren't CPUs at the moment, but still, there's no + * guarantee.
+ * ii) The CPU might be in a low-frequency power saving mode, and the CPU + * might be switched to higher frequency at any time. If this happens + * during the measurement, the result can be anywhere between the + * low and high frequencies. Also, if you're interested in the + * high frequency value only, this function might return the low one + * instead.
+ * iii) On SMP systems exhibiting TSC drift (see \ref cpu_rdtsc) + * + * the quad_check option will run four consecutive measurements and + * then return the average of the two most-consistent results. The total + * runtime of the function will still be `millis' - consider using + * a bit more time for the timing interval. + * + * Finally, for benchmarking / CPU intensive applications, the best strategy is + * to use the cpu_tsc_mark() / cpu_tsc_unmark() / cpu_clock_by_mark() method. + * Begin by mark()-ing about one second after application startup (allowing the + * power-saving manager to kick in and rise the frequency during that time), + * then unmark() just before application finishing. The result will most + * acurately represent at what frequency your app was running. + * + * @returns the CPU clock frequency in MHz (within some measurement error + * margin). If RDTSC is not supported, the result is -1. + */ +int cpu_clock_measure(int millis, int quad_check); + +/** + * @brief Measure the CPU clock frequency using instruction-counting + * + * @param millis - how much time to allocate for each run, in milliseconds + * @param runs - how many runs to perform + * + * The function performs a busy-wait cycle using a known number of "heavy" (SSE) + * instructions. These instructions run at (more or less guaranteed) 1 IPC rate, + * so by running a busy loop for a fixed amount of time, and measuring the + * amount of instructions done, the CPU clock is accurately measured. + * + * Of course, this function is still affected by the power-saving schemes, so + * the warnings as of cpu_clock_measure() still apply. However, this function is + * immune to problems with detection, related to the Intel Nehalem's "Turbo" + * mode, where the internal clock is raised, but the RDTSC rate is unaffected. + * + * The function will run for about (millis * runs) milliseconds. + * You can make only a single busy-wait run (runs == 1); however, this can + * be affected by task scheduling (which will break the counting), so allowing + * more than one run is recommended. As run length is not imperative for + * accurate readings (e.g., 50ms is sufficient), you can afford a lot of short + * runs, e.g. 10 runs of 50ms or 20 runs of 25ms. + * + * Recommended values - millis = 50, runs = 4. For more robustness, + * increase the number of runs. + * + * NOTE: on Bulldozer and later CPUs, the busy-wait cycle runs at 1.4 IPC, thus + * the results are skewed. This is corrected internally by dividing the resulting + * value by 1.4. + * However, this only occurs if the thread is executed on a single CMT + * module - if there are other threads competing for resources, the results are + * unpredictable. Make sure you run cpu_clock_by_ic() on a CPU that is free from + * competing threads, or if there are such threads, they shouldn't exceed the + * number of modules. On a Bulldozer X8, that means 4 threads. + * + * @returns the CPU clock frequency in MHz (within some measurement error + * margin). If SSE is not supported, the result is -1. If the input parameters + * are incorrect, or some other internal fault is detected, the result is -2. + */ +int cpu_clock_by_ic(int millis, int runs); + +/** + * @brief Get the CPU clock frequency (all-in-one method) + * + * This is an all-in-one method for getting the CPU clock frequency. + * It tries to use the OS for that. If the OS doesn't have this info, it + * uses cpu_clock_measure with 200ms time interval and quadruple checking. + * + * @returns the CPU clock frequency in MHz. If every possible method fails, + * the result is -1. + */ +int cpu_clock(void); + + +/** + * @brief The return value of cpuid_get_epc(). + * @details + * Describes an EPC (Enclave Page Cache) layout (physical address and size). + * A CPU may have one or more EPC areas, and information about each is + * fetched via \ref cpuid_get_epc. + */ +struct cpu_epc_t { + uint64_t start_addr; + uint64_t length; +}; + +/** + * @brief Fetches information about an EPC (Enclave Page Cache) area. + * @param index - zero-based index, valid range [0..cpu_id_t.egx.num_epc_sections) + * @param raw - a pointer to fetched raw CPUID data. Needed only for testing, + * you can safely pass NULL here (if you pass a real structure, + * it will be used for fetching the leaf 12h data if index < 2; + * otherwise the real CPUID instruction will be used). + * @returns the requested data. If the CPU doesn't support SGX, or if + * index >= cpu_id_t.egx.num_epc_sections, both fields of the returned + * structure will be zeros. + */ +struct cpu_epc_t cpuid_get_epc(int index, const struct cpu_raw_data_t* raw); + +/** + * @brief Returns the libcpuid version + * + * @returns the string representation of the libcpuid version, like "0.1.1" + */ +const char* cpuid_lib_version(void); + +typedef void (*libcpuid_warn_fn_t) (const char *msg); +/** + * @brief Sets the warning print function + * + * In some cases, the internal libcpuid machinery would like to emit useful + * debug warnings. By default, these warnings are written to stderr. However, + * you can set a custom function that will receive those warnings. + * + * @param warn_fun - the warning function you want to set. If NULL, warnings + * are disabled. The function takes const char* argument. + * + * @returns the current warning function. You can use the return value to + * keep the previous warning function and restore it at your discretion. + */ +libcpuid_warn_fn_t cpuid_set_warn_function(libcpuid_warn_fn_t warn_fun); + +/** + * @brief Sets the verbosiness level + * + * When the verbosiness level is above zero, some functions might print + * diagnostic information about what are they doing. The higher the level is, + * the more detail is printed. Level zero is guaranteed to omit all such + * output. The output is written using the same machinery as the warnings, + * @see cpuid_set_warn_function() + * + * @param level the desired verbosiness level. Useful values 0..2 inclusive + */ +void cpuid_set_verbosiness_level(int level); + + +/** + * @brief Obtains the CPU vendor from CPUID from the current CPU + * @note The result is cached. + * @returns VENDOR_UNKNOWN if failed, otherwise the CPU vendor type. + * @see cpu_vendor_t + */ +cpu_vendor_t cpuid_get_vendor(void); + +/** + * @brief a structure that holds a list of processor names + */ +struct cpu_list_t { + /** Number of entries in the list */ + int num_entries; + /** Pointers to names. There will be num_entries of them */ + char **names; +}; + +/** + * @brief Gets a list of all known CPU names from a specific vendor. + * + * This function compiles a list of all known CPU (code)names + * (i.e. the possible values of cpu_id_t::cpu_codename) for the given vendor. + * + * There are about 100 entries for Intel and AMD, and a few for the other + * vendors. The list is written out in approximate chronological introduction + * order of the parts. + * + * @param vendor the vendor to be queried + * @param list [out] the resulting list will be written here. + * NOTE: As the memory is dynamically allocated, be sure to call + * cpuid_free_cpu_list() after you're done with the data + * @see cpu_list_t + */ +void cpuid_get_cpu_list(cpu_vendor_t vendor, struct cpu_list_t* list); + +/** + * @brief Frees a CPU list + * + * This function deletes all the memory associated with a CPU list, as obtained + * by cpuid_get_cpu_list() + * + * @param list - the list to be free()'d. + */ +void cpuid_free_cpu_list(struct cpu_list_t* list); + +struct msr_driver_t; +/** + * @brief Starts/opens a driver, needed to read MSRs (Model Specific Registers) + * + * On systems that support it, this function will create a temporary + * system driver, that has privileges to execute the RDMSR instruction. + * After the driver is created, you can read MSRs by calling \ref cpu_rdmsr + * + * @returns a handle to the driver on success, and NULL on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +struct msr_driver_t* cpu_msr_driver_open(void); + +/** + * @brief Similar to \ref cpu_msr_driver_open, but accept one parameter + * + * This function works on certain operating systems (GNU/Linux, FreeBSD) + * + * @param core_num specify the core number for MSR. + * The first core number is 0. + * The last core number is \ref cpuid_get_total_cpus - 1. + * + * @returns a handle to the driver on success, and NULL on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +struct msr_driver_t* cpu_msr_driver_open_core(unsigned core_num); + +/** + * @brief Reads a Model-Specific Register (MSR) + * + * If the CPU has MSRs (as indicated by the CPU_FEATURE_MSR flag), you can + * read a MSR with the given index by calling this function. + * + * There are several prerequisites you must do before reading MSRs: + * 1) You must ensure the CPU has RDMSR. Check the CPU_FEATURE_MSR flag + * in cpu_id_t::flags + * 2) You must ensure that the CPU implements the specific MSR you intend to + * read. + * 3) You must open a MSR-reader driver. RDMSR is a privileged instruction and + * needs ring-0 access in order to work. This temporary driver is created + * by calling \ref cpu_msr_driver_open + * + * @param handle - a handle to the MSR reader driver, as created by + * cpu_msr_driver_open + * @param msr_index - the numeric ID of the MSR you want to read + * @param result - a pointer to a 64-bit integer, where the MSR value is stored + * + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +int cpu_rdmsr(struct msr_driver_t* handle, uint32_t msr_index, uint64_t* result); + + +typedef enum { + INFO_MPERF, /*!< Maximum performance frequency clock. This + is a counter, which increments as a + proportion of the actual processor speed. */ + INFO_APERF, /*!< Actual performance frequency clock. This + accumulates the core clock counts when the + core is active. */ + INFO_MIN_MULTIPLIER, /*!< Minimum CPU:FSB ratio for this CPU, + multiplied by 100. */ + INFO_CUR_MULTIPLIER, /*!< Current CPU:FSB ratio, multiplied by 100. + e.g., a CPU:FSB value of 18.5 reads as + "1850". */ + INFO_MAX_MULTIPLIER, /*!< Maximum CPU:FSB ratio for this CPU, + multiplied by 100. */ + INFO_TEMPERATURE, /*!< The current core temperature in Celsius. */ + INFO_THROTTLING, /*!< 1 if the current logical processor is + throttling. 0 if it is running normally. */ + INFO_VOLTAGE, /*!< The current core voltage in Volt, + multiplied by 100. */ + INFO_BCLK, /*!< See \ref INFO_BUS_CLOCK. */ + INFO_BUS_CLOCK, /*!< The main bus clock in MHz, + e.g., FSB/QPI/DMI/HT base clock, + multiplied by 100. */ +} cpu_msrinfo_request_t; + +/** + * @brief Similar to \ref cpu_rdmsr, but extract a range of bits + * + * @param handle - a handle to the MSR reader driver, as created by + * cpu_msr_driver_open + * @param msr_index - the numeric ID of the MSR you want to read + * @param highbit - the high bit in range, must be inferior to 64 + * @param lowbit - the low bit in range, must be equal or superior to 0 + * @param result - a pointer to a 64-bit integer, where the MSR value is stored + * + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +int cpu_rdmsr_range(struct msr_driver_t* handle, uint32_t msr_index, uint8_t highbit, + uint8_t lowbit, uint64_t* result); + +/** + * @brief Reads extended CPU information from Model-Specific Registers. + * @param handle - a handle to an open MSR driver, @see cpu_msr_driver_open + * @param which - which info field should be returned. A list of + * available information entities is listed in the + * cpu_msrinfo_request_t enum. + * @retval - if the requested information is available for the current + * processor model, the respective value is returned. + * if no information is available, or the CPU doesn't support + * the query, the special value CPU_INVALID_VALUE is returned + * @note This function is not MT-safe. If you intend to call it from multiple + * threads, guard it through a mutex or a similar primitive. + */ +int cpu_msrinfo(struct msr_driver_t* handle, cpu_msrinfo_request_t which); +#define CPU_INVALID_VALUE 0x3fffffff + +/** + * @brief Writes the raw MSR data to a text file + * @param data - a pointer to msr_driver_t structure + * @param filename - the path of the file, where the serialized data should be + * written. If empty, stdout will be used. + * @note This is intended primarily for debugging. On some processor, which is + * not currently supported or not completely recognized by cpu_identify, + * one can still successfully get the raw data and write it to a file. + * libcpuid developers can later import this file and debug the detection + * code as if running on the actual hardware. + * The file is simple text format of "something=value" pairs. Version info + * is also written, but the format is not intended to be neither backward- + * nor forward compatible. + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +int msr_serialize_raw_data(struct msr_driver_t* handle, const char* filename); + +/** + * @brief Closes an open MSR driver + * + * This function unloads the MSR driver opened by cpu_msr_driver_open and + * frees any resources associated with it. + * + * @param handle - a handle to the MSR reader driver, as created by + * cpu_msr_driver_open + * + * @returns zero if successful, and some negative number on error. + * The error message can be obtained by calling \ref cpuid_error. + * @see cpu_error_t + */ +int cpu_msr_driver_close(struct msr_driver_t* handle); + +#ifdef __cplusplus +}; /* extern "C" */ +#endif + + +/** @} */ + +#endif /* __LIBCPUID_H__ */ diff --git a/compat/libcpuid/libcpuid_constants.h b/compat/libcpuid/libcpuid_constants.h new file mode 100644 index 000000000..3ddb6d5e1 --- /dev/null +++ b/compat/libcpuid/libcpuid_constants.h @@ -0,0 +1,47 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +/** + * @File libcpuid_constants.h + * @Author Veselin Georgiev + * @Brief Some limits and constants for libcpuid + */ + +#ifndef __LIBCPUID_CONSTANTS_H__ +#define __LIBCPUID_CONSTANTS_H__ + +#define VENDOR_STR_MAX 16 +#define BRAND_STR_MAX 64 +#define CPU_FLAGS_MAX 128 +#define MAX_CPUID_LEVEL 32 +#define MAX_EXT_CPUID_LEVEL 32 +#define MAX_INTELFN4_LEVEL 8 +#define MAX_INTELFN11_LEVEL 4 +#define MAX_INTELFN12H_LEVEL 4 +#define MAX_INTELFN14H_LEVEL 4 +#define CPU_HINTS_MAX 16 +#define SGX_FLAGS_MAX 14 + +#endif /* __LIBCPUID_CONSTANTS_H__ */ diff --git a/compat/libcpuid/libcpuid_internal.h b/compat/libcpuid/libcpuid_internal.h new file mode 100644 index 000000000..7f3671da6 --- /dev/null +++ b/compat/libcpuid/libcpuid_internal.h @@ -0,0 +1,64 @@ +/* + * Copyright 2016 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +#ifndef __LIBCPUID_INTERNAL_H__ +#define __LIBCPUID_INTERNAL_H__ +/* + * This file contains internal undocumented declarations and function prototypes + * for the workings of the internal library infrastructure. + */ + +enum _common_codes_t { + NA = 0, + NC, /* No code */ +}; + +#define CODE(x) x +#define CODE2(x, y) x = y +enum _amd_code_t { + #include "amd_code_t.h" +}; +typedef enum _amd_code_t amd_code_t; + +enum _intel_code_t { + #include "intel_code_t.h" +}; +typedef enum _intel_code_t intel_code_t; +#undef CODE +#undef CODE2 + +struct internal_id_info_t { + union { + amd_code_t amd; + intel_code_t intel; + } code; + uint64_t bits; + int score; // detection (matchtable) score +}; + +int cpu_ident_internal(struct cpu_raw_data_t* raw, struct cpu_id_t* data, + struct internal_id_info_t* internal); + +#endif /* __LIBCPUID_INTERNAL_H__ */ diff --git a/compat/libcpuid/libcpuid_types.h b/compat/libcpuid/libcpuid_types.h new file mode 100644 index 000000000..36886e06b --- /dev/null +++ b/compat/libcpuid/libcpuid_types.h @@ -0,0 +1,67 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +/** + * @File libcpuid_types.h + * @Author Veselin Georgiev + * @Brief Type specifications for libcpuid. + */ + +#ifndef __LIBCPUID_TYPES_H__ +#define __LIBCPUID_TYPES_H__ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if defined(HAVE_STDINT_H) +# include +#else +/* we have to provide our own: */ +# if !defined(HAVE_INT32_T) && !defined(__int32_t_defined) +typedef int int32_t; +# endif + +# if !defined(HAVE_UINT32_T) && !defined(__uint32_t_defined) +typedef unsigned uint32_t; +# endif + +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef signed short int16_t; +typedef unsigned short uint16_t; +#if (defined _MSC_VER) && (_MSC_VER <= 1300) + /* MSVC 6.0: no long longs ... */ + typedef signed __int64 int64_t; + typedef unsigned __int64 uint64_t; +#else + /* all other sane compilers: */ + typedef signed long long int64_t; + typedef unsigned long long uint64_t; +#endif + +#endif + +#endif /* __LIBCPUID_TYPES_H__ */ diff --git a/compat/libcpuid/libcpuid_util.c b/compat/libcpuid/libcpuid_util.c new file mode 100644 index 000000000..4de1871a4 --- /dev/null +++ b/compat/libcpuid/libcpuid_util.c @@ -0,0 +1,218 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ + +#include +#include +#include +#include +#include +#include "libcpuid.h" +#include "libcpuid_util.h" + +int _current_verboselevel; + +void match_features(const struct feature_map_t* matchtable, int count, uint32_t reg, struct cpu_id_t* data) +{ + int i; + for (i = 0; i < count; i++) + if (reg & (1u << matchtable[i].bit)) + data->flags[matchtable[i].feature] = 1; +} + +static void default_warn(const char *msg) +{ + fprintf(stderr, "%s", msg); +} + +libcpuid_warn_fn_t _warn_fun = default_warn; + +#if defined(_MSC_VER) +# define vsnprintf _vsnprintf +#endif +void warnf(const char* format, ...) +{ + char buff[1024]; + va_list va; + if (!_warn_fun) return; + va_start(va, format); + vsnprintf(buff, sizeof(buff), format, va); + va_end(va); + _warn_fun(buff); +} + +void debugf(int verboselevel, const char* format, ...) +{ + char buff[1024]; + va_list va; + if (verboselevel > _current_verboselevel) return; + va_start(va, format); + vsnprintf(buff, sizeof(buff), format, va); + va_end(va); + _warn_fun(buff); +} + +static int popcount64(uint64_t mask) +{ + int num_set_bits = 0; + + while (mask) { + mask &= mask - 1; + num_set_bits++; + } + + return num_set_bits; +} + +static int score(const struct match_entry_t* entry, const struct cpu_id_t* data, + int brand_code, uint64_t bits, int model_code) +{ + int res = 0; + if (entry->family == data->family ) res += 2; + if (entry->model == data->model ) res += 2; + if (entry->stepping == data->stepping ) res += 2; + if (entry->ext_family == data->ext_family) res += 2; + if (entry->ext_model == data->ext_model ) res += 2; + if (entry->ncores == data->num_cores ) res += 2; + if (entry->l2cache == data->l2_cache ) res += 1; + if (entry->l3cache == data->l3_cache ) res += 1; + if (entry->brand_code == brand_code ) res += 2; + if (entry->model_code == model_code ) res += 2; + + res += popcount64(entry->model_bits & bits) * 2; + return res; +} + +int match_cpu_codename(const struct match_entry_t* matchtable, int count, + struct cpu_id_t* data, int brand_code, uint64_t bits, + int model_code) +{ + int bestscore = -1; + int bestindex = 0; + int i, t; + + debugf(3, "Matching cpu f:%d, m:%d, s:%d, xf:%d, xm:%d, ncore:%d, l2:%d, bcode:%d, bits:%llu, code:%d\n", + data->family, data->model, data->stepping, data->ext_family, + data->ext_model, data->num_cores, data->l2_cache, brand_code, (unsigned long long) bits, model_code); + + for (i = 0; i < count; i++) { + t = score(&matchtable[i], data, brand_code, bits, model_code); + debugf(3, "Entry %d, `%s', score %d\n", i, matchtable[i].name, t); + if (t > bestscore) { + debugf(2, "Entry `%s' selected - best score so far (%d)\n", matchtable[i].name, t); + bestscore = t; + bestindex = i; + } + } + strcpy(data->cpu_codename, matchtable[bestindex].name); + return bestscore; +} + +void generic_get_cpu_list(const struct match_entry_t* matchtable, int count, + struct cpu_list_t* list) +{ + int i, j, n, good; + n = 0; + list->names = (char**) malloc(sizeof(char*) * count); + for (i = 0; i < count; i++) { + if (strstr(matchtable[i].name, "Unknown")) continue; + good = 1; + for (j = n - 1; j >= 0; j--) + if (!strcmp(list->names[j], matchtable[i].name)) { + good = 0; + break; + } + if (!good) continue; +#if defined(_MSC_VER) + list->names[n++] = _strdup(matchtable[i].name); +#else + list->names[n++] = strdup(matchtable[i].name); +#endif + } + list->num_entries = n; +} + +static int xmatch_entry(char c, const char* p) +{ + int i, j; + if (c == 0) return -1; + if (c == p[0]) return 1; + if (p[0] == '.') return 1; + if (p[0] == '#' && isdigit(c)) return 1; + if (p[0] == '[') { + j = 1; + while (p[j] && p[j] != ']') j++; + if (!p[j]) return -1; + for (i = 1; i < j; i++) + if (p[i] == c) return j + 1; + } + return -1; +} + +int match_pattern(const char* s, const char* p) +{ + int i, j, dj, k, n, m; + n = (int) strlen(s); + m = (int) strlen(p); + for (i = 0; i < n; i++) { + if (xmatch_entry(s[i], p) != -1) { + j = 0; + k = 0; + while (j < m && ((dj = xmatch_entry(s[i + k], p + j)) != -1)) { + k++; + j += dj; + } + if (j == m) return i + 1; + } + } + return 0; +} + +struct cpu_id_t* get_cached_cpuid(void) +{ + static int initialized = 0; + static struct cpu_id_t id; + if (initialized) return &id; + if (cpu_identify(NULL, &id)) + memset(&id, 0, sizeof(id)); + initialized = 1; + return &id; +} + +int match_all(uint64_t bits, uint64_t mask) +{ + return (bits & mask) == mask; +} + +void debug_print_lbits(int debuglevel, uint64_t mask) +{ + int i, first = 0; + for (i = 0; i < 64; i++) if (mask & (((uint64_t) 1) << i)) { + if (first) first = 0; + else debugf(2, " + "); + debugf(2, "LBIT(%d)", i); + } + debugf(2, "\n"); +} diff --git a/compat/libcpuid/libcpuid_util.h b/compat/libcpuid/libcpuid_util.h new file mode 100644 index 000000000..aba63f1a9 --- /dev/null +++ b/compat/libcpuid/libcpuid_util.h @@ -0,0 +1,100 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +#ifndef __LIBCPUID_UTIL_H__ +#define __LIBCPUID_UTIL_H__ + +#define COUNT_OF(array) (sizeof(array) / sizeof(array[0])) + +#define LBIT(x) (((long long) 1) << x) + +struct feature_map_t { + unsigned bit; + cpu_feature_t feature; +}; + +void match_features(const struct feature_map_t* matchtable, int count, + uint32_t reg, struct cpu_id_t* data); + +struct match_entry_t { + int family, model, stepping, ext_family, ext_model; + int ncores, l2cache, l3cache, brand_code; + uint64_t model_bits; + int model_code; + char name[32]; +}; + +// returns the match score: +int match_cpu_codename(const struct match_entry_t* matchtable, int count, + struct cpu_id_t* data, int brand_code, uint64_t bits, + int model_code); + +void warnf(const char* format, ...) +#ifdef __GNUC__ +__attribute__((format(printf, 1, 2))) +#endif +; +void debugf(int verboselevel, const char* format, ...) +#ifdef __GNUC__ +__attribute__((format(printf, 2, 3))) +#endif +; +void generic_get_cpu_list(const struct match_entry_t* matchtable, int count, + struct cpu_list_t* list); + +/* + * Seek for a pattern in `haystack'. + * Pattern may be an fixed string, or contain the special metacharacters + * '.' - match any single character + * '#' - match any digit + * '[] - match any of the given chars (regex-like ranges are not + * supported) + * Return val: 0 if the pattern is not found. Nonzero if it is found (actually, + * x + 1 where x is the index where the match is found). + */ +int match_pattern(const char* haystack, const char* pattern); + +/* + * Gets an initialized cpu_id_t. It is cached, so that internal libcpuid + * machinery doesn't need to issue cpu_identify more than once. + */ +struct cpu_id_t* get_cached_cpuid(void); + + +/* returns true if all bits of mask are present in `bits'. */ +int match_all(uint64_t bits, uint64_t mask); + +/* print what bits a mask consists of */ +void debug_print_lbits(int debuglevel, uint64_t mask); + +/* + * Sets the current errno + */ +int set_error(cpu_error_t err); + +extern libcpuid_warn_fn_t _warn_fun; +extern int _current_verboselevel; + +#endif /* __LIBCPUID_UTIL_H__ */ diff --git a/compat/libcpuid/recog_amd.c b/compat/libcpuid/recog_amd.c new file mode 100644 index 000000000..b735c4005 --- /dev/null +++ b/compat/libcpuid/recog_amd.c @@ -0,0 +1,549 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ + +#include +#include +#include +#include "libcpuid.h" +#include "libcpuid_util.h" +#include "libcpuid_internal.h" +#include "recog_amd.h" + +const struct amd_code_str { amd_code_t code; char *str; } amd_code_str[] = { + #define CODE(x) { x, #x } + #define CODE2(x, y) CODE(x) + #include "amd_code_t.h" + #undef CODE +}; + +struct amd_code_and_bits_t { + int code; + uint64_t bits; +}; + +enum _amd_bits_t { + ATHLON_ = LBIT( 0 ), + _XP_ = LBIT( 1 ), + _M_ = LBIT( 2 ), + _MP_ = LBIT( 3 ), + MOBILE_ = LBIT( 4 ), + DURON_ = LBIT( 5 ), + SEMPRON_ = LBIT( 6 ), + OPTERON_ = LBIT( 7 ), + TURION_ = LBIT( 8 ), + _LV_ = LBIT( 9 ), + _64_ = LBIT( 10 ), + _X2 = LBIT( 11 ), + _X3 = LBIT( 12 ), + _X4 = LBIT( 13 ), + _X6 = LBIT( 14 ), + _FX = LBIT( 15 ), +}; +typedef enum _amd_bits_t amd_bits_t; + +enum _amd_model_codes_t { + // Only for Ryzen CPUs: + _1400, + _1500, + _1600, +}; + + +const struct match_entry_t cpudb_amd[] = { + { -1, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown AMD CPU" }, + + /* 486 and the likes */ + { 4, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown AMD 486" }, + { 4, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "AMD 486DX2" }, + { 4, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "AMD 486DX2WB" }, + { 4, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "AMD 486DX4" }, + { 4, 9, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "AMD 486DX4WB" }, + + /* Pentia clones */ + { 5, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown AMD 586" }, + { 5, 0, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K5" }, + { 5, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K5" }, + { 5, 2, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K5" }, + { 5, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K5" }, + + /* The K6 */ + { 5, 6, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6" }, + { 5, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6" }, + + { 5, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6-2" }, + { 5, 9, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6-III" }, + { 5, 10, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown K6" }, + { 5, 11, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown K6" }, + { 5, 12, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown K6" }, + { 5, 13, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "K6-2+" }, + + /* Athlon et al. */ + { 6, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon (Slot-A)" }, + { 6, 2, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon (Slot-A)" }, + { 6, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Duron (Spitfire)" }, + { 6, 4, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon (ThunderBird)" }, + + { 6, 6, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Athlon" }, + { 6, 6, -1, -1, -1, 1, -1, -1, NC, ATHLON_ , 0, "Athlon (Palomino)" }, + { 6, 6, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_MP_ , 0, "Athlon MP (Palomino)" }, + { 6, 6, -1, -1, -1, 1, -1, -1, NC, DURON_ , 0, "Duron (Palomino)" }, + { 6, 6, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_ , 0, "Athlon XP" }, + + { 6, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Athlon XP" }, + { 6, 7, -1, -1, -1, 1, -1, -1, NC, DURON_ , 0, "Duron (Morgan)" }, + + { 6, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon XP" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_ , 0, "Athlon XP (Thoroughbred)" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_ , 0, "Athlon XP (Thoroughbred)" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, DURON_ , 0, "Duron (Applebred)" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, SEMPRON_ , 0, "Sempron (Thoroughbred)" }, + { 6, 8, -1, -1, -1, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron (Thoroughbred)" }, + { 6, 8, -1, -1, -1, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron (Thoroughbred)" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_MP_ , 0, "Athlon MP (Thoroughbred)" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_|_M_ , 0, "Mobile Athlon (T-Bred)" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_|_M_|_LV_, 0, "Mobile Athlon (T-Bred)" }, + + { 6, 10, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Athlon XP (Barton)" }, + { 6, 10, -1, -1, -1, 1, 512, -1, NC, ATHLON_|_XP_ , 0, "Athlon XP (Barton)" }, + { 6, 10, -1, -1, -1, 1, 512, -1, NC, SEMPRON_ , 0, "Sempron (Barton)" }, + { 6, 10, -1, -1, -1, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron (Thorton)" }, + { 6, 10, -1, -1, -1, 1, 256, -1, NC, ATHLON_|_XP_ , 0, "Athlon XP (Thorton)" }, + { 6, 10, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_MP_ , 0, "Athlon MP (Barton)" }, + { 6, 10, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_|_M_ , 0, "Mobile Athlon (Barton)" }, + { 6, 10, -1, -1, -1, 1, -1, -1, NC, ATHLON_|_XP_|_M_|_LV_, 0, "Mobile Athlon (Barton)" }, + + /* K8 Architecture */ + { 15, -1, -1, 15, -1, 1, -1, -1, NC, 0 , 0, "Unknown K8" }, + { 15, -1, -1, 16, -1, 1, -1, -1, NC, 0 , 0, "Unknown K9" }, + + { 15, -1, -1, 15, -1, 1, -1, -1, NC, 0 , 0, "Unknown A64" }, + { 15, -1, -1, 15, -1, 1, -1, -1, NC, OPTERON_ , 0, "Opteron" }, + { 15, -1, -1, 15, -1, 2, -1, -1, NC, OPTERON_|_X2 , 0, "Opteron (Dual Core)" }, + { 15, 3, -1, 15, -1, 1, -1, -1, NC, OPTERON_ , 0, "Opteron" }, + { 15, 3, -1, 15, -1, 2, -1, -1, NC, OPTERON_|_X2 , 0, "Opteron (Dual Core)" }, + { 15, -1, -1, 15, -1, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (512K)" }, + { 15, -1, -1, 15, -1, 1, 1024, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (1024K)" }, + { 15, -1, -1, 15, -1, 1, -1, -1, NC, ATHLON_|_FX , 0, "Athlon FX" }, + { 15, -1, -1, 15, -1, 1, -1, -1, NC, ATHLON_|_64_|_FX , 0, "Athlon 64 FX" }, + { 15, 3, -1, 15, 35, 2, -1, -1, NC, ATHLON_|_64_|_FX , 0, "Athlon 64 FX X2 (Toledo)" }, + { 15, -1, -1, 15, -1, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (512K)" }, + { 15, -1, -1, 15, -1, 2, 1024, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (1024K)" }, + { 15, -1, -1, 15, -1, 1, 512, -1, NC, TURION_|_64_ , 0, "Turion 64 (512K)" }, + { 15, -1, -1, 15, -1, 1, 1024, -1, NC, TURION_|_64_ , 0, "Turion 64 (1024K)" }, + { 15, -1, -1, 15, -1, 2, 512, -1, NC, TURION_|_X2 , 0, "Turion 64 X2 (512K)" }, + { 15, -1, -1, 15, -1, 2, 1024, -1, NC, TURION_|_X2 , 0, "Turion 64 X2 (1024K)" }, + { 15, -1, -1, 15, -1, 1, 128, -1, NC, SEMPRON_ , 0, "A64 Sempron (128K)" }, + { 15, -1, -1, 15, -1, 1, 256, -1, NC, SEMPRON_ , 0, "A64 Sempron (256K)" }, + { 15, -1, -1, 15, -1, 1, 512, -1, NC, SEMPRON_ , 0, "A64 Sempron (512K)" }, + { 15, -1, -1, 15, 0x4f, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Orleans/512K)" }, + { 15, -1, -1, 15, 0x5f, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Orleans/512K)" }, + { 15, -1, -1, 15, 0x2f, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Venice/512K)" }, + { 15, -1, -1, 15, 0x2c, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Venice/512K)" }, + { 15, -1, -1, 15, 0x1f, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Winchester/512K)" }, + { 15, -1, -1, 15, 0x0c, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Newcastle/512K)" }, + { 15, -1, -1, 15, 0x27, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (San Diego/512K)" }, + { 15, -1, -1, 15, 0x37, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (San Diego/512K)" }, + { 15, -1, -1, 15, 0x04, 1, 512, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (ClawHammer/512K)" }, + + { 15, -1, -1, 15, 0x5f, 1, 1024, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (Orleans/1024K)" }, + { 15, -1, -1, 15, 0x27, 1, 1024, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (San Diego/1024K)" }, + { 15, -1, -1, 15, 0x04, 1, 1024, -1, NC, ATHLON_|_64_ , 0, "Athlon 64 (ClawHammer/1024K)" }, + + { 15, -1, -1, 15, 0x4b, 2, 256, -1, NC, SEMPRON_ , 0, "Athlon 64 X2 (Windsor/256K)" }, + + { 15, -1, -1, 15, 0x23, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Toledo/512K)" }, + { 15, -1, -1, 15, 0x4b, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Windsor/512K)" }, + { 15, -1, -1, 15, 0x43, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Windsor/512K)" }, + { 15, -1, -1, 15, 0x6b, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Brisbane/512K)" }, + { 15, -1, -1, 15, 0x2b, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Manchester/512K)"}, + + { 15, -1, -1, 15, 0x23, 2, 1024, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Toledo/1024K)" }, + { 15, -1, -1, 15, 0x43, 2, 1024, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon 64 X2 (Windsor/1024K)" }, + + { 15, -1, -1, 15, 0x08, 1, 128, -1, NC, MOBILE_|SEMPRON_ , 0, "Mobile Sempron 64 (Dublin/128K)"}, + { 15, -1, -1, 15, 0x08, 1, 256, -1, NC, MOBILE_|SEMPRON_ , 0, "Mobile Sempron 64 (Dublin/256K)"}, + { 15, -1, -1, 15, 0x0c, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Paris)" }, + { 15, -1, -1, 15, 0x1c, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/128K)" }, + { 15, -1, -1, 15, 0x1c, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/256K)" }, + { 15, -1, -1, 15, 0x1c, 1, 128, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Sonora/128K)"}, + { 15, -1, -1, 15, 0x1c, 1, 256, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Sonora/256K)"}, + { 15, -1, -1, 15, 0x2c, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/128K)" }, + { 15, -1, -1, 15, 0x2c, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/256K)" }, + { 15, -1, -1, 15, 0x2c, 1, 128, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Albany/128K)"}, + { 15, -1, -1, 15, 0x2c, 1, 256, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Albany/256K)"}, + { 15, -1, -1, 15, 0x2f, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/128K)" }, + { 15, -1, -1, 15, 0x2f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Palermo/256K)" }, + { 15, -1, -1, 15, 0x4f, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Manila/128K)" }, + { 15, -1, -1, 15, 0x4f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Manila/256K)" }, + { 15, -1, -1, 15, 0x5f, 1, 128, -1, NC, SEMPRON_ , 0, "Sempron 64 (Manila/128K)" }, + { 15, -1, -1, 15, 0x5f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Manila/256K)" }, + { 15, -1, -1, 15, 0x6b, 2, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 Dual (Sherman/256K)"}, + { 15, -1, -1, 15, 0x6b, 2, 512, -1, NC, SEMPRON_ , 0, "Sempron 64 Dual (Sherman/512K)"}, + { 15, -1, -1, 15, 0x7f, 1, 256, -1, NC, SEMPRON_ , 0, "Sempron 64 (Sparta/256K)" }, + { 15, -1, -1, 15, 0x7f, 1, 512, -1, NC, SEMPRON_ , 0, "Sempron 64 (Sparta/512K)" }, + { 15, -1, -1, 15, 0x4c, 1, 256, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Keene/256K)"}, + { 15, -1, -1, 15, 0x4c, 1, 512, -1, NC, MOBILE_| SEMPRON_ , 0, "Mobile Sempron 64 (Keene/512K)"}, + { 15, -1, -1, 15, -1, 2, -1, -1, NC, SEMPRON_ , 0, "Sempron Dual Core" }, + + { 15, -1, -1, 15, 0x24, 1, 512, -1, NC, TURION_|_64_ , 0, "Turion 64 (Lancaster/512K)" }, + { 15, -1, -1, 15, 0x24, 1, 1024, -1, NC, TURION_|_64_ , 0, "Turion 64 (Lancaster/1024K)" }, + { 15, -1, -1, 15, 0x48, 2, 256, -1, NC, TURION_|_X2 , 0, "Turion X2 (Taylor)" }, + { 15, -1, -1, 15, 0x48, 2, 512, -1, NC, TURION_|_X2 , 0, "Turion X2 (Trinidad)" }, + { 15, -1, -1, 15, 0x4c, 1, 512, -1, NC, TURION_|_64_ , 0, "Turion 64 (Richmond)" }, + { 15, -1, -1, 15, 0x68, 2, 256, -1, NC, TURION_|_X2 , 0, "Turion X2 (Tyler/256K)" }, + { 15, -1, -1, 15, 0x68, 2, 512, -1, NC, TURION_|_X2 , 0, "Turion X2 (Tyler/512K)" }, + { 15, -1, -1, 17, 3, 2, 512, -1, NC, TURION_|_X2 , 0, "Turion X2 (Griffin/512K)" }, + { 15, -1, -1, 17, 3, 2, 1024, -1, NC, TURION_|_X2 , 0, "Turion X2 (Griffin/1024K)" }, + + /* K10 Architecture (2007) */ + { 15, -1, -1, 16, -1, 1, -1, -1, PHENOM, 0 , 0, "Unknown AMD Phenom" }, + { 15, 2, -1, 16, -1, 1, -1, -1, PHENOM, 0 , 0, "Phenom" }, + { 15, 2, -1, 16, -1, 3, -1, -1, PHENOM, 0 , 0, "Phenom X3 (Toliman)" }, + { 15, 2, -1, 16, -1, 4, -1, -1, PHENOM, 0 , 0, "Phenom X4 (Agena)" }, + { 15, 2, -1, 16, -1, 3, 512, -1, PHENOM, 0 , 0, "Phenom X3 (Toliman/256K)" }, + { 15, 2, -1, 16, -1, 3, 512, -1, PHENOM, 0 , 0, "Phenom X3 (Toliman/512K)" }, + { 15, 2, -1, 16, -1, 4, 128, -1, PHENOM, 0 , 0, "Phenom X4 (Agena/128K)" }, + { 15, 2, -1, 16, -1, 4, 256, -1, PHENOM, 0 , 0, "Phenom X4 (Agena/256K)" }, + { 15, 2, -1, 16, -1, 4, 512, -1, PHENOM, 0 , 0, "Phenom X4 (Agena/512K)" }, + { 15, 2, -1, 16, -1, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon X2 (Kuma)" }, + /* Phenom II derivates: */ + { 15, 4, -1, 16, -1, 4, -1, -1, NC, 0 , 0, "Phenom (Deneb-based)" }, + { 15, 4, -1, 16, -1, 1, 1024, -1, NC, SEMPRON_ , 0, "Sempron (Sargas)" }, + { 15, 4, -1, 16, -1, 2, 512, -1, PHENOM2, 0 , 0, "Phenom II X2 (Callisto)" }, + { 15, 4, -1, 16, -1, 3, 512, -1, PHENOM2, 0 , 0, "Phenom II X3 (Heka)" }, + { 15, 4, -1, 16, -1, 4, 512, -1, PHENOM2, 0 , 0, "Phenom II X4" }, + { 15, 4, -1, 16, 4, 4, 512, -1, PHENOM2, 0 , 0, "Phenom II X4 (Deneb)" }, + { 15, 5, -1, 16, 5, 4, 512, -1, PHENOM2, 0 , 0, "Phenom II X4 (Deneb)" }, + { 15, 4, -1, 16, 10, 4, 512, -1, PHENOM2, 0 , 0, "Phenom II X4 (Zosma)" }, + { 15, 4, -1, 16, 10, 6, 512, -1, PHENOM2, 0 , 0, "Phenom II X6 (Thuban)" }, + /* Athlon II derivates: */ + { 15, 6, -1, 16, 6, 2, 512, -1, NC, ATHLON_|_X2 , 0, "Athlon II (Champlain)" }, + { 15, 6, -1, 16, 6, 2, 512, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon II X2 (Regor)" }, + { 15, 6, -1, 16, 6, 2, 1024, -1, NC, ATHLON_|_64_|_X2 , 0, "Athlon II X2 (Regor)" }, + { 15, 5, -1, 16, 5, 3, 512, -1, NC, ATHLON_|_64_|_X3 , 0, "Athlon II X3 (Rana)" }, + { 15, 5, -1, 16, 5, 4, 512, -1, NC, ATHLON_|_64_|_X4 , 0, "Athlon II X4 (Propus)" }, + /* Llano APUs (2011): */ + { 15, 1, -1, 18, 1, 2, -1, -1, FUSION_EA, 0 , 0, "Llano X2" }, + { 15, 1, -1, 18, 1, 3, -1, -1, FUSION_EA, 0 , 0, "Llano X3" }, + { 15, 1, -1, 18, 1, 4, -1, -1, FUSION_EA, 0 , 0, "Llano X4" }, + + /* Family 14h: Bobcat Architecture (2011) */ + { 15, 2, -1, 20, -1, 1, -1, -1, FUSION_C, 0 , 0, "Brazos Ontario" }, + { 15, 2, -1, 20, -1, 2, -1, -1, FUSION_C, 0 , 0, "Brazos Ontario (Dual-core)" }, + { 15, 1, -1, 20, -1, 1, -1, -1, FUSION_E, 0 , 0, "Brazos Zacate" }, + { 15, 1, -1, 20, -1, 2, -1, -1, FUSION_E, 0 , 0, "Brazos Zacate (Dual-core)" }, + { 15, 2, -1, 20, -1, 2, -1, -1, FUSION_Z, 0 , 0, "Brazos Desna (Dual-core)" }, + + /* Family 15h: Bulldozer Architecture (2011) */ + { 15, -1, -1, 21, 0, 4, -1, -1, NC, 0 , 0, "Bulldozer X2" }, + { 15, -1, -1, 21, 1, 4, -1, -1, NC, 0 , 0, "Bulldozer X2" }, + { 15, -1, -1, 21, 1, 6, -1, -1, NC, 0 , 0, "Bulldozer X3" }, + { 15, -1, -1, 21, 1, 8, -1, -1, NC, 0 , 0, "Bulldozer X4" }, + /* 2nd-gen, Piledriver core (2012): */ + { 15, -1, -1, 21, 2, 4, -1, -1, NC, 0 , 0, "Vishera X2" }, + { 15, -1, -1, 21, 2, 6, -1, -1, NC, 0 , 0, "Vishera X3" }, + { 15, -1, -1, 21, 2, 8, -1, -1, NC, 0 , 0, "Vishera X4" }, + { 15, 0, -1, 21, 16, 2, -1, -1, FUSION_A, 0 , 0, "Trinity X2" }, + { 15, 0, -1, 21, 16, 4, -1, -1, FUSION_A, 0 , 0, "Trinity X4" }, + { 15, 3, -1, 21, 19, 2, -1, -1, FUSION_A, 0 , 0, "Richland X2" }, + { 15, 3, -1, 21, 19, 4, -1, -1, FUSION_A, 0 , 0, "Richland X4" }, + /* 3rd-gen, Steamroller core (2014): */ + { 15, 0, -1, 21, 48, 2, -1, -1, FUSION_A, 0 , 0, "Kaveri X2" }, + { 15, 0, -1, 21, 48, 4, -1, -1, FUSION_A, 0 , 0, "Kaveri X4" }, + { 15, 8, -1, 21, 56, 4, -1, -1, FUSION_A, 0 , 0, "Godavari X4" }, + /* 4th-gen, Excavator core (2015): */ + { 15, 1, -1, 21, 96, 2, -1, -1, FUSION_A, 0 , 0, "Carrizo X2" }, + { 15, 1, -1, 21, 96, 4, -1, -1, FUSION_A, 0 , 0, "Carrizo X4" }, + { 15, 5, -1, 21, 101, 2, -1, -1, FUSION_A, 0 , 0, "Bristol Ridge X2" }, + { 15, 5, -1, 21, 101, 4, -1, -1, FUSION_A, 0 , 0, "Bristol Ridge X4" }, + { 15, 0, -1, 21, 112, 2, -1, -1, FUSION_A, 0 , 0, "Stoney Ridge X2" }, + { 15, 0, -1, 21, 112, 2, -1, -1, FUSION_E, 0 , 0, "Stoney Ridge X2" }, + + /* Family 16h: Jaguar Architecture (2013) */ + { 15, 0, -1, 22, 0, 2, -1, -1, FUSION_A, 0 , 0, "Kabini X2" }, + { 15, 0, -1, 22, 0, 4, -1, -1, FUSION_A, 0 , 0, "Kabini X4" }, + /* 2nd-gen, Puma core (2013): */ + { 15, 0, -1, 22, 48, 2, -1, -1, FUSION_E, 0 , 0, "Mullins X2" }, + { 15, 0, -1, 22, 48, 4, -1, -1, FUSION_A, 0 , 0, "Mullins X4" }, + + /* Family 17h: Zen Architecture (2017) */ + { 15, -1, -1, 23, 1, 8, -1, -1, NC, 0 , 0, "Ryzen 7" }, + { 15, -1, -1, 23, 1, 6, -1, -1, NC, 0 , _1600, "Ryzen 5" }, + { 15, -1, -1, 23, 1, 4, -1, -1, NC, 0 , _1500, "Ryzen 5" }, + { 15, -1, -1, 23, 1, 4, -1, -1, NC, 0 , _1400, "Ryzen 5" }, + { 15, -1, -1, 23, 1, 4, -1, -1, NC, 0 , 0, "Ryzen 3" }, + //{ 15, -1, -1, 23, 1, 4, -1, -1, NC, 0 , 0, "Raven Ridge" }, //TBA + + /* Newer Opterons: */ + { 15, 9, -1, 22, 9, 8, -1, -1, NC, OPTERON_ , 0, "Magny-Cours Opteron" }, +}; + + +static void load_amd_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + const struct feature_map_t matchtable_edx81[] = { + { 20, CPU_FEATURE_NX }, + { 22, CPU_FEATURE_MMXEXT }, + { 25, CPU_FEATURE_FXSR_OPT }, + { 30, CPU_FEATURE_3DNOWEXT }, + { 31, CPU_FEATURE_3DNOW }, + }; + const struct feature_map_t matchtable_ecx81[] = { + { 1, CPU_FEATURE_CMP_LEGACY }, + { 2, CPU_FEATURE_SVM }, + { 5, CPU_FEATURE_ABM }, + { 6, CPU_FEATURE_SSE4A }, + { 7, CPU_FEATURE_MISALIGNSSE }, + { 8, CPU_FEATURE_3DNOWPREFETCH }, + { 9, CPU_FEATURE_OSVW }, + { 10, CPU_FEATURE_IBS }, + { 11, CPU_FEATURE_XOP }, + { 12, CPU_FEATURE_SKINIT }, + { 13, CPU_FEATURE_WDT }, + { 16, CPU_FEATURE_FMA4 }, + { 21, CPU_FEATURE_TBM }, + }; + const struct feature_map_t matchtable_edx87[] = { + { 0, CPU_FEATURE_TS }, + { 1, CPU_FEATURE_FID }, + { 2, CPU_FEATURE_VID }, + { 3, CPU_FEATURE_TTP }, + { 4, CPU_FEATURE_TM_AMD }, + { 5, CPU_FEATURE_STC }, + { 6, CPU_FEATURE_100MHZSTEPS }, + { 7, CPU_FEATURE_HWPSTATE }, + /* id 8 is handled in common */ + { 9, CPU_FEATURE_CPB }, + { 10, CPU_FEATURE_APERFMPERF }, + { 11, CPU_FEATURE_PFI }, + { 12, CPU_FEATURE_PA }, + }; + if (raw->ext_cpuid[0][0] >= 0x80000001) { + match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data); + match_features(matchtable_ecx81, COUNT_OF(matchtable_ecx81), raw->ext_cpuid[1][2], data); + } + if (raw->ext_cpuid[0][0] >= 0x80000007) + match_features(matchtable_edx87, COUNT_OF(matchtable_edx87), raw->ext_cpuid[7][3], data); + if (raw->ext_cpuid[0][0] >= 0x8000001a) { + /* We have the extended info about SSE unit size */ + data->detection_hints[CPU_HINT_SSE_SIZE_AUTH] = 1; + data->sse_size = (raw->ext_cpuid[0x1a][0] & 1) ? 128 : 64; + } +} + +static void decode_amd_cache_info(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + int l3_result; + const int assoc_table[16] = { + 0, 1, 2, 0, 4, 0, 8, 0, 16, 0, 32, 48, 64, 96, 128, 255 + }; + unsigned n = raw->ext_cpuid[0][0]; + + if (n >= 0x80000005) { + data->l1_data_cache = (raw->ext_cpuid[5][2] >> 24) & 0xff; + data->l1_assoc = (raw->ext_cpuid[5][2] >> 16) & 0xff; + data->l1_cacheline = (raw->ext_cpuid[5][2]) & 0xff; + data->l1_instruction_cache = (raw->ext_cpuid[5][3] >> 24) & 0xff; + } + if (n >= 0x80000006) { + data->l2_cache = (raw->ext_cpuid[6][2] >> 16) & 0xffff; + data->l2_assoc = assoc_table[(raw->ext_cpuid[6][2] >> 12) & 0xf]; + data->l2_cacheline = (raw->ext_cpuid[6][2]) & 0xff; + + l3_result = (raw->ext_cpuid[6][3] >> 18); + if (l3_result > 0) { + l3_result = 512 * l3_result; /* AMD spec says it's a range, + but we take the lower bound */ + data->l3_cache = l3_result; + data->l3_assoc = assoc_table[(raw->ext_cpuid[6][3] >> 12) & 0xf]; + data->l3_cacheline = (raw->ext_cpuid[6][3]) & 0xff; + } else { + data->l3_cache = 0; + } + } +} + +static void decode_amd_number_of_cores(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + int logical_cpus = -1, num_cores = -1; + + if (raw->basic_cpuid[0][0] >= 1) { + logical_cpus = (raw->basic_cpuid[1][1] >> 16) & 0xff; + if (raw->ext_cpuid[0][0] >= 8) { + num_cores = 1 + (raw->ext_cpuid[8][2] & 0xff); + } + } + if (data->flags[CPU_FEATURE_HT]) { + if (num_cores > 1) { + if (data->ext_family >= 23) + num_cores /= 2; // e.g., Ryzen 7 reports 16 "real" cores, but they are really just 8. + data->num_cores = num_cores; + data->num_logical_cpus = logical_cpus; + } else { + data->num_cores = 1; + data->num_logical_cpus = (logical_cpus >= 2 ? logical_cpus : 2); + } + } else { + data->num_cores = data->num_logical_cpus = 1; + } +} + +static int amd_has_turion_modelname(const char *bs) +{ + /* We search for something like TL-60. Ahh, I miss regexes...*/ + int i, l, k; + char code[3] = {0}; + const char* codes[] = { "ML", "MT", "MK", "TK", "TL", "RM", "ZM", "" }; + l = (int) strlen(bs); + for (i = 3; i < l - 2; i++) { + if (bs[i] == '-' && + isupper(bs[i-1]) && isupper(bs[i-2]) && !isupper(bs[i-3]) && + isdigit(bs[i+1]) && isdigit(bs[i+2]) && !isdigit(bs[i+3])) + { + code[0] = bs[i-2]; + code[1] = bs[i-1]; + for (k = 0; codes[k][0]; k++) + if (!strcmp(codes[k], code)) return 1; + } + } + return 0; +} + +static struct amd_code_and_bits_t decode_amd_codename_part1(const char *bs) +{ + amd_code_t code = NC; + uint64_t bits = 0; + struct amd_code_and_bits_t result; + + if (strstr(bs, "Dual Core") || + strstr(bs, "Dual-Core") || + strstr(bs, " X2 ")) + bits |= _X2; + if (strstr(bs, " X4 ")) bits |= _X4; + if (strstr(bs, " X3 ")) bits |= _X3; + if (strstr(bs, "Opteron")) bits |= OPTERON_; + if (strstr(bs, "Phenom")) { + code = (strstr(bs, "II")) ? PHENOM2 : PHENOM; + } + if (amd_has_turion_modelname(bs)) { + bits |= TURION_; + } + if (strstr(bs, "Athlon(tm)")) bits |= ATHLON_; + if (strstr(bs, "Sempron(tm)")) bits |= SEMPRON_; + if (strstr(bs, "Duron")) bits |= DURON_; + if (strstr(bs, " 64 ")) bits |= _64_; + if (strstr(bs, " FX")) bits |= _FX; + if (strstr(bs, " MP")) bits |= _MP_; + if (strstr(bs, "Athlon(tm) 64") || strstr(bs, "Athlon(tm) II X") || match_pattern(bs, "Athlon(tm) X#")) { + bits |= ATHLON_ | _64_; + } + if (strstr(bs, "Turion")) bits |= TURION_; + + if (strstr(bs, "mobile") || strstr(bs, "Mobile")) { + bits |= MOBILE_; + } + + if (strstr(bs, "XP")) bits |= _XP_; + if (strstr(bs, "XP-M")) bits |= _M_; + if (strstr(bs, "(LV)")) bits |= _LV_; + + if (match_pattern(bs, "C-##")) code = FUSION_C; + if (match_pattern(bs, "E-###")) code = FUSION_E; + if (match_pattern(bs, "Z-##")) code = FUSION_Z; + if (match_pattern(bs, "E#-####") || match_pattern(bs, "A#-####")) code = FUSION_EA; + + result.code = code; + result.bits = bits; + return result; +} + +static int decode_amd_ryzen_model_code(const char* bs) +{ + const struct { + int model_code; + const char* match_str; + } patterns[] = { + { _1600, "1600" }, + { _1500, "1500" }, + { _1400, "1400" }, + }; + int i; + + for (i = 0; i < COUNT_OF(patterns); i++) + if (strstr(bs, patterns[i].match_str)) + return patterns[i].model_code; + // + return 0; +} + +static void decode_amd_codename(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) +{ + struct amd_code_and_bits_t code_and_bits = decode_amd_codename_part1(data->brand_str); + int i = 0; + char* code_str = NULL; + int model_code; + + for (i = 0; i < COUNT_OF(amd_code_str); i++) { + if (code_and_bits.code == amd_code_str[i].code) { + code_str = amd_code_str[i].str; + break; + } + } + if (/*code == ATHLON_64_X2*/ match_all(code_and_bits.bits, ATHLON_|_64_|_X2) && data->l2_cache < 512) { + code_and_bits.bits &= ~(ATHLON_ | _64_); + code_and_bits.bits |= SEMPRON_; + } + if (code_str) + debugf(2, "Detected AMD brand code: %d (%s)\n", code_and_bits.code, code_str); + else + debugf(2, "Detected AMD brand code: %d\n", code_and_bits.code); + + if (code_and_bits.bits) { + debugf(2, "Detected AMD bits: "); + debug_print_lbits(2, code_and_bits.bits); + } + // is it Ryzen? if so, we need to detect discern between the four-core 1400/1500 (Ryzen 5) and the four-core Ryzen 3: + model_code = (data->ext_family == 23) ? decode_amd_ryzen_model_code(data->brand_str) : 0; + + internal->code.amd = code_and_bits.code; + internal->bits = code_and_bits.bits; + internal->score = match_cpu_codename(cpudb_amd, COUNT_OF(cpudb_amd), data, code_and_bits.code, + code_and_bits.bits, model_code); +} + +int cpuid_identify_amd(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) +{ + load_amd_features(raw, data); + decode_amd_cache_info(raw, data); + decode_amd_number_of_cores(raw, data); + decode_amd_codename(raw, data, internal); + return 0; +} + +void cpuid_get_list_amd(struct cpu_list_t* list) +{ + generic_get_cpu_list(cpudb_amd, COUNT_OF(cpudb_amd), list); +} diff --git a/compat/libcpuid/recog_amd.h b/compat/libcpuid/recog_amd.h new file mode 100644 index 000000000..34e895983 --- /dev/null +++ b/compat/libcpuid/recog_amd.h @@ -0,0 +1,32 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +#ifndef __RECOG_AMD_H__ +#define __RECOG_AMD_H__ + +int cpuid_identify_amd(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal); +void cpuid_get_list_amd(struct cpu_list_t* list); + +#endif /* __RECOG_AMD_H__ */ diff --git a/compat/libcpuid/recog_intel.c b/compat/libcpuid/recog_intel.c new file mode 100644 index 000000000..1d6c6a840 --- /dev/null +++ b/compat/libcpuid/recog_intel.c @@ -0,0 +1,935 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +#include +#include +#include "libcpuid.h" +#include "libcpuid_util.h" +#include "libcpuid_internal.h" +#include "recog_intel.h" + +const struct intel_bcode_str { intel_code_t code; char *str; } intel_bcode_str[] = { + #define CODE(x) { x, #x } + #define CODE2(x, y) CODE(x) + #include "intel_code_t.h" + #undef CODE +}; + +typedef struct { + int code; + uint64_t bits; +} intel_code_and_bits_t; + +enum _intel_model_t { + UNKNOWN = -1, + _3000 = 100, + _3100, + _3200, + X3200, + _3300, + X3300, + _5100, + _5200, + _5300, + _5400, + _2xxx, /* Core i[357] 2xxx */ + _3xxx, /* Core i[357] 3xxx */ +}; +typedef enum _intel_model_t intel_model_t; + +enum _intel_bits_t { + PENTIUM_ = LBIT( 0 ), + CELERON_ = LBIT( 1 ), + MOBILE_ = LBIT( 2 ), + CORE_ = LBIT( 3 ), + _I_ = LBIT( 4 ), + _M_ = LBIT( 5 ), + _3 = LBIT( 6 ), + _5 = LBIT( 7 ), + _7 = LBIT( 8 ), + XEON_ = LBIT( 9 ), + _MP = LBIT( 10 ), + ATOM_ = LBIT( 11 ), + +}; +typedef enum _intel_bits_t intel_bits_t; + +const struct match_entry_t cpudb_intel[] = { + { -1, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Intel CPU" }, + + /* i486 */ + { 4, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown i486" }, + { 4, 0, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX-25/33" }, + { 4, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX-50" }, + { 4, 2, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 SX" }, + { 4, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX2" }, + { 4, 4, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 SL" }, + { 4, 5, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 SX2" }, + { 4, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX2 WriteBack" }, + { 4, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX4" }, + { 4, 9, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "i486 DX4 WriteBack" }, + + /* All Pentia: + Pentium 1 */ + { 5, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Pentium" }, + { 5, 0, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium A-Step" }, + { 5, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium 1 (0.8u)" }, + { 5, 2, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium 1 (0.35u)" }, + { 5, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium OverDrive" }, + { 5, 4, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium 1 (0.35u)" }, + { 5, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium 1 (0.35u)" }, + { 5, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium MMX (0.25u)" }, + + /* Pentium 2 / 3 / M / Conroe / whatsnext - all P6 based. */ + { 6, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown P6" }, + { 6, 0, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium Pro" }, + { 6, 1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium Pro" }, + { 6, 3, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium II (Klamath)" }, + { 6, 5, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium II (Deschutes)" }, + { 6, 5, -1, -1, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile Pentium II (Tonga)"}, + { 6, 6, -1, -1, -1, 1, -1, -1, NC,0 , 0, "Pentium II (Dixon)" }, + + { 6, 3, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-II Xeon (Klamath)" }, + { 6, 5, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-II Xeon (Drake)" }, + { 6, 6, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-II Xeon (Dixon)" }, + + { 6, 5, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-II Celeron (Covington)" }, + { 6, 6, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-II Celeron (Mendocino)" }, + + /* -------------------------------------------------- */ + + { 6, 7, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium III (Katmai)" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium III (Coppermine)"}, + { 6, 10, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium III (Coppermine)"}, + { 6, 11, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Pentium III (Tualatin)" }, + + { 6, 7, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-III Xeon (Tanner)" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-III Xeon (Cascades)" }, + { 6, 10, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-III Xeon (Cascades)" }, + { 6, 11, -1, -1, -1, 1, -1, -1, NC, XEON_ , 0, "P-III Xeon (Tualatin)" }, + + { 6, 7, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-III Celeron (Katmai)" }, + { 6, 8, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-III Celeron (Coppermine)" }, + { 6, 10, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-III Celeron (Coppermine)" }, + { 6, 11, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "P-III Celeron (Tualatin)" }, + + /* Netburst based (Pentium 4 and later) + classic P4s */ + { 15, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Pentium 4" }, + { 15, -1, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "Unknown P-4 Celeron" }, + { 15, -1, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Unknown Xeon" }, + + { 15, 0, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Willamette)" }, + { 15, 1, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Willamette)" }, + { 15, 2, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Northwood)" }, + { 15, 3, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Prescott)" }, + { 15, 4, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Prescott)" }, + { 15, 6, -1, 15, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium 4 (Cedar Mill)" }, + { 15, 0, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Willamette)" }, + { 15, 1, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Willamette)" }, + { 15, 2, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Northwood)" }, + { 15, 3, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Prescott)" }, + { 15, 4, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Prescott)" }, + { 15, 6, -1, 15, -1, 1, -1, -1, NC, MOBILE_|PENTIUM_, 0, "Mobile P-4 (Cedar Mill)" }, + + /* server CPUs */ + { 15, 0, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Foster)" }, + { 15, 1, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Foster)" }, + { 15, 2, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Prestonia)" }, + { 15, 2, -1, 15, -1, 1, -1, -1, NC, XEON_|_MP , 0, "Xeon (Gallatin)" }, + { 15, 3, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Nocona)" }, + { 15, 4, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Nocona)" }, + { 15, 4, -1, 15, -1, 1, -1, -1, IRWIN, XEON_ , 0, "Xeon (Irwindale)" }, + { 15, 4, -1, 15, -1, 1, -1, -1, NC, XEON_|_MP , 0, "Xeon (Cranford)" }, + { 15, 4, -1, 15, -1, 1, -1, -1, POTOMAC, XEON_ , 0, "Xeon (Potomac)" }, + { 15, 6, -1, 15, -1, 1, -1, -1, NC, XEON_ , 0, "Xeon (Dempsey)" }, + + /* Pentium Ds */ + { 15, 4, 4, 15, -1, 1, -1, -1, NC, 0 , 0, "Pentium D (SmithField)" }, + { 15, 4, -1, 15, -1, 1, -1, -1, PENTIUM_D, 0 , 0, "Pentium D (SmithField)" }, + { 15, 4, 7, 15, -1, 1, -1, -1, NC, 0 , 0, "Pentium D (SmithField)" }, + { 15, 6, -1, 15, -1, 1, -1, -1, PENTIUM_D, 0 , 0, "Pentium D (Presler)" }, + + /* Celeron and Celeron Ds */ + { 15, 1, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron (Willamette)" }, + { 15, 2, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron (Northwood)" }, + { 15, 3, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron D (Prescott)" }, + { 15, 4, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron D (Prescott)" }, + { 15, 6, -1, 15, -1, 1, -1, -1, NC, CELERON_ , 0, "P-4 Celeron D (Cedar Mill)" }, + + /* -------------------------------------------------- */ + /* Intel Core microarchitecture - P6-based */ + + { 6, 9, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Pentium M" }, + { 6, 9, -1, -1, -1, 1, -1, -1, PENTIUM_M, 0 , 0, "Unknown Pentium M" }, + { 6, 9, -1, -1, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium M (Banias)" }, + { 6, 9, -1, -1, -1, 1, -1, -1, PENTIUM_M, 0 , 0, "Pentium M (Banias)" }, + { 6, 9, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "Celeron M" }, + { 6, 13, -1, -1, -1, 1, -1, -1, NC, PENTIUM_ , 0, "Pentium M (Dothan)" }, + { 6, 13, -1, -1, -1, 1, -1, -1, PENTIUM_M, 0 , 0, "Pentium M (Dothan)" }, + { 6, 13, -1, -1, -1, 1, -1, -1, NC, CELERON_ , 0, "Celeron M" }, + + { 6, 12, -1, -1, -1, -1, -1, -1, NC, ATOM_ , 0, "Unknown Atom" }, + { 6, 12, -1, -1, -1, -1, -1, -1, DIAMONDVILLE,ATOM_, 0, "Atom (Diamondville)" }, + { 6, 12, -1, -1, -1, -1, -1, -1, SILVERTHORNE,ATOM_, 0, "Atom (Silverthorne)" }, + { 6, 12, -1, -1, -1, -1, -1, -1, CEDARVIEW, ATOM_ , 0, "Atom (Cedarview)" }, + { 6, 6, -1, -1, -1, -1, -1, -1, CEDARVIEW, ATOM_ , 0, "Atom (Cedarview)" }, + { 6, 12, -1, -1, -1, -1, -1, -1, PINEVIEW, ATOM_ , 0, "Atom (Pineview)" }, + + /* -------------------------------------------------- */ + + { 6, 14, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Yonah" }, + { 6, 14, -1, -1, -1, 1, -1, -1, CORE_SOLO, 0 , 0, "Yonah (Core Solo)" }, + { 6, 14, -1, -1, -1, 2, -1, -1, CORE_DUO, 0 , 0, "Yonah (Core Duo)" }, + { 6, 14, -1, -1, -1, 1, -1, -1, CORE_SOLO, MOBILE_, 0, "Yonah (Core Solo)" }, + { 6, 14, -1, -1, -1, 2, -1, -1, CORE_DUO , MOBILE_, 0, "Yonah (Core Duo)" }, + { 6, 14, -1, -1, -1, 1, -1, -1, CORE_SOLO, 0 , 0, "Yonah (Core Solo)" }, + + { 6, 15, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Unknown Core 2" }, + { 6, 15, -1, -1, -1, 2, 4096, -1, CORE_DUO, 0 , 0, "Conroe (Core 2 Duo)" }, + { 6, 15, -1, -1, -1, 2, 1024, -1, CORE_DUO, 0 , 0, "Conroe (Core 2 Duo) 1024K" }, + { 6, 15, -1, -1, -1, 2, 512, -1, CORE_DUO, 0 , 0, "Conroe (Core 2 Duo) 512K" }, + { 6, 15, -1, -1, -1, 4, -1, -1, QUAD_CORE, 0 , 0, "Kentsfield (Core 2 Quad)" }, + { 6, 15, -1, -1, -1, 4, 4096, -1, QUAD_CORE, 0 , 0, "Kentsfield (Core 2 Quad)" }, + { 6, 15, -1, -1, -1, 400, -1, -1, MORE_THAN_QUADCORE, 0, 0, "More than quad-core" }, + { 6, 15, -1, -1, -1, 2, 2048, -1, CORE_DUO, 0 , 0, "Allendale (Core 2 Duo)" }, + { 6, 15, -1, -1, -1, 2, -1, -1, MOBILE_CORE_DUO, 0, 0, "Merom (Core 2 Duo)" }, + { 6, 15, -1, -1, -1, 2, 2048, -1, MEROM, 0 , 0, "Merom (Core 2 Duo) 2048K" }, + { 6, 15, -1, -1, -1, 2, 4096, -1, MEROM, 0 , 0, "Merom (Core 2 Duo) 4096K" }, + + { 6, 15, -1, -1, 15, 1, -1, -1, NC, CELERON_ , 0, "Conroe-L (Celeron)" }, + { 6, 6, -1, -1, 22, 1, -1, -1, NC, CELERON_ , 0, "Conroe-L (Celeron)" }, + { 6, 15, -1, -1, 15, 2, -1, -1, NC, CELERON_ , 0, "Conroe-L (Allendale)" }, + { 6, 6, -1, -1, 22, 2, -1, -1, NC, CELERON_ , 0, "Conroe-L (Allendale)" }, + + + { 6, 6, -1, -1, 22, 1, -1, -1, NC, 0 , 0, "Unknown Core ?" }, + { 6, 7, -1, -1, 23, 1, -1, -1, NC, 0 , 0, "Unknown Core ?" }, + { 6, 6, -1, -1, 22, 400, -1, -1, MORE_THAN_QUADCORE, 0, 0, "More than quad-core" }, + { 6, 7, -1, -1, 23, 400, -1, -1, MORE_THAN_QUADCORE, 0, 0, "More than quad-core" }, + + { 6, 7, -1, -1, 23, 1, -1, -1, CORE_SOLO , 0, 0, "Unknown Core 45nm" }, + { 6, 7, -1, -1, 23, 1, -1, -1, CORE_DUO , 0, 0, "Unknown Core 45nm" }, + { 6, 7, -1, -1, 23, 2, 1024, -1, WOLFDALE , 0, 0, "Celeron Wolfdale 1M" }, + { 6, 7, -1, -1, 23, 2, 2048, -1, WOLFDALE , 0, 0, "Wolfdale (Core 2 Duo) 2M" }, + { 6, 7, -1, -1, 23, 2, 3072, -1, WOLFDALE , 0, 0, "Wolfdale (Core 2 Duo) 3M" }, + { 6, 7, -1, -1, 23, 2, 6144, -1, WOLFDALE , 0, 0, "Wolfdale (Core 2 Duo) 6M" }, + { 6, 7, -1, -1, 23, 1, -1, -1, MOBILE_CORE_DUO , 0, 0, "Penryn (Core 2 Duo)" }, + { 6, 7, -1, -1, 23, 2, 1024, -1, PENRYN , 0, 0, "Penryn (Core 2 Duo)" }, + { 6, 7, -1, -1, 23, 2, 3072, -1, PENRYN , 0, 0, "Penryn (Core 2 Duo) 3M" }, + { 6, 7, -1, -1, 23, 2, 6144, -1, PENRYN , 0, 0, "Penryn (Core 2 Duo) 6M" }, + { 6, 7, -1, -1, 23, 4, 2048, -1, NC , 0, 0, "Yorkfield (Core 2 Quad) 2M"}, + { 6, 7, -1, -1, 23, 4, 3072, -1, NC , 0, 0, "Yorkfield (Core 2 Quad) 3M"}, + { 6, 7, -1, -1, 23, 4, 6144, -1, NC , 0, 0, "Yorkfield (Core 2 Quad) 6M"}, + + /* Core microarchitecture-based Xeons: */ + { 6, 14, -1, -1, 14, 1, -1, -1, NC, XEON_ , 0, "Xeon LV" }, + { 6, 15, -1, -1, 15, 2, 4096, -1, NC, XEON_ , _5100, "Xeon (Woodcrest)" }, + { 6, 15, -1, -1, 15, 2, 2048, -1, NC, XEON_ , _3000, "Xeon (Conroe/2M)" }, + { 6, 15, -1, -1, 15, 2, 4096, -1, NC, XEON_ , _3000, "Xeon (Conroe/4M)" }, + { 6, 15, -1, -1, 15, 4, 4096, -1, NC, XEON_ , X3200, "Xeon (Kentsfield)" }, + { 6, 15, -1, -1, 15, 4, 4096, -1, NC, XEON_ , _5300, "Xeon (Clovertown)" }, + { 6, 7, -1, -1, 23, 2, 6144, -1, NC, XEON_ , _3100, "Xeon (Wolfdale)" }, + { 6, 7, -1, -1, 23, 2, 6144, -1, NC, XEON_ , _5200, "Xeon (Wolfdale DP)" }, + { 6, 7, -1, -1, 23, 4, 6144, -1, NC, XEON_ , _5400, "Xeon (Harpertown)" }, + { 6, 7, -1, -1, 23, 4, 3072, -1, NC, XEON_ , X3300, "Xeon (Yorkfield/3M)" }, + { 6, 7, -1, -1, 23, 4, 6144, -1, NC, XEON_ , X3300, "Xeon (Yorkfield/6M)" }, + + /* Nehalem CPUs (45nm): */ + { 6, 10, -1, -1, 26, 4, -1, -1, GAINESTOWN, XEON_ , 0, "Gainestown (Xeon)" }, + { 6, 10, -1, -1, 26, 4, -1, 4096, GAINESTOWN, XEON_ , 0, "Gainestown 4M (Xeon)" }, + { 6, 10, -1, -1, 26, 4, -1, 8192, GAINESTOWN, XEON_ , 0, "Gainestown 8M (Xeon)" }, + { 6, 10, -1, -1, 26, 4, -1, -1, NC, XEON_|_7 , 0, "Bloomfield (Xeon)" }, + { 6, 10, -1, -1, 26, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Bloomfield (Core i7)" }, + { 6, 10, -1, -1, 30, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Lynnfield (Core i7)" }, + { 6, 5, -1, -1, 37, 4, -1, 8192, NC, CORE_|_I_|_5 , 0, "Lynnfield (Core i5)" }, + + /* Westmere CPUs (32nm): */ + { 6, 5, -1, -1, 37, 2, -1, -1, NC, 0 , 0, "Unknown Core i3/i5" }, + { 6, 12, -1, -1, 44, -1, -1, -1, WESTMERE, XEON_ , 0, "Westmere (Xeon)" }, + { 6, 12, -1, -1, 44, -1, -1, 12288, WESTMERE, XEON_ , 0, "Gulftown (Xeon)" }, + { 6, 12, -1, -1, 44, 4, -1, 12288, NC, CORE_|_I_|_7 , 0, "Gulftown (Core i7)" }, + { 6, 5, -1, -1, 37, 2, -1, 4096, NC, CORE_|_I_|_5 , 0, "Clarkdale (Core i5)" }, + { 6, 5, -1, -1, 37, 2, -1, 4096, NC, CORE_|_I_|_3 , 0, "Clarkdale (Core i3)" }, + { 6, 5, -1, -1, 37, 2, -1, -1, NC, PENTIUM_ , 0, "Arrandale" }, + { 6, 5, -1, -1, 37, 2, -1, 4096, NC, CORE_|_I_|_7 , 0, "Arrandale (Core i7)" }, + { 6, 5, -1, -1, 37, 2, -1, 3072, NC, CORE_|_I_|_5 , 0, "Arrandale (Core i5)" }, + { 6, 5, -1, -1, 37, 2, -1, 3072, NC, CORE_|_I_|_3 , 0, "Arrandale (Core i3)" }, + + /* Sandy Bridge CPUs (32nm): */ + { 6, 10, -1, -1, 42, -1, -1, -1, NC, 0 , 0, "Unknown Sandy Bridge" }, + { 6, 10, -1, -1, 42, -1, -1, -1, NC, XEON_ , 0, "Sandy Bridge (Xeon)" }, + { 6, 10, -1, -1, 42, -1, -1, -1, NC, CORE_|_I_|_7 , 0, "Sandy Bridge (Core i7)" }, + { 6, 10, -1, -1, 42, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Sandy Bridge (Core i7)" }, + { 6, 10, -1, -1, 42, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Sandy Bridge (Core i5)" }, + { 6, 10, -1, -1, 42, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Sandy Bridge (Core i3)" }, + { 6, 10, -1, -1, 42, 2, -1, -1, NC, PENTIUM_ , 0, "Sandy Bridge (Pentium)" }, + { 6, 10, -1, -1, 42, 1, -1, -1, NC, CELERON_ , 0, "Sandy Bridge (Celeron)" }, + { 6, 10, -1, -1, 42, 2, -1, -1, NC, CELERON_ , 0, "Sandy Bridge (Celeron)" }, + { 6, 13, -1, -1, 45, -1, -1, -1, NC, CORE_|_I_|_3 , 0, "Sandy Bridge-E" }, + { 6, 13, -1, -1, 45, -1, -1, -1, NC, XEON_ , 0, "Sandy Bridge-E (Xeon)" }, + + /* Ivy Bridge CPUs (22nm): */ + { 6, 10, -1, -1, 58, -1, -1, -1, NC, XEON_ , 0, "Ivy Bridge (Xeon)" }, + { 6, 10, -1, -1, 58, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Ivy Bridge (Core i7)" }, + { 6, 10, -1, -1, 58, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Ivy Bridge (Core i5)" }, + { 6, 10, -1, -1, 58, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Ivy Bridge (Core i3)" }, + { 6, 10, -1, -1, 58, 2, -1, -1, NC, PENTIUM_ , 0, "Ivy Bridge (Pentium)" }, + { 6, 10, -1, -1, 58, 1, -1, -1, NC, CELERON_ , 0, "Ivy Bridge (Celeron)" }, + { 6, 10, -1, -1, 58, 2, -1, -1, NC, CELERON_ , 0, "Ivy Bridge (Celeron)" }, + { 6, 14, -1, -1, 62, -1, -1, -1, NC, 0 , 0, "Ivy Bridge-E" }, + + /* Haswell CPUs (22nm): */ + { 6, 12, -1, -1, 60, -1, -1, -1, NC, XEON_ , 0, "Haswell (Xeon)" }, + { 6, 12, -1, -1, 60, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Haswell (Core i7)" }, + { 6, 5, -1, -1, 69, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Haswell (Core i7)" }, + { 6, 6, -1, -1, 70, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Haswell (Core i7)" }, + { 6, 12, -1, -1, 60, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Haswell (Core i5)" }, + { 6, 5, -1, -1, 69, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Haswell (Core i5)" }, + { 6, 12, -1, -1, 60, 2, -1, -1, NC, CORE_|_I_|_5 , 0, "Haswell (Core i5)" }, + { 6, 5, -1, -1, 69, 2, -1, -1, NC, CORE_|_I_|_5 , 0, "Haswell (Core i5)" }, + { 6, 12, -1, -1, 60, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Haswell (Core i3)" }, + { 6, 5, -1, -1, 69, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Haswell (Core i3)" }, + { 6, 12, -1, -1, 60, 2, -1, -1, NC, PENTIUM_ , 0, "Haswell (Pentium)" }, + { 6, 12, -1, -1, 60, 2, -1, -1, NC, CELERON_ , 0, "Haswell (Celeron)" }, + { 6, 12, -1, -1, 60, 1, -1, -1, NC, CELERON_ , 0, "Haswell (Celeron)" }, + { 6, 15, -1, -1, 63, -1, -1, -1, NC, 0 , 0, "Haswell-E" }, + + /* Broadwell CPUs (14nm): */ + { 6, 7, -1, -1, 71, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell (Core i7)" }, + { 6, 7, -1, -1, 71, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Broadwell (Core i5)" }, + { 6, 13, -1, -1, 61, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell-U (Core i7)" }, + { 6, 13, -1, -1, 61, 2, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell-U (Core i7)" }, + { 6, 13, -1, -1, 61, 2, -1, -1, NC, CORE_|_I_|_5 , 0, "Broadwell-U (Core i5)" }, + { 6, 13, -1, -1, 61, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Broadwell-U (Core i3)" }, + { 6, 13, -1, -1, 61, 2, -1, -1, NC, PENTIUM_ , 0, "Broadwell-U (Pentium)" }, + { 6, 13, -1, -1, 61, 2, -1, -1, NC, CELERON_ , 0, "Broadwell-U (Celeron)" }, + { 6, 13, -1, -1, 61, 2, -1, -1, NA, 0 , 0, "Broadwell-U (Core M)" }, + { 6, 15, -1, -1, 79, -1, -1, -1, NC, XEON_ , 0, "Broadwell-E (Xeon)" }, + { 6, 15, -1, -1, 79, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Broadwell-E (Core i3)" }, + { 6, 15, -1, -1, 79, 2, -1, -1, NC, CORE_|_I_|_5 , 0, "Broadwell-E (Core i5)" }, + { 6, 15, -1, -1, 79, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Broadwell-E (Core i5)" }, + { 6, 15, -1, -1, 79, 2, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell-E (Core i7)" }, + { 6, 15, -1, -1, 79, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Broadwell-E (Core i7)" }, + + /* Skylake CPUs (14nm): */ + { 6, 14, -1, -1, 94, -1, -1, -1, NC, XEON_ , 0, "Skylake (Xeon)" }, + { 6, 14, -1, -1, 94, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Skylake (Core i7)" }, + { 6, 14, -1, -1, 94, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Skylake (Core i5)" }, + { 6, 14, -1, -1, 94, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Skylake (Core i3)" }, + { 6, 14, -1, -1, 94, 2, -1, -1, NC, PENTIUM_ , 0, "Skylake (Pentium)" }, + { 6, 14, -1, -1, 78, 2, -1, -1, NC, PENTIUM_ , 0, "Skylake (Pentium)" }, + { 6, 14, -1, -1, 94, 2, -1, -1, NC, CELERON_ , 0, "Skylake (Celeron)" }, + { 6, 14, -1, -1, 78, 2, -1, -1, NC, CELERON_ , 0, "Skylake (Celeron)" }, + { 6, 14, -1, -1, 78, 2, -1, -1, NC, CORE_|_M_|_7 , 0, "Skylake (Core m7)" }, + { 6, 14, -1, -1, 78, 2, -1, -1, NC, CORE_|_M_|_5 , 0, "Skylake (Core m5)" }, + { 6, 14, -1, -1, 78, 2, -1, -1, NC, CORE_|_M_|_3 , 0, "Skylake (Core m3)" }, + + /* Kaby Lake CPUs (14nm): */ + { 6, 14, -1, -1, 158, 4, -1, -1, NC, CORE_|_I_|_7 , 0, "Kaby Lake (Core i7)" }, + { 6, 14, -1, -1, 158, 4, -1, -1, NC, CORE_|_I_|_5 , 0, "Kaby Lake (Core i5)" }, + { 6, 14, -1, -1, 158, 2, -1, -1, NC, CORE_|_I_|_3 , 0, "Kaby Lake (Core i3)" }, + { 6, 14, -1, -1, 158, 2, -1, -1, NC, PENTIUM_ , 0, "Kaby Lake (Pentium)" }, + { 6, 14, -1, -1, 158, 2, -1, -1, NC, CELERON_ , 0, "Kaby Lake (Celeron)" }, + { 6, 14, -1, -1, 158, 2, -1, -1, NC, CORE_|_M_|_3 , 0, "Kaby Lake (Core m3)" }, + + /* Itaniums */ + { 7, -1, -1, -1, -1, 1, -1, -1, NC, 0 , 0, "Itanium" }, + { 15, -1, -1, 16, -1, 1, -1, -1, NC, 0 , 0, "Itanium 2" }, +}; + + +static void load_intel_features(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + const struct feature_map_t matchtable_edx1[] = { + { 18, CPU_FEATURE_PN }, + { 21, CPU_FEATURE_DTS }, + { 22, CPU_FEATURE_ACPI }, + { 27, CPU_FEATURE_SS }, + { 29, CPU_FEATURE_TM }, + { 30, CPU_FEATURE_IA64 }, + { 31, CPU_FEATURE_PBE }, + }; + const struct feature_map_t matchtable_ecx1[] = { + { 2, CPU_FEATURE_DTS64 }, + { 4, CPU_FEATURE_DS_CPL }, + { 5, CPU_FEATURE_VMX }, + { 6, CPU_FEATURE_SMX }, + { 7, CPU_FEATURE_EST }, + { 8, CPU_FEATURE_TM2 }, + { 10, CPU_FEATURE_CID }, + { 14, CPU_FEATURE_XTPR }, + { 15, CPU_FEATURE_PDCM }, + { 18, CPU_FEATURE_DCA }, + { 21, CPU_FEATURE_X2APIC }, + }; + const struct feature_map_t matchtable_edx81[] = { + { 20, CPU_FEATURE_XD }, + }; + const struct feature_map_t matchtable_ebx7[] = { + { 2, CPU_FEATURE_SGX }, + { 4, CPU_FEATURE_HLE }, + { 11, CPU_FEATURE_RTM }, + { 16, CPU_FEATURE_AVX512F }, + { 17, CPU_FEATURE_AVX512DQ }, + { 18, CPU_FEATURE_RDSEED }, + { 19, CPU_FEATURE_ADX }, + { 26, CPU_FEATURE_AVX512PF }, + { 27, CPU_FEATURE_AVX512ER }, + { 28, CPU_FEATURE_AVX512CD }, + { 29, CPU_FEATURE_SHA_NI }, + { 30, CPU_FEATURE_AVX512BW }, + { 31, CPU_FEATURE_AVX512VL }, + }; + if (raw->basic_cpuid[0][0] >= 1) { + match_features(matchtable_edx1, COUNT_OF(matchtable_edx1), raw->basic_cpuid[1][3], data); + match_features(matchtable_ecx1, COUNT_OF(matchtable_ecx1), raw->basic_cpuid[1][2], data); + } + if (raw->ext_cpuid[0][0] >= 1) { + match_features(matchtable_edx81, COUNT_OF(matchtable_edx81), raw->ext_cpuid[1][3], data); + } + // detect TSX/AVX512: + if (raw->basic_cpuid[0][0] >= 7) { + match_features(matchtable_ebx7, COUNT_OF(matchtable_ebx7), raw->basic_cpuid[7][1], data); + } +} + +enum _cache_type_t { + L1I, + L1D, + L2, + L3, + L4 +}; +typedef enum _cache_type_t cache_type_t; + +static void check_case(uint8_t on, cache_type_t cache, int size, int assoc, int linesize, struct cpu_id_t* data) +{ + if (!on) return; + switch (cache) { + case L1I: + data->l1_instruction_cache = size; + break; + case L1D: + data->l1_data_cache = size; + data->l1_assoc = assoc; + data->l1_cacheline = linesize; + break; + case L2: + data->l2_cache = size; + data->l2_assoc = assoc; + data->l2_cacheline = linesize; + break; + case L3: + data->l3_cache = size; + data->l3_assoc = assoc; + data->l3_cacheline = linesize; + break; + case L4: + data->l4_cache = size; + data->l4_assoc = assoc; + data->l4_cacheline = linesize; + break; + default: + break; + } +} + +static void decode_intel_oldstyle_cache_info(struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + uint8_t f[256] = {0}; + int reg, off; + uint32_t x; + for (reg = 0; reg < 4; reg++) { + x = raw->basic_cpuid[2][reg]; + if (x & 0x80000000) continue; + for (off = 0; off < 4; off++) { + f[x & 0xff] = 1; + x >>= 8; + } + } + + check_case(f[0x06], L1I, 8, 4, 32, data); + check_case(f[0x08], L1I, 16, 4, 32, data); + check_case(f[0x0A], L1D, 8, 2, 32, data); + check_case(f[0x0C], L1D, 16, 4, 32, data); + check_case(f[0x22], L3, 512, 4, 64, data); + check_case(f[0x23], L3, 1024, 8, 64, data); + check_case(f[0x25], L3, 2048, 8, 64, data); + check_case(f[0x29], L3, 4096, 8, 64, data); + check_case(f[0x2C], L1D, 32, 8, 64, data); + check_case(f[0x30], L1I, 32, 8, 64, data); + check_case(f[0x39], L2, 128, 4, 64, data); + check_case(f[0x3A], L2, 192, 6, 64, data); + check_case(f[0x3B], L2, 128, 2, 64, data); + check_case(f[0x3C], L2, 256, 4, 64, data); + check_case(f[0x3D], L2, 384, 6, 64, data); + check_case(f[0x3E], L2, 512, 4, 64, data); + check_case(f[0x41], L2, 128, 4, 32, data); + check_case(f[0x42], L2, 256, 4, 32, data); + check_case(f[0x43], L2, 512, 4, 32, data); + check_case(f[0x44], L2, 1024, 4, 32, data); + check_case(f[0x45], L2, 2048, 4, 32, data); + check_case(f[0x46], L3, 4096, 4, 64, data); + check_case(f[0x47], L3, 8192, 8, 64, data); + check_case(f[0x4A], L3, 6144, 12, 64, data); + check_case(f[0x4B], L3, 8192, 16, 64, data); + check_case(f[0x4C], L3, 12288, 12, 64, data); + check_case(f[0x4D], L3, 16384, 16, 64, data); + check_case(f[0x4E], L2, 6144, 24, 64, data); + check_case(f[0x60], L1D, 16, 8, 64, data); + check_case(f[0x66], L1D, 8, 4, 64, data); + check_case(f[0x67], L1D, 16, 4, 64, data); + check_case(f[0x68], L1D, 32, 4, 64, data); + /* The following four entries are trace cache. Intel does not + * specify a cache-line size, so we use -1 instead + */ + check_case(f[0x70], L1I, 12, 8, -1, data); + check_case(f[0x71], L1I, 16, 8, -1, data); + check_case(f[0x72], L1I, 32, 8, -1, data); + check_case(f[0x73], L1I, 64, 8, -1, data); + + check_case(f[0x78], L2, 1024, 4, 64, data); + check_case(f[0x79], L2, 128, 8, 64, data); + check_case(f[0x7A], L2, 256, 8, 64, data); + check_case(f[0x7B], L2, 512, 8, 64, data); + check_case(f[0x7C], L2, 1024, 8, 64, data); + check_case(f[0x7D], L2, 2048, 8, 64, data); + check_case(f[0x7F], L2, 512, 2, 64, data); + check_case(f[0x82], L2, 256, 8, 32, data); + check_case(f[0x83], L2, 512, 8, 32, data); + check_case(f[0x84], L2, 1024, 8, 32, data); + check_case(f[0x85], L2, 2048, 8, 32, data); + check_case(f[0x86], L2, 512, 4, 64, data); + check_case(f[0x87], L2, 1024, 8, 64, data); + + if (f[0x49]) { + /* This flag is overloaded with two meanings. On Xeon MP + * (family 0xf, model 0x6) this means L3 cache. On all other + * CPUs (notably Conroe et al), this is L2 cache. In both cases + * it means 4MB, 16-way associative, 64-byte line size. + */ + if (data->family == 0xf && data->model == 0x6) { + data->l3_cache = 4096; + data->l3_assoc = 16; + data->l3_cacheline = 64; + } else { + data->l2_cache = 4096; + data->l2_assoc = 16; + data->l2_cacheline = 64; + } + } + if (f[0x40]) { + /* Again, a special flag. It means: + * 1) If no L2 is specified, then CPU is w/o L2 (0 KB) + * 2) If L2 is specified by other flags, then, CPU is w/o L3. + */ + if (data->l2_cache == -1) { + data->l2_cache = 0; + } else { + data->l3_cache = 0; + } + } +} + +static void decode_intel_deterministic_cache_info(struct cpu_raw_data_t* raw, + struct cpu_id_t* data) +{ + int ecx; + int ways, partitions, linesize, sets, size, level, typenumber; + cache_type_t type; + for (ecx = 0; ecx < MAX_INTELFN4_LEVEL; ecx++) { + typenumber = raw->intel_fn4[ecx][0] & 0x1f; + if (typenumber == 0) break; + level = (raw->intel_fn4[ecx][0] >> 5) & 0x7; + if (level == 1 && typenumber == 1) + type = L1D; + else if (level == 1 && typenumber == 2) + type = L1I; + else if (level == 2 && typenumber == 3) + type = L2; + else if (level == 3 && typenumber == 3) + type = L3; + else if (level == 4 && typenumber == 3) + type = L4; + else { + warnf("deterministic_cache: unknown level/typenumber combo (%d/%d), cannot\n", level, typenumber); + warnf("deterministic_cache: recognize cache type\n"); + continue; + } + ways = ((raw->intel_fn4[ecx][1] >> 22) & 0x3ff) + 1; + partitions = ((raw->intel_fn4[ecx][1] >> 12) & 0x3ff) + 1; + linesize = (raw->intel_fn4[ecx][1] & 0xfff) + 1; + sets = raw->intel_fn4[ecx][2] + 1; + size = ways * partitions * linesize * sets / 1024; + check_case(1, type, size, ways, linesize, data); + } +} + +static int decode_intel_extended_topology(struct cpu_raw_data_t* raw, + struct cpu_id_t* data) +{ + int i, level_type, num_smt = -1, num_core = -1; + for (i = 0; i < MAX_INTELFN11_LEVEL; i++) { + level_type = (raw->intel_fn11[i][2] & 0xff00) >> 8; + switch (level_type) { + case 0x01: + num_smt = raw->intel_fn11[i][1] & 0xffff; + break; + case 0x02: + num_core = raw->intel_fn11[i][1] & 0xffff; + break; + default: + break; + } + } + if (num_smt == -1 || num_core == -1) return 0; + data->num_logical_cpus = num_core; + data->num_cores = num_core / num_smt; + // make sure num_cores is at least 1. In VMs, the CPUID instruction + // is rigged and may give nonsensical results, but we should at least + // avoid outputs like data->num_cores == 0. + if (data->num_cores <= 0) data->num_cores = 1; + return 1; +} + +static void decode_intel_number_of_cores(struct cpu_raw_data_t* raw, + struct cpu_id_t* data) +{ + int logical_cpus = -1, num_cores = -1; + + if (raw->basic_cpuid[0][0] >= 11) { + if (decode_intel_extended_topology(raw, data)) return; + } + + if (raw->basic_cpuid[0][0] >= 1) { + logical_cpus = (raw->basic_cpuid[1][1] >> 16) & 0xff; + if (raw->basic_cpuid[0][0] >= 4) { + num_cores = 1 + ((raw->basic_cpuid[4][0] >> 26) & 0x3f); + } + } + if (data->flags[CPU_FEATURE_HT]) { + if (num_cores > 1) { + data->num_cores = num_cores; + data->num_logical_cpus = logical_cpus; + } else { + data->num_cores = 1; + data->num_logical_cpus = (logical_cpus >= 1 ? logical_cpus : 1); + if (data->num_logical_cpus == 1) + data->flags[CPU_FEATURE_HT] = 0; + } + } else { + data->num_cores = data->num_logical_cpus = 1; + } +} + +static intel_code_and_bits_t get_brand_code_and_bits(struct cpu_id_t* data) +{ + intel_code_t code = (intel_code_t) NC; + intel_code_and_bits_t result; + uint64_t bits = 0; + int i = 0; + const char* bs = data->brand_str; + const char* s; + const struct { intel_code_t c; const char *search; } matchtable[] = { + { PENTIUM_M, "Pentium(R) M" }, + { CORE_SOLO, "Pentium(R) Dual CPU" }, + { CORE_SOLO, "Pentium(R) Dual-Core" }, + { PENTIUM_D, "Pentium(R) D" }, + { CORE_SOLO, "Genuine Intel(R) CPU" }, + { CORE_SOLO, "Intel(R) Core(TM)" }, + { DIAMONDVILLE, "CPU [N ][23]## " }, + { SILVERTHORNE, "CPU Z" }, + { PINEVIEW, "CPU [ND][45]## " }, + { CEDARVIEW, "CPU [ND]#### " }, + }; + + const struct { uint64_t bit; const char* search; } bit_matchtable[] = { + { XEON_, "Xeon" }, + { _MP, " MP" }, + { ATOM_, "Atom(TM) CPU" }, + { MOBILE_, "Mobile" }, + { CELERON_, "Celeron" }, + { PENTIUM_, "Pentium" }, + }; + + for (i = 0; i < COUNT_OF(bit_matchtable); i++) { + if (match_pattern(bs, bit_matchtable[i].search)) + bits |= bit_matchtable[i].bit; + } + + if ((i = match_pattern(bs, "Core(TM) [im][357]")) != 0) { + bits |= CORE_; + i--; + switch (bs[i + 9]) { + case 'i': bits |= _I_; break; + case 'm': bits |= _M_; break; + } + switch (bs[i + 10]) { + case '3': bits |= _3; break; + case '5': bits |= _5; break; + case '7': bits |= _7; break; + } + } + for (i = 0; i < COUNT_OF(matchtable); i++) + if (match_pattern(bs, matchtable[i].search)) { + code = matchtable[i].c; + break; + } + debugf(2, "intel matchtable result is %d\n", code); + if (bits & XEON_) { + if (match_pattern(bs, "W35##") || match_pattern(bs, "[ELXW]75##")) + bits |= _7; + else if (match_pattern(bs, "[ELXW]55##")) + code = GAINESTOWN; + else if (match_pattern(bs, "[ELXW]56##")) + code = WESTMERE; + else if (data->l3_cache > 0 && data->family == 16) + /* restrict by family, since later Xeons also have L3 ... */ + code = IRWIN; + } + if (match_all(bits, XEON_ + _MP) && data->l3_cache > 0) + code = POTOMAC; + if (code == CORE_SOLO) { + s = strstr(bs, "CPU"); + if (s) { + s += 3; + while (*s == ' ') s++; + if (*s == 'T') + bits |= MOBILE_; + } + } + if (code == CORE_SOLO) { + switch (data->num_cores) { + case 1: break; + case 2: + { + code = CORE_DUO; + if (data->num_logical_cpus > 2) + code = DUAL_CORE_HT; + break; + } + case 4: + { + code = QUAD_CORE; + if (data->num_logical_cpus > 4) + code = QUAD_CORE_HT; + break; + } + default: + code = MORE_THAN_QUADCORE; break; + } + } + + if (code == CORE_DUO && (bits & MOBILE_) && data->model != 14) { + if (data->ext_model < 23) { + code = MEROM; + } else { + code = PENRYN; + } + } + if (data->ext_model == 23 && + (code == CORE_DUO || code == PENTIUM_D || (bits & CELERON_))) { + code = WOLFDALE; + } + + result.code = code; + result.bits = bits; + return result; +} + +static intel_model_t get_model_code(struct cpu_id_t* data) +{ + int i = 0; + int l = (int) strlen(data->brand_str); + const char *bs = data->brand_str; + int mod_flags = 0, model_no = 0, ndigs = 0; + /* If the CPU is a Core ix, then just return the model number generation: */ + if ((i = match_pattern(bs, "Core(TM) i[357]")) != 0) { + i += 11; + if (i + 4 >= l) return UNKNOWN; + if (bs[i] == '2') return _2xxx; + if (bs[i] == '3') return _3xxx; + return UNKNOWN; + } + + /* For Core2-based Xeons: */ + while (i < l - 3) { + if (bs[i] == 'C' && bs[i+1] == 'P' && bs[i+2] == 'U') + break; + i++; + } + if (i >= l - 3) return UNKNOWN; + i += 3; + while (i < l - 4 && bs[i] == ' ') i++; + if (i >= l - 4) return UNKNOWN; + while (i < l - 4 && !isdigit(bs[i])) { + if (bs[i] >= 'A' && bs[i] <= 'Z') + mod_flags |= (1 << (bs[i] - 'A')); + i++; + } + if (i >= l - 4) return UNKNOWN; + while (isdigit(bs[i])) { + ndigs++; + model_no = model_no * 10 + (int) (bs[i] - '0'); + i++; + } + if (ndigs != 4) return UNKNOWN; +#define HAVE(ch, flags) ((flags & (1 << ((int)(ch-'A')))) != 0) + switch (model_no / 100) { + case 30: return _3000; + case 31: return _3100; + case 32: + { + return (HAVE('X', mod_flags)) ? X3200 : _3200; + } + case 33: + { + return (HAVE('X', mod_flags)) ? X3300 : _3300; + } + case 51: return _5100; + case 52: return _5200; + case 53: return _5300; + case 54: return _5400; + default: + return UNKNOWN; + } +#undef HAVE +} + +static void decode_intel_sgx_features(const struct cpu_raw_data_t* raw, struct cpu_id_t* data) +{ + struct cpu_epc_t epc; + int i; + + if (raw->basic_cpuid[0][0] < 0x12) return; // no 12h leaf + if (raw->basic_cpuid[0x12][0] == 0) return; // no sub-leafs available, probably it's disabled by BIOS + + // decode sub-leaf 0: + if (raw->basic_cpuid[0x12][0] & 1) data->sgx.flags[INTEL_SGX1] = 1; + if (raw->basic_cpuid[0x12][0] & 2) data->sgx.flags[INTEL_SGX2] = 1; + if (data->sgx.flags[INTEL_SGX1] || data->sgx.flags[INTEL_SGX2]) + data->sgx.present = 1; + data->sgx.misc_select = raw->basic_cpuid[0x12][1]; + data->sgx.max_enclave_32bit = (raw->basic_cpuid[0x12][3] ) & 0xff; + data->sgx.max_enclave_64bit = (raw->basic_cpuid[0x12][3] >> 8) & 0xff; + + // decode sub-leaf 1: + data->sgx.secs_attributes = raw->intel_fn12h[1][0] | (((uint64_t) raw->intel_fn12h[1][1]) << 32); + data->sgx.secs_xfrm = raw->intel_fn12h[1][2] | (((uint64_t) raw->intel_fn12h[1][3]) << 32); + + // decode higher-order subleafs, whenever present: + data->sgx.num_epc_sections = -1; + for (i = 0; i < 1000000; i++) { + epc = cpuid_get_epc(i, raw); + if (epc.length == 0) { + debugf(2, "SGX: epc section request for %d returned null, no more EPC sections.\n", i); + data->sgx.num_epc_sections = i; + break; + } + } + if (data->sgx.num_epc_sections == -1) { + debugf(1, "SGX: warning: seems to be infinitude of EPC sections.\n"); + data->sgx.num_epc_sections = 1000000; + } +} + +struct cpu_epc_t cpuid_get_epc(int index, const struct cpu_raw_data_t* raw) +{ + uint32_t regs[4]; + struct cpu_epc_t retval = {0, 0}; + if (raw && index < MAX_INTELFN12H_LEVEL - 2) { + // this was queried already, use the data: + memcpy(regs, raw->intel_fn12h[2 + index], sizeof(regs)); + } else { + // query this ourselves: + regs[0] = 0x12; + regs[2] = 2 + index; + regs[1] = regs[3] = 0; + cpu_exec_cpuid_ext(regs); + } + + // decode values: + if ((regs[0] & 0xf) == 0x1) { + retval.start_addr |= (regs[0] & 0xfffff000); // bits [12, 32) -> bits [12, 32) + retval.start_addr |= ((uint64_t) (regs[1] & 0x000fffff)) << 32; // bits [0, 20) -> bits [32, 52) + retval.length |= (regs[2] & 0xfffff000); // bits [12, 32) -> bits [12, 32) + retval.length |= ((uint64_t) (regs[3] & 0x000fffff)) << 32; // bits [0, 20) -> bits [32, 52) + } + return retval; +} + +int cpuid_identify_intel(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal) +{ + intel_code_and_bits_t brand; + intel_model_t model_code; + int i; + char* brand_code_str = NULL; + + load_intel_features(raw, data); + if (raw->basic_cpuid[0][0] >= 4) { + /* Deterministic way is preferred, being more generic */ + decode_intel_deterministic_cache_info(raw, data); + } else if (raw->basic_cpuid[0][0] >= 2) { + decode_intel_oldstyle_cache_info(raw, data); + } + decode_intel_number_of_cores(raw, data); + + brand = get_brand_code_and_bits(data); + model_code = get_model_code(data); + for (i = 0; i < COUNT_OF(intel_bcode_str); i++) { + if (brand.code == intel_bcode_str[i].code) { + brand_code_str = intel_bcode_str[i].str; + break; + } + } + if (brand_code_str) + debugf(2, "Detected Intel brand code: %d (%s)\n", brand.code, brand_code_str); + else + debugf(2, "Detected Intel brand code: %d\n", brand.code); + if (brand.bits) { + debugf(2, "Detected Intel bits: "); + debug_print_lbits(2, brand.bits); + } + debugf(2, "Detected Intel model code: %d\n", model_code); + + internal->code.intel = brand.code; + internal->bits = brand.bits; + + if (data->flags[CPU_FEATURE_SGX]) { + debugf(2, "SGX seems to be present, decoding...\n"); + // if SGX is indicated by the CPU, verify its presence: + decode_intel_sgx_features(raw, data); + } + + internal->score = match_cpu_codename(cpudb_intel, COUNT_OF(cpudb_intel), data, + brand.code, brand.bits, model_code); + return 0; +} + +void cpuid_get_list_intel(struct cpu_list_t* list) +{ + generic_get_cpu_list(cpudb_intel, COUNT_OF(cpudb_intel), list); +} diff --git a/compat/libcpuid/recog_intel.h b/compat/libcpuid/recog_intel.h new file mode 100644 index 000000000..b99c783bf --- /dev/null +++ b/compat/libcpuid/recog_intel.h @@ -0,0 +1,32 @@ +/* + * Copyright 2008 Veselin Georgiev, + * anrieffNOSPAM @ mgail_DOT.com (convert to gmail) + * + * 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 AUTHOR ``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 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. + */ +#ifndef __RECOG_INTEL_H__ +#define __RECOG_INTEL_H__ + +int cpuid_identify_intel(struct cpu_raw_data_t* raw, struct cpu_id_t* data, struct internal_id_info_t* internal); +void cpuid_get_list_intel(struct cpu_list_t* list); + +#endif /*__RECOG_INTEL_H__*/