aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-01-15 16:24:34 -0500
committerJack Lloyd <[email protected]>2017-01-15 16:24:34 -0500
commit81b53d285826e2e2b67d03c2d32241f8cce99fff (patch)
tree90660899ccd6403af96c5808fd40f0a860d889b7
parent7eb382e4af1978841176326ed213a7a46e3cb2ad (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.cpp70
-rw-r--r--src/lib/utils/os_utils.h50
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);
+
}
}