From: Rusty Russell Date: Fri, 27 Sep 2013 18:40:40 +0000 (+0930) Subject: Merge branch 'cpuid' of https://github.com/decltype/ccan X-Git-Url: https://git.ozlabs.org/?a=commitdiff_plain;h=659efe73b8aa2cf479a8e13f81752b47d1154afc;hp=6affde3b132bc51f5096fede31732d765c6f5684;p=ccan Merge branch 'cpuid' of https://github.com/decltype/ccan --- diff --git a/Makefile-ccan b/Makefile-ccan index d81031ea..86d6ac0c 100644 --- a/Makefile-ccan +++ b/Makefile-ccan @@ -42,6 +42,7 @@ MODS_WITH_SRC := antithread \ ciniparser \ crc \ crcsync \ + cpuid \ daemonize \ daemon_with_notify \ dgraph \ diff --git a/ccan/cpuid/BSD-MIT b/ccan/cpuid/BSD-MIT new file mode 120000 index 00000000..2354d129 --- /dev/null +++ b/ccan/cpuid/BSD-MIT @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/cpuid/_info b/ccan/cpuid/_info new file mode 100644 index 00000000..1603b4be --- /dev/null +++ b/ccan/cpuid/_info @@ -0,0 +1,43 @@ +#include "cpuid.h" + +/** + * cpuid - a CPUID instruction parser for x86/x86_64 CPUs. + * + * This module tries to keep-it-simple to get information about the CPU + * from the CPU. + * + * Example: + * #include + * + * int main() + * { + * int highest; + * cpuid(CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED, &highest); + * printf ("Highest extended function supported: %d\n", highest); + * + * return 0; + * } + * + * Author: Ahmed Samy + * License: MIT + * Version: 0.1 + */ + +int main(int argc, char *argv[]) +{ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { +#if defined(__i386__) || defined(__i386) || defined(__x86_64) \ + || defined(_M_AMD64) || defined(__M_X64) + /* Nothing. */ +#else + printf("ccan/build_assert\n"); +#endif + return 0; + } + + return 1; +} + diff --git a/ccan/cpuid/cpuid.c b/ccan/cpuid/cpuid.c new file mode 100644 index 00000000..6133e13f --- /dev/null +++ b/ccan/cpuid/cpuid.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2013 Ahmed Samy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This file has been written with some help from wikipedia: + * http://en.wikipedia.org/wiki/CPUID + */ + +/* Only compile this file if we're on a x86 machine. */ +#if defined(__i386__) || defined(__i386) || defined(__x86_64) \ + || defined(_M_AMD64) || defined(__M_X64) +#include "cpuid.h" + +#include + +enum { + CPU_PROC_BRAND_STRING_INTERNAL0 = 0x80000003, + CPU_PROC_BRAND_STRING_INTERNAL1 = 0x80000004 +}; + +#ifndef _MSC_VER +static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) +{ + __asm__( + "xchg %%ebx, %%edi\n\t" /* 32bit PIC: Don't clobber ebx. */ + "cpuid\n\t" + "xchg %%ebx, %%edi\n\t" + : "=a"(*eax), "=D"(*ebx), "=c"(*ecx), "=d"(*edx) + : "0" (info) + ); +} +#else +#include + +static void ___cpuid(cpuid_t info, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) +{ + uint32_t registers[4]; + __cpuid(registers, info); + + *eax = registers[0]; + *ebx = registers[1]; + *ecx = registers[2]; + *edx = registers[3]; +} +#endif + +static struct { + uint32_t feature; + uint32_t mask; + bool use_edx; /* ecx will be used if false. */ +} features[] = { + { CF_MMX, 1 << 23, true }, + { CF_SSE, 1 << 25, true }, + { CF_SSE2, 1 << 26, true }, + { CF_SSE3, 1 << 9, false }, + { CF_FPU, 1 << 0, true }, + + { CF_TSC, 1 << 4, true }, + { CF_MSR, 1 << 5, true }, + + { CF_SSSE3, 1 << 9, false }, + { CF_AVX, 1 << 28, false }, + + /* Extended ones. */ + { CEF_x64, 1 << 30, true }, + { CEF_FPU, 1 << 0, true }, + { CEF_DE, 1 << 2, true }, + { CEF_SYSCALLRET, 1 << 11, true }, + { CEF_CMOV, 1 << 15, true }, + + { CEF_SSE4a, 1 << 6, false }, + { CEF_FMA4, 1 << 16, false }, + { CEF_XOP, 1 << 11, false } +}; + +static bool has_feature(int feature, uint32_t ecx, uint32_t edx) +{ + int i; + + for (i = 0; i < sizeof(features) / sizeof(features[0]); ++i) { + if (features[i].feature == feature) { + if (features[i].use_edx) + return (edx & features[i].mask); + else + return (ecx & features[i].mask); + } + } + + return false; +} + +bool cpuid_is_supported(void) +{ + int ret = 0; + + asm volatile( + "pushfl\n\t" + "popl %%eax\n\t" + "movl %%eax, %%ecx\n\t" + "xorl $0x200000, %%eax\n\t" + "pushl %%eax\n\t" + "popfl\n\t" + + "pushfl\n\t" + "popl %%eax\n\t" + "xorl %%ecx, %%eax\n\t" + "shrl $21, %%eax\n\t" + "andl $1, %%eax\n\t" + "pushl %%ecx\n\t" + "popfl\n\t" + + : "=a" (ret) + ); + + return !!ret; +} + +bool cpuid_test_feature(cpuid_t feature) +{ + if (feature > CPU_VIRT_PHYS_ADDR_SIZES || feature < CPU_EXTENDED_PROC_INFO_FEATURE_BITS) + return false; + + return (feature <= cpuid_highest_ext_func_supported()); +} + +bool cpuid_has_feature(int feature, bool extended) +{ + uint32_t eax, ebx, ecx, edx; + + if (!extended) + ___cpuid(CPU_PROCINFO_AND_FEATUREBITS, &eax, &ebx, &ecx, &edx); + else + ___cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, &eax, &ebx, &ecx, &edx); + + return has_feature(feature, ecx, edx); +} + +static const char *const cpuids[] = { + "Nooooooooone", + "AMDisbetter!", + "AuthenticAMD", + "CentaurHauls", + "CyrixInstead", + "GenuineIntel", + "TransmetaCPU", + "GeniuneTMx86", + "Geode by NSC", + "NexGenDriven", + "RiseRiseRise", + "SiS SiS SiS ", + "UMC UMC UMC ", + "VIA VIA VIA ", + "Vortex86 SoC", + "KVMKVMKVMKVM" +}; + +cputype_t cpuid_get_cpu_type(void) +{ + static cputype_t cputype; + if (cputype == CT_NONE) { + union { + char buf[12]; + uint32_t bufu32[3]; + } u; + uint32_t i; + + ___cpuid(CPU_VENDORID, &i, &u.bufu32[0], &u.bufu32[2], &u.bufu32[1]); + u.buf[12] = '\0'; + + for (i = 0; i < sizeof(cpuids) / sizeof(cpuids[0]); ++i) { + if (strncmp(cpuids[i], u.buf, 12) == 0) { + cputype = (cputype_t)i; + break; + } + } + } + + return cputype; +} + +const char *cpuid_get_cpu_type_string(const cputype_t cputype) +{ + return cpuids[(int)cputype]; +} + +uint32_t cpuid_highest_ext_func_supported(void) +{ + static uint32_t highest; + + if (!highest) { + asm volatile( + "cpuid\n\t" + : "=a" (highest) + : "a" (CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) + ); + } + + return highest; +} + +void cpuid(cpuid_t info, uint32_t *buf) +{ + /* Sanity checks, make sure we're not trying to do something + * invalid or we are trying to get information that isn't supported + * by the CPU. */ + if (info > CPU_VIRT_PHYS_ADDR_SIZES || (info > CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED + && !cpuid_test_feature(info))) + return; + + if (info == CPU_PROC_BRAND_STRING) { + ___cpuid(CPU_PROC_BRAND_STRING, &buf[0], &buf[1], &buf[2], &buf[3]); + ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL0, &buf[4], &buf[5], &buf[6], &buf[7]); + ___cpuid(CPU_PROC_BRAND_STRING_INTERNAL1, &buf[8], &buf[9], &buf[10], &buf[11]); + return; + } else if (info == CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED) { + *buf = cpuid_highest_ext_func_supported(); + return; + } + + uint32_t eax, ebx, ecx, edx; + ___cpuid(info, &eax, &ebx, &ecx, &edx); + + switch (info) { + case CPU_VENDORID: + buf[0] = ebx; + buf[1] = edx; + buf[2] = ecx; + break; + case CPU_PROCINFO_AND_FEATUREBITS: + buf[0] = eax; /* The so called "signature" of the CPU. */ + buf[1] = edx; /* Feature flags #1. */ + buf[2] = ecx; /* Feature flags #2. */ + buf[3] = ebx; /* Additional feature information. */ + break; + case CPU_CACHE_AND_TLBD_INFO: + buf[0] = eax; + buf[1] = ebx; + buf[2] = ecx; + buf[3] = edx; + break; + case CPU_EXTENDED_PROC_INFO_FEATURE_BITS: + buf[0] = edx; + buf[1] = ecx; + break; + case CPU_L1_CACHE_AND_TLB_IDS: + buf[0] = eax; + buf[1] = ebx; + buf[2] = ecx; + buf[3] = edx; + break; + case CPU_EXTENDED_L2_CACHE_FEATURES: + *buf = ecx; + break; + case CPU_ADV_POWER_MGT_INFO: + *buf = edx; + break; + case CPU_VIRT_PHYS_ADDR_SIZES: + *buf = eax; + break; + default: + *buf = 0xbaadf00d; + break; + } +} + +#endif + diff --git a/ccan/cpuid/cpuid.h b/ccan/cpuid/cpuid.h new file mode 100644 index 00000000..305d69aa --- /dev/null +++ b/ccan/cpuid/cpuid.h @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2013 Ahmed Samy + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef CCAN_CPUID_H +#define CCAN_CPUID_H + +#include +#include + +/** + * enum cpuid - stuff to get information on from the CPU. + * + * This is used as a parameter in cpuid(). + * + * CPU_VENDORID: + * The CPU's Vendor ID. + * + * CPU_PROCINFO_AND_FEATUREBITS: + * Processor information and feature bits (SSE, etc.). + * + * CPU_CACHE_AND_TLBD_INFO + * Cache and TLBD Information. + * + * CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED: + * Highest extended function supported address. + * Can be like 0x80000008. + * + * CPU_EXTENDED_PROC_INFO_FEATURE_BITS: + * Extended processor information and feature bits (64bit etc.) + * + * CPU_PROC_BRAND_STRING: + * The Processor's brand string. + * + * CPU_L1_CACHE_AND_TLB_IDS: + * L1 Cache and TLB Identifications. + * + * CPU_EXTENDED_L2_CACHE_FEATURES: + * Extended L2 Cache features. + * + * CPU_ADV_POWER_MGT_INFO: + * Advaned power management information. + * + * CPU_VIRT_PHYS_ADDR_SIZES: + * Virtual and physical address sizes. + */ + +typedef enum cpuid { + CPU_VENDORID = 0, + CPU_PROCINFO_AND_FEATUREBITS = 1, + CPU_CACHE_AND_TLBD_INFO = 2, + + CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED = 0x80000000, + CPU_EXTENDED_PROC_INFO_FEATURE_BITS = 0x80000001, + CPU_PROC_BRAND_STRING = 0x80000002, + CPU_L1_CACHE_AND_TLB_IDS = 0x80000005, + CPU_EXTENDED_L2_CACHE_FEATURES = 0x80000006, + CPU_ADV_POWER_MGT_INFO = 0x80000007, + CPU_VIRT_PHYS_ADDR_SIZES = 0x80000008 +} cpuid_t; + +#define CF_MMX 0 +#define CF_SSE 1 +#define CF_SSE2 2 +#define CF_SSE3 3 +#define CF_FPU 4 +#define CF_TSC 5 +#define CF_MSR 6 +#define CF_SSSE3 7 +#define CF_AVX 8 +#define CF_FMA 9 + +#define CEF_x64 10 +#define CEF_FPU 11 +#define CEF_DE 12 +#define CEF_SYSCALLRET 13 +#define CEF_CMOV 14 +#define CEF_SSE4a 15 +#define CEF_FMA4 16 +#define CEF_XOP 17 + +typedef enum cputype { + CT_NONE, + CT_AMDK5, + CT_AMD, + CT_CENTAUR, + CT_CYRIX, + CT_INTEL, + CT_TRANSMETA, + CT_NATIONAL_SEMICONDUCTOR, + CT_NEXGEN, + CT_RISE, + CT_SIS, + CT_UMC, + CT_VIA, + CT_VORTEX, + CT_KVM +} cputype_t; + +#if defined(__i386__) || defined(__i386) || defined(__x86_64) \ + || defined(_M_AMD64) || defined(__M_X64) + +/** + * cpuid_get_cpu_type - Get CPU Type + * + * Returns the CPU Type as cputype_t. + * + * See also: cpuid_get_cpu_type_string() + */ +cputype_t cpuid_get_cpu_type(void); + +/** + * cpuid_get_cpu_type_string - Get CPU Type string + * + * Returns the CPU type string based off cputype_t. + */ +const char *cpuid_get_cpu_type_string(const cputype_t cputype); + +/** + * cpuid_is_supported - test if the CPUID instruction is supported + * + * CPUID is not supported by old CPUS. + * + * Returns true if the cpuid instruction is supported, false otherwise. + * + * See also: cpuid() + */ +bool cpuid_is_supported(void); + +/** + * cpuid_highest_ext_func_supported - Get the highest extended function supported + * + * + * Returns the highest extended function supported. + * + * This is the same as calling: + * cpuid(CPU_HIGHEST_EEXTENDED_FUNCTION_SUPPORTED, &highest); + * + * This is made visible to the linker because it's easier to call it + * instead of calling cpuid with less type-checking. cpuid calls this. + * + * See also: cpuid() + */ +uint32_t cpuid_highest_ext_func_supported(void); + +/** + * cpuid - Get Some information from the CPU. + * + * This function expects buf to be a valid pointer to a string/int/... + * depending on the requested information. + * + * For CPU_VENDOR_ID: + * Returns a string into buf. + * + * For CPU_PROCINFO_AND_FEATUREBITS: + * buf[0]: + * - 3:0 - Stepping + * - 7:4 - Model + * - 11:8 - Family + * - 13:12 - Processor Type + * - 19:16 - Extended Model + * - 27:20 - Extended family + * buf[1] and buf[2]: + * Feature flags + * buf[3]: + * Additional feature information. + * + * For CPU_L1_CACHE_AND_TLB_IDS: + * buf[0]: (eax): + * - 7..0 Number of times to exec cpuid to get all descriptors. + * - 15..8 Instruction TLB: 4K Pages, 4-way set associtive, 128 entries. + * - 23..16 Data TLB: 4k Pages, 4-way set associtive, 128 entries. + * - 24..31 Instruction TLB: 4K Pages, 4-way set associtive, 2 entries. + * buf[1]: (ebx): + * - 7..0 64-byte prefetching + * - 8..31 Null descriptor + * buf[2]: (ecx): + * - 0..31 Null descriptor + * buf[3]: (edx): + * - 7..0 2nd-level cache, 2M, 8-way set associtive, 64-byte line size + * - 15..8 1st-level instruction cache: 32K, 8-way set associtive, 64 byte line size + * - 16..23 Data TLB: 4M Pages, 4-way set associtive, 8 entires. + * - 24..31 1st-level data cache: 32K, 8-way set associtive, 64 byte line size + * + * For CPU_HIGHEST_EXTENDED_FUNCTION_SUPPORTED: + * Returns the highest supported function in *buf (expects an integer ofc) + * + * For CPU_EXTENDED_PROC_INFO_FEATURE_BITS: + * Returns them in buf[0] and buf[1]. + * + * For CPU_VIRT_PHYS_ADDR_SIZES: + * Returns it as an integer in *buf. + * + * For CPU_PROC_BRAND_STRING: + * Have a char array with at least 48 bytes assigned to it. + * + * Here's a page which will help you parse the data provided by this function. + * http://www.flounder.com/cpuid_explorer2.htm + * + * If an invalid flag has been passed a 0xbaadf00d is returned in *buf. + */ +void cpuid(cpuid_t info, uint32_t *buf); + +/** + * cpuid_test_feature - Test if @feature is available + * + * Returns true if feature is supported, false otherwise. + * + * The feature parameter must be >= CPU_EXTENDED_PROC_INFO_FEATURE_BITS + * and <= CPU_VIRT_PHYS_ADDR_SIZES. + */ +bool cpuid_test_feature(cpuid_t feature); + +/** + * cpuid_has_feature - Test if @feature is supported + * + * Test if the CPU supports MMX/SSE* etc. + * For the extended parameter, usually you want to pass it as + * false if you're not passing CEF_*. + * + * For more information about the CPU extended features, have a look + * at: + * http://en.wikipedia.org/wiki/CPUID + * + * Returns true if the feature is available, false otherwise. + */ +#define cpuid_has_mmx() cpuid_has_feature(CF_MMX, false) +#define cpuid_has_sse() cpuid_has_feature(CF_SSE, false) +#define cpuid_has_sse2() cpuid_has_feature(CF_SSE2, false) +#define cpuid_has_sse3() cpuid_has_feature(CF_SSE3, false) +#define cpuid_has_ssse3() cpuid_has_feature(CF_SSSE3, false) +#define cpuid_has_avx() cpuid_has_feature(CF_AVX, false) +#define cpuid_has_fma() cpuid_has_feature(CF_FMA, false) +#define cpuid_has_x64() cpuid_has_feature(CEF_x64, true) +#define cpuid_has_sse4a() cpuid_has_feature(CEF_SSE4a, true) +#define cpuid_has_fma4() cpuid_has_feature(CEF_FMA4, true) +#define cpuid_has_xop() cpuid_has_feature(CEF_XOP, true) +bool cpuid_has_feature(int feature, bool extended); + +#else +#include + +#define cpuid_get_cpu_type() BUILD_ASSERT_OR_ZERO(0) +#define cpuid_get_cpu_type_string() BUILD_ASSERT_OR_ZERO(0) + +#define cpuid_is_supported() BUILD_ASSERT_OR_ZERO(0) +#define cpuid(info, buf) BUILD_ASSERT_OR_ZERO(0) + +#define cpuid_highest_ext_func_supported() BUILD_ASSERT_OR_ZERO(0) +#define cpuid_test_feature(feature) BUILD_ASSERT_OR_ZERO(0) +#define cpuid_has_feature(feature, ext) BUILD_ASSERT_OR_ZERO(0) + +#endif +#endif + diff --git a/ccan/cpuid/test/run.c b/ccan/cpuid/test/run.c new file mode 100644 index 00000000..c96f3ab8 --- /dev/null +++ b/ccan/cpuid/test/run.c @@ -0,0 +1,56 @@ +#include "cpuid.h" + +#include +#include + +int main() +{ + if (!cpuid_is_supported()) { + printf ("CPUID instruction is not supported by this CPU\n"); + return 1; + } + + printf ("Vendor ID: %s\n", cpuid_get_cpu_type_string (cpuid_get_cpu_type ())); + + char buf[48]; + cpuid(CPU_PROC_BRAND_STRING, (uint32_t *)buf); + printf ("Processor Brand: %s\n", buf); + + printf ("Highest extended function supported: %#010x\n", cpuid_highest_ext_func_supported()); + + union { + struct { + uint32_t phys_bits : 8; + uint32_t virt_bits : 8; + uint32_t reserved : 16; + }; + uint32_t w; + } s; + cpuid(CPU_VIRT_PHYS_ADDR_SIZES, &s.w); + printf ("Physical address size: %d\nVirtual: %d\n", s.phys_bits, s.virt_bits); + + uint32_t extfeatures[2]; + cpuid(CPU_EXTENDED_PROC_INFO_FEATURE_BITS, extfeatures); + printf ("Extended processor info and feature bits: %d %d\n", extfeatures[0], extfeatures[1]); + + union { + struct { + uint32_t line_size : 8; + uint32_t reserved : 4; + uint32_t assoc : 4; + uint32_t cache_size : 16; + }; + + uint32_t w; + } l2c; + + cpuid(CPU_EXTENDED_L2_CACHE_FEATURES, &l2c.w); + printf ("L2 Cache Size: %u KB\tLine Size: %u bytes\tAssociativity: %02xh\n", + l2c.cache_size, l2c.line_size, l2c.assoc); + + uint32_t invalid; + cpuid(0x0ffffffUL, &invalid); + printf ("Testing invalid: %#010x\n", invalid); + return 0; +} +