aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/utils
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-01-31 20:07:06 -0500
committerJack Lloyd <[email protected]>2017-01-31 20:07:06 -0500
commitae8d2bd859120839ee08bc35daebda7ef4971ea6 (patch)
treecd1a8db67e062854ccd7e9211707b13ee757f64d /src/lib/utils
parente934e03821ab28dc2573ff52da49a3b700d4cfb7 (diff)
parent328491ce573582058a4ebdef74d687cbd1466c44 (diff)
Merge GH #860 Support CPU instruction probe on Windows, add OS::get_high_resolution_clock
Diffstat (limited to 'src/lib/utils')
-rw-r--r--src/lib/utils/os_utils.cpp83
-rw-r--r--src/lib/utils/os_utils.h28
2 files changed, 74 insertions, 37 deletions
diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp
index c6d99237c..fc401c3c1 100644
--- a/src/lib/utils/os_utils.cpp
+++ b/src/lib/utils/os_utils.cpp
@@ -28,9 +28,7 @@
namespace Botan {
-namespace OS {
-
-uint32_t get_process_id()
+uint32_t OS::get_process_id()
{
#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
return ::getpid();
@@ -43,14 +41,16 @@ uint32_t get_process_id()
#endif
}
-uint64_t get_processor_timestamp()
+uint64_t OS::get_processor_timestamp()
{
#if defined(BOTAN_TARGET_OS_HAS_QUERY_PERF_COUNTER)
LARGE_INTEGER tv;
::QueryPerformanceCounter(&tv);
return tv.QuadPart;
-#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
+#elif defined(BOTAN_USE_GCC_INLINE_ASM)
+
+#if defined(BOTAN_TARGET_CPU_IS_X86_FAMILY)
if(CPUID::has_rdtsc()) // not available on all x86 CPUs
{
uint32_t rtc_low = 0, rtc_high = 0;
@@ -58,7 +58,7 @@ uint64_t get_processor_timestamp()
return (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
}
-#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_PPC64)
+#elif defined(BOTAN_TARGET_ARCH_IS_PPC64)
uint32_t rtc_low = 0, rtc_high = 0;
asm volatile("mftbu %0; mftb %1" : "=r" (rtc_high), "=r" (rtc_low));
@@ -71,33 +71,46 @@ uint64_t get_processor_timestamp()
return (static_cast<uint64_t>(rtc_high) << 32) | rtc_low;
}
-#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_ALPHA)
+#elif defined(BOTAN_TARGET_ARCH_IS_ALPHA)
uint64_t rtc = 0;
asm volatile("rpcc %0" : "=r" (rtc));
return rtc;
// OpenBSD does not trap access to the %tick register
-#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
+#elif defined(BOTAN_TARGET_ARCH_IS_SPARC64) && !defined(BOTAN_TARGET_OS_IS_OPENBSD)
uint64_t rtc = 0;
asm volatile("rd %%tick, %0" : "=r" (rtc));
return rtc;
-#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_IA64)
+#elif defined(BOTAN_TARGET_ARCH_IS_IA64)
uint64_t rtc = 0;
asm volatile("mov %0=ar.itc" : "=r" (rtc));
return rtc;
-#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_S390X)
+#elif defined(BOTAN_TARGET_ARCH_IS_S390X)
uint64_t rtc = 0;
asm volatile("stck 0(%0)" : : "a" (&rtc) : "memory", "cc");
return rtc;
-#elif defined(BOTAN_USE_GCC_INLINE_ASM) && defined(BOTAN_TARGET_ARCH_IS_HPPA)
+#elif defined(BOTAN_TARGET_ARCH_IS_HPPA)
uint64_t rtc = 0;
asm volatile("mfctl 16,%0" : "=r" (rtc)); // 64-bit only?
return rtc;
+
+#else
+ //#warning "OS::get_processor_timestamp not implemented"
#endif
+#endif
+
+ return 0;
+ }
+
+uint64_t OS::get_high_resolution_clock()
+ {
+ if(uint64_t cpu_clock = OS::get_processor_timestamp())
+ return cpu_clock;
+
/*
If we got here either we either don't have an asm instruction
above, or (for x86) RDTSC is not available at runtime. Try some
@@ -141,7 +154,7 @@ uint64_t get_processor_timestamp()
return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
}
-uint64_t get_system_timestamp_ns()
+uint64_t OS::get_system_timestamp_ns()
{
#if defined(BOTAN_TARGET_OS_HAS_CLOCK_GETTIME)
struct timespec ts;
@@ -155,7 +168,7 @@ uint64_t get_system_timestamp_ns()
return std::chrono::duration_cast<std::chrono::nanoseconds>(now).count();
}
-size_t get_memory_locking_limit()
+size_t OS::get_memory_locking_limit()
{
#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
/*
@@ -241,7 +254,7 @@ size_t get_memory_locking_limit()
return 0;
}
-void* allocate_locked_pages(size_t length)
+void* OS::allocate_locked_pages(size_t length)
{
#if defined(BOTAN_TARGET_OS_HAS_POSIX_MLOCK)
@@ -298,7 +311,7 @@ void* allocate_locked_pages(size_t length)
#endif
}
-void free_locked_pages(void* ptr, size_t length)
+void OS::free_locked_pages(void* ptr, size_t length)
{
if(ptr == nullptr || length == 0)
return;
@@ -330,8 +343,10 @@ void botan_sigill_handler(int)
}
#endif
-int run_cpu_instruction_probe(std::function<int ()> probe_fn)
+int OS::run_cpu_instruction_probe(std::function<int ()> probe_fn)
{
+ int probe_result = -3;
+
#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX)
struct sigaction old_sigaction;
struct sigaction sigaction;
@@ -345,12 +360,6 @@ int run_cpu_instruction_probe(std::function<int ()> probe_fn)
if(rc != 0)
throw Exception("run_cpu_instruction_probe sigaction failed");
- /*
- There doesn't seem to be any way for probe_result to not be initialized
- by some code path below, but this initializer is left as error just in case.
- */
- int probe_result = -3;
-
try
{
rc = ::sigsetjmp(g_sigill_jmp_buf, /*save sigs*/1);
@@ -373,18 +382,34 @@ int run_cpu_instruction_probe(std::function<int ()> probe_fn)
probe_result = -2;
}
+ // Restore old SIGILL handler, if any
rc = ::sigaction(SIGILL, &old_sigaction, nullptr);
if(rc != 0)
throw Exception("run_cpu_instruction_probe sigaction restore failed");
- return probe_result;
-#else
- // TODO: Windows support
- return -9; // not supported
-#endif
- }
+#elif defined(BOTAN_TARGET_OS_IS_WINDOWS) && defined(BOTAN_TARGET_COMPILER_IS_MSVC)
+
+ // Windows SEH
+ __try
+ {
+ try
+ {
+ probe_result = probe_fn();
+ }
+ catch(...)
+ {
+ probe_result = -2;
+ }
+ }
+ __except(::GetExceptionCode() == EXCEPTION_ILLEGAL_INSTRUCTION ?
+ EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
+ {
+ probe_result = -1;
+ }
+#endif
-}
+ return probe_result;
+ }
}
diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h
index 60ef2cf5b..a1693bcc5 100644
--- a/src/lib/utils/os_utils.h
+++ b/src/lib/utils/os_utils.h
@@ -32,23 +32,31 @@ namespace OS {
uint32_t BOTAN_DLL get_process_id();
/**
-* @return highest resolution clock available on the system.
+* @return CPU processor clock, if available
+*
+* On Windows, calls QueryPerformanceCounter.
+*
+* Under GCC or Clang on supported platforms the hardware cycle counter is queried.
+* Currently supported processors are x86, PPC, Alpha, SPARC, IA-64, S/390x, and HP-PA.
+* If no CPU cycle counter is available on this system, returns zero.
+*/
+uint64_t BOTAN_DLL get_processor_timestamp();
+
+/*
+* @return best resolution timestamp available
*
* The epoch and update rate of this clock is arbitrary and depending
* on the hardware it may not tick at a constant rate.
*
* Uses hardware cycle counter, if available.
-* On Windows, always calls QueryPerformanceCounter.
-* Under GCC or Clang on supported platforms the hardware cycle counter is queried:
-* x86, PPC, Alpha, SPARC, IA-64, S/390x, and HP-PA
-* On other platforms clock_gettime is used with some monotonic timer, if available.
+* On POSIX platforms clock_gettime is used with a monotonic timer
* As a final fallback std::chrono::high_resolution_clock is used.
*/
-uint64_t BOTAN_DLL get_processor_timestamp();
+uint64_t BOTAN_DLL get_high_resolution_clock();
/**
-* @return system clock with best resolution available, normalized to
-* nanoseconds resolution.
+* @return system clock (reflecting wall clock) with best resolution
+* available, normalized to nanoseconds resolution.
*/
uint64_t BOTAN_DLL get_system_timestamp_ns();
@@ -86,6 +94,10 @@ void free_locked_pages(void* ptr, size_t length);
* can test to make sure the instruction works properly before
* indicating that the instruction is available.
*
+* @warning on Unix systems uses signal handling in a way that is not
+* thread safe. It should only be called in a single-threaded context
+* (ie, at static init time).
+*
* Return codes:
* -1 illegal instruction detected
* -2 exception thrown