summaryrefslogtreecommitdiffstats
path: root/js/src/jit/FlushICache.cpp
blob: 1e2ec69272fe884f61b0e529887ed633a900bf3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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