diff options
author | Jack Lloyd <[email protected]> | 2017-01-15 16:24:34 -0500 |
---|---|---|
committer | Jack Lloyd <[email protected]> | 2017-01-15 16:24:34 -0500 |
commit | 81b53d285826e2e2b67d03c2d32241f8cce99fff (patch) | |
tree | 90660899ccd6403af96c5808fd40f0a860d889b7 | |
parent | 7eb382e4af1978841176326ed213a7a46e3cb2ad (diff) |
Add OS::run_cpu_instruction_probe
Needed for ARM detection, also probably useful on PowerPC for AltiVec
Works fine for me on Linux x86-64 testing with ud2 instruction.
-rw-r--r-- | src/lib/utils/os_utils.cpp | 70 | ||||
-rw-r--r-- | src/lib/utils/os_utils.h | 50 |
2 files changed, 107 insertions, 13 deletions
diff --git a/src/lib/utils/os_utils.cpp b/src/lib/utils/os_utils.cpp index 46ce2a056..233a3991c 100644 --- a/src/lib/utils/os_utils.cpp +++ b/src/lib/utils/os_utils.cpp @@ -17,6 +17,8 @@ #include <sys/mman.h> #include <sys/resource.h> #include <unistd.h> + #include <signal.h> + #include <setjmp.h> #endif #if defined(BOTAN_TARGET_OS_IS_WINDOWS) || defined(BOTAN_TARGET_OS_IS_MINGW) @@ -298,6 +300,74 @@ void free_locked_pages(void* ptr, size_t length) #endif } +#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) +namespace { + +static ::sigjmp_buf g_sigill_jmp_buf; + +void botan_sigill_handler(int) + { + ::siglongjmp(g_sigill_jmp_buf, /*non-zero return value*/1); + } + +} +#endif + +int run_cpu_instruction_probe(std::function<int ()> probe_fn) + { +#if defined(BOTAN_TARGET_OS_TYPE_IS_UNIX) + struct sigaction old_sigaction; + struct sigaction sigaction; + + sigaction.sa_handler = botan_sigill_handler; + ::sigemptyset(&sigaction.sa_mask); + sigaction.sa_flags = 0; + + int rc = ::sigaction(SIGILL, &sigaction, &old_sigaction); + + 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); + + if(rc == 0) + { + // first call to sigsetjmp + probe_result = probe_fn(); + } + else if(rc == 1) + { + // non-local return from siglongjmp in signal handler: return error + probe_result = -1; + } + else + throw Exception("run_cpu_instruction_probe unexpected sigsetjmp return value"); + } + catch(...) + { + probe_result = -2; + } + + 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 + } + + } } diff --git a/src/lib/utils/os_utils.h b/src/lib/utils/os_utils.h index 213c5982b..b06a72265 100644 --- a/src/lib/utils/os_utils.h +++ b/src/lib/utils/os_utils.h @@ -1,6 +1,6 @@ /* * OS specific utility functions -* (C) 2015,2016 Jack Lloyd +* (C) 2015,2016,2017 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -15,18 +15,21 @@ namespace Botan { namespace OS { /** -* Returns the OS assigned process ID, if available. Otherwise throws. +* @return process ID assigned by the operating system. +* On Unix and Windows systems, this always returns a result +* On IncludeOS it returns 0 since there is no process ID to speak of +* in a unikernel. */ uint32_t get_process_id(); /** -* Return the highest resolution clock available on the system. +* @return highest resolution clock available on the system. * * The epoch and update rate of this clock is arbitrary and depending * on the hardware it may not tick at a constant rate. * -* Returns the value of the hardware cycle counter, if available. -* On Windows calls QueryPerformanceCounter. +* 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. @@ -35,30 +38,51 @@ uint32_t get_process_id(); uint64_t get_processor_timestamp(); /** -* Returns the value of the system clock with best resolution available, -* normalized to nanoseconds resolution. +* @return system clock with best resolution available, normalized to +* nanoseconds resolution. */ uint64_t get_system_timestamp_ns(); -/* -* Returns the maximum amount of memory (in bytes) we could/should -* hyptothetically allocate. Reads "BOTAN_MLOCK_POOL_SIZE" from -* environment which can be set to zero. +/** +* @return maximum amount of memory (in bytes) Botan could/should +* hyptothetically allocate for the memory poool. Reads environment +* variable "BOTAN_MLOCK_POOL_SIZE", set to "0" to disable pool. */ size_t get_memory_locking_limit(); -/* +/** * Request so many bytes of page-aligned RAM locked into memory using * mlock, VirtualLock, or similar. Returns null on failure. The memory * returned is zeroed. Free it with free_locked_pages. +* @param length requested allocation in bytes */ void* allocate_locked_pages(size_t length); -/* +/** * Free memory allocated by allocate_locked_pages +* @param ptr a pointer returned by allocate_locked_pages +* @param length length passed to allocate_locked_pages */ void free_locked_pages(void* ptr, size_t length); +/** +* Run a probe instruction to test for support for a CPU instruction. +* Runs in system-specific env that catches illegal instructions; this +* function always fails if the OS doesn't provide this. +* Returns value of probe_fn, if it could run. +* If error occurs, returns negative number. +* This allows probe_fn to indicate errors of its own, if it wants. +* For example the instruction might not only be only available on some +* CPUs, but also buggy on some subset of these - the probe function +* can test to make sure the instruction works properly before +* indicating that the instruction is available. +* +* Return codes: +* -1 illegal instruction detected +* -2 exception thrown +*/ +int run_cpu_instruction_probe(std::function<int ()> probe_fn); + } } |