diff options
Diffstat (limited to 'js/src/jit/FlushICache.cpp')
-rw-r--r-- | js/src/jit/FlushICache.cpp | 132 |
1 files changed, 132 insertions, 0 deletions
diff --git a/js/src/jit/FlushICache.cpp b/js/src/jit/FlushICache.cpp new file mode 100644 index 0000000000..1e2ec69272 --- /dev/null +++ b/js/src/jit/FlushICache.cpp @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "jit/FlushICache.h" + +#ifdef JS_CODEGEN_ARM64 +# include "jit/arm64/vixl/MozCachingDecoder.h" +# include "jit/arm64/vixl/Simulator-vixl.h" +#endif + +#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) + +# ifdef __linux__ +# include <linux/version.h> +# define LINUX_HAS_MEMBARRIER (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)) +# else +# define LINUX_HAS_MEMBARRIER 0 +# endif + +# if LINUX_HAS_MEMBARRIER || defined(__android__) +# include <string.h> + +# if LINUX_HAS_MEMBARRIER +# include <linux/membarrier.h> +# include <sys/syscall.h> +# include <sys/utsname.h> +# include <unistd.h> +# elif defined(__android__) +# include <sys/syscall.h> +# include <unistd.h> +# else +# error "Missing platform-specific declarations for membarrier syscall!" +# endif // __linux__ / ANDROID + +static int membarrier(int cmd, int flags) { + return syscall(__NR_membarrier, cmd, flags); +} + +// These definitions come from the Linux kernel source, for kernels before 4.16 +// which didn't have access to these membarrier commands. +# ifndef MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE +# define MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE (1 << 5) +# endif + +# ifndef MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE +# define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE (1 << 6) +# endif +# endif // LINUX_HAS_MEMBARRIER || defined(__android__) + +using namespace js; +using namespace js::jit; + +namespace js { +namespace jit { + +bool CanFlushExecutionContextForAllThreads() { +# if (LINUX_HAS_MEMBARRIER || defined(__android__)) + // On linux, check the kernel supports membarrier(2), that is, it's a kernel + // above Linux 4.16 included. + // + // Note: this code has been extracted (August 2020) from + // https://android.googlesource.com/platform/art/+/58520dfba31d6eeef75f5babff15e09aa28e5db8/libartbase/base/membarrier.cc#50 + static constexpr int kRequiredMajor = 4; + static constexpr int kRequiredMinor = 16; + + static bool computed = false; + static bool kernelHasMembarrier = false; + + if (computed) { + return kernelHasMembarrier; + } + + struct utsname uts; + int major, minor; + kernelHasMembarrier = uname(&uts) == 0 && strcmp(uts.sysname, "Linux") == 0 && + sscanf(uts.release, "%d.%d", &major, &minor) == 2 && + major >= kRequiredMajor && + (major != kRequiredMajor || minor >= kRequiredMinor); + + // As a test bed, try to run the syscall with the command registering the + // intent to use the actual membarrier we'll want to carry out later. + // + // IMPORTANT: This is required or else running the membarrier later won't + // actually interrupt the threads in this process. + if (kernelHasMembarrier && + membarrier(MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE, 0) != 0) { + kernelHasMembarrier = false; + } + + computed = true; + return kernelHasMembarrier; +# else + // On other platforms, we assume that the syscall for flushing the icache + // will flush the execution context for other cores. + return true; +# endif +} + +void FlushExecutionContextForAllThreads() { + // Callers must check that this operation is available. + MOZ_RELEASE_ASSERT(CanFlushExecutionContextForAllThreads()); + +# if defined(JS_SIMULATOR_ARM64) && defined(JS_CACHE_SIMULATOR_ARM64) + // Emulate what the real hardware would do by emitting a membarrier that'll + // interrupt and flush the execution context of all threads. + using js::jit::SimulatorProcess; + js::jit::AutoLockSimulatorCache alsc; + SimulatorProcess::membarrier(); +# elif (LINUX_HAS_MEMBARRIER || defined(__android__)) + // The caller has checked this can be performed, which will have registered + // this process to receive the membarrier. See above. + // + // membarrier will trigger an inter-processor-interrupt on any active threads + // of this process. This is an execution context synchronization event + // equivalent to running an `isb` instruction. + if (membarrier(MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE, 0) != 0) { + // Better safe than sorry. + MOZ_CRASH("membarrier can't be executed"); + } +# else + // On other platforms, we assume that the syscall for flushing the icache + // will flush the execution context for other cores. +# endif +} + +} // namespace jit +} // namespace js + +#endif |