From 5c254480e0e134335eb3a57d15884d126a789129 Mon Sep 17 00:00:00 2001 From: Pekka Riikonen Date: Sat, 1 Nov 2008 11:29:52 +0200 Subject: [PATCH] Added SILC CPUID API The API can be used to determine the features of the x86 and x86-64 CPUs on the fly. --- lib/silcutil/Makefile.ad | 6 +- lib/silcutil/silccpuid.c | 123 ++++++++++++++++++++++++++++ lib/silcutil/silccpuid.h | 75 +++++++++++++++++ lib/silcutil/silcruntime.h.in | 1 + lib/silcutil/silctypes.h | 15 +++- lib/silcutil/tests/Makefile.am | 4 +- lib/silcutil/tests/test_silccpuid.c | 26 ++++++ scripts/release | 4 +- 8 files changed, 244 insertions(+), 10 deletions(-) create mode 100644 lib/silcutil/silccpuid.c create mode 100644 lib/silcutil/silccpuid.h create mode 100644 lib/silcutil/tests/test_silccpuid.c diff --git a/lib/silcutil/Makefile.ad b/lib/silcutil/Makefile.ad index 9ddeb19e..1fd44962 100644 --- a/lib/silcutil/Makefile.ad +++ b/lib/silcutil/Makefile.ad @@ -71,7 +71,8 @@ libsilcutil_la_SOURCES = \ silcbufferstream.c \ silclocalnetstream.c \ silcxml.c \ - silcavltree.c + silcavltree.c \ + silccpuid.c include_HEADERS = \ $(SILC_DIST_HEADER) \ @@ -131,7 +132,8 @@ include_HEADERS = \ silclocalnetstream.h \ silcxml.h \ silctree.h \ - silctree_i.h + silctree_i.h \ + silccpuid.h SILC_EXTRA_DIST = diff --git a/lib/silcutil/silccpuid.c b/lib/silcutil/silccpuid.c new file mode 100644 index 00000000..d969c5e2 --- /dev/null +++ b/lib/silcutil/silccpuid.c @@ -0,0 +1,123 @@ +/* + + silccpuid.c + + Author: Pekka Riikonen + + Copyright (C) 2008 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#include + +#if defined(SILC_I486) || defined(SILC_X86_64) +static void silc_cpuid(SilcUInt32 level, SilcUInt32 *ecx, SilcUInt32 *edx) +{ + /* This works on modern CPUs. Probably fails on some ancient CPUs. */ + asm volatile ("xorl %%ecx, %%ecx \n" + "xorl %%edx, %%edx \n" + "movl %2, %%eax \n" + "cpuid \n" + "movl %%edx, %0 \n" + "movl %%ecx, %1 \n" + : "=m" (*edx), "=m" (*ecx) + : "r" (level) + : "ebx", "eax", "ecx", "edx"); + + SILC_LOG_DEBUG(("CPUID 0x%08x: ecx=0x%08x, edx=0x%08x", level, *ecx, *edx)); +} +#endif /* SILC_I486 || SILC_X86_64 */ + +SilcCPUIdFeatures silc_cpuid_features(void) +{ + SilcCPUIdFeatures id = SILC_CPUID_UNKNOWN; +#if defined(SILC_I486) || defined(SILC_X86_64) + SilcUInt32 ecx = 0, edx = 0; + + /* Basic level */ + silc_cpuid(1, &ecx, &edx); + if (!edx) + return SILC_CPUID_UNKNOWN; + + SILC_LOG_DEBUG(("386")); + id = SILC_CPUID_386; + + if (edx & (1 << 23)) { + SILC_LOG_DEBUG(("MMX")); + id |= SILC_CPUID_MMX; + } + + if (edx & (1 << 15)) { + SILC_LOG_DEBUG(("CMOV")); + id |= SILC_CPUID_CMOV; + } + + if (edx & (1 << 25)) { + SILC_LOG_DEBUG(("SSE")); + id |= SILC_CPUID_SSE; + } + + if (edx & (1 << 26)) { + SILC_LOG_DEBUG(("SSE2")); + id |= SILC_CPUID_SSE2; + } + + if (ecx & (1 << 0)) { + SILC_LOG_DEBUG(("SSE3")); + id |= SILC_CPUID_SSE3; + } + + if (ecx & (1 << 9)) { + SILC_LOG_DEBUG(("SSSE3")); + id |= SILC_CPUID_SSSE3; + } + + if (ecx & (1 << 19)) { + SILC_LOG_DEBUG(("SSE4.1")); + id |= SILC_CPUID_SSE41; + } + + if (ecx & (1 << 20)) { + SILC_LOG_DEBUG(("SSE4.2")); + id |= SILC_CPUID_SSE41; + } + + if (ecx & (1 << 28)) { + SILC_LOG_DEBUG(("AVX")); + id |= SILC_CPUID_AVX; + } + + if (ecx & (1 << 25)) { + SILC_LOG_DEBUG(("AES")); + id |= SILC_CPUID_AES; + } + + /* Extended level */ + ecx = edx = 0; + silc_cpuid(0x80000001, &ecx, &edx); + if (!ecx) + goto out; + + if (ecx & (1 << 6)) { + SILC_LOG_DEBUG(("SSE4a")); + id |= SILC_CPUID_SSE4A; + } + + if (ecx & (1 << 25)) { + SILC_LOG_DEBUG(("SSE5")); + id |= SILC_CPUID_SSE5; + } + + out: +#endif /* SILC_I486 || SILC_X86_64 */ + return id; +} diff --git a/lib/silcutil/silccpuid.h b/lib/silcutil/silccpuid.h new file mode 100644 index 00000000..b5114d34 --- /dev/null +++ b/lib/silcutil/silccpuid.h @@ -0,0 +1,75 @@ +/* + + silccpuid.h + + Author: Pekka Riikonen + + Copyright (C) 2008 Pekka Riikonen + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + +*/ + +#ifndef SILCCPUID_H +#define SILCCPUID_H + +/****d* silcutil/SilcCPUIdFeatures + * + * NAME + * + * typedef enum { ... } SilcCPUIdFeatures; + * + * DESCRIPTION + * + * CPU features. Indicates which instruction sets the CPU supports. + * + */ +typedef enum { + SILC_CPUID_UNKNOWN = 0x00000000, /* 386 only */ + SILC_CPUID_386 = 0x00000002, /* 386 */ + SILC_CPUID_MMX = 0x00000004, /* MMX */ + SILC_CPUID_CMOV = 0x00000008, /* CMOV, FCOMI */ + SILC_CPUID_SSE = 0x00000010, /* SSE */ + SILC_CPUID_SSE2 = 0x00000020, /* SSE2 */ + SILC_CPUID_SSE3 = 0x00000040, /* SSE3 */ + SILC_CPUID_SSSE3 = 0x00000080, /* SSSE3 */ + SILC_CPUID_SSE41 = 0x00000100, /* SSE4.1 */ + SILC_CPUID_SSE42 = 0x00000200, /* SSE4.2 */ + SILC_CPUID_SSE4A = 0x00000400, /* SSE4a */ + SILC_CPUID_SSE5 = 0x00000800, /* SSE5 */ + SILC_CPUID_AVX = 0x00001000, /* AVX */ + SILC_CPUID_AES = 0x00002000, /* AES */ +} SilcCPUIdFeatures; +/***/ + +/****f* silcutil/silc_cpuid_features + * + * SYNOPSIS + * + * SilcCPUIdFeatures silc_cpuid_features(void); + * + * DESCRIPTION + * + * Returns the CPU instruction set features. The ID can be used to + * determine what features the CPU supports. The returned value is a + * bitmask of supported features. + * + * NOTES + * + * This function should not be called at a code path where speed is + * critical. It is recommended to call this once and later use the + * returned value to determine features. + * + * This function works only on x86 or x86-64 CPUs. + * + ***/ +SilcCPUIdFeatures silc_cpuid_features(void); + +#endif /* SILCCPUID_H */ diff --git a/lib/silcutil/silcruntime.h.in b/lib/silcutil/silcruntime.h.in index 6d20103c..1a8e3e7e 100644 --- a/lib/silcutil/silcruntime.h.in +++ b/lib/silcutil/silcruntime.h.in @@ -200,6 +200,7 @@ extern "C" { /* SILC Runtime Toolkit includes */ #include +#include #include #include #include diff --git a/lib/silcutil/silctypes.h b/lib/silcutil/silctypes.h index 5d768d81..802f8113 100644 --- a/lib/silcutil/silctypes.h +++ b/lib/silcutil/silctypes.h @@ -163,14 +163,14 @@ typedef signed short SilcInt16; * * SOURCE */ -#if SILC_SIZEOF_LONG == 4 -typedef unsigned long SilcUInt32; -typedef signed long SilcInt32; -#else #if SILC_SIZEOF_INT == 4 typedef unsigned int SilcUInt32; typedef signed int SilcInt32; #else +#if SILC_SIZEOF_LONG == 4 +typedef unsigned long SilcUInt32; +typedef signed long SilcInt32; +#else #if SILC_SIZEOF_LONG_LONG >= 4 #ifndef WIN32 typedef unsigned long long SilcUInt32; @@ -1325,4 +1325,11 @@ void silc_prefetch_block(void *addr, #undef SILC_PREFETCH_ASM } +typedef char __check_size1[sizeof(SilcInt8) == 1 ? 1 : -1]; +typedef char __check_size2[sizeof(SilcUInt8) == 1 ? 1 : -1]; +typedef char __check_size3[sizeof(SilcInt16) == 2 ? 1 : -1]; +typedef char __check_size4[sizeof(SilcUInt16) == 2 ? 1 : -1]; +typedef char __check_size5[sizeof(SilcInt32) == 4 ? 1 : -1]; +typedef char __check_size6[sizeof(SilcUInt32) == 4 ? 1 : -1]; + #endif /* SILCTYPES_H */ diff --git a/lib/silcutil/tests/Makefile.am b/lib/silcutil/tests/Makefile.am index 77aa4b7a..b7519c61 100644 --- a/lib/silcutil/tests/Makefile.am +++ b/lib/silcutil/tests/Makefile.am @@ -25,7 +25,7 @@ check_PROGRAMS = \ test_silcdll test_silcenv test_silctimer test_silcbitops \ test_silcregex test_silcbuffmt test_silcdir test_silcthreadqueue \ test_silcrand test_silcglobal test_silcbufferstream test_silcxml \ - test_silclocalnetstream test_silctree + test_silclocalnetstream test_silctree test_silccpuid TESTS = test_silcstrutil test_silcstringprep test_silchashtable \ test_silclist test_silcfsm test_silcasync test_silcschedule \ @@ -34,7 +34,7 @@ TESTS = test_silcstrutil test_silcstringprep test_silchashtable \ test_silcdll test_silcenv test_silctimer test_silcbitops \ test_silcregex test_silcbuffmt test_silcdir test_silcthreadqueue \ test_silcrand test_silcglobal test_silcbufferstream \ - test_silclocalnetstream test_silctree + test_silclocalnetstream test_silctree test_silccpuid LIBS = $(SILC_COMMON_LIBS) LDADD = -L.. -L../.. -lsrt diff --git a/lib/silcutil/tests/test_silccpuid.c b/lib/silcutil/tests/test_silccpuid.c new file mode 100644 index 00000000..8e4da50e --- /dev/null +++ b/lib/silcutil/tests/test_silccpuid.c @@ -0,0 +1,26 @@ +/* CPUID tests */ + +#include "silcruntime.h" + +int main(int argc, char **argv) +{ + SilcBool success = FALSE; + SilcCPUIdFeatures id; + + if (argc > 1 && !strcmp(argv[1], "-d")) { + silc_log_debug(TRUE); + silc_log_quick(TRUE); + silc_log_debug_hexdump(TRUE); + silc_log_set_debug_string("*cpu*"); + } + + id = silc_cpuid_features(); + + success = TRUE; + +// err: + SILC_LOG_DEBUG(("Testing was %s", success ? "SUCCESS" : "FAILURE")); + fprintf(stderr, "Testing was %s\n", success ? "SUCCESS" : "FAILURE"); + + return !success; +} diff --git a/scripts/release b/scripts/release index b5ab3fb3..55e592e2 100644 --- a/scripts/release +++ b/scripts/release @@ -2,7 +2,7 @@ last="$1" new="$2" -git tag $new -git log --no-merges $new ^$last > ChangeLog +git tag -a -m "Release $new" $new +git log --no-merges $new ^$last >> ChangeLog echo "" >> ChangeLog git diff --stat --summary -M $last $new >> ChangeLog -- 2.24.0