1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <type_traits>
18
19extern "C" {
20
21#include "qurt.h"
22
23}  // extern "C"
24
25#include "ash/debug.h"
26
27#include "chre/core/event_loop.h"
28#include "chre/core/event_loop_manager.h"
29#include "chre/core/init.h"
30#include "chre/core/static_nanoapps.h"
31#include "chre/platform/fatal_error.h"
32#include "chre/platform/log.h"
33#include "chre/platform/memory.h"
34#include "chre/platform/mutex.h"
35#include "chre/platform/shared/platform_log.h"
36#include "chre/platform/slpi/fastrpc.h"
37#include "chre/platform/slpi/preloaded_nanoapps.h"
38#include "chre/platform/slpi/uimg_util.h"
39#include "chre/util/lock_guard.h"
40
41using chre::EventLoop;
42using chre::EventLoopManagerSingleton;
43using chre::LockGuard;
44using chre::Mutex;
45using chre::UniquePtr;
46
47extern "C" int chre_slpi_stop_thread(void);
48
49// Qualcomm-defined function needed to indicate that the CHRE thread may call
50// dlopen() (without it, the thread will deadlock when calling dlopen()). Not in
51// any header file in the SLPI tree or Hexagon SDK (3.0), so declaring here.
52// Returns 0 to indicate success.
53extern "C" int HAP_thread_migrate(qurt_thread_t thread);
54
55namespace {
56
57//! Size of the stack for the CHRE thread, in bytes.
58constexpr size_t kStackSize = (8 * 1024);
59
60//! Memory partition where the thread control block (TCB) should be stored,
61//! which controls micro-image support.
62//! @see qurt_thread_attr_set_tcb_partition
63constexpr unsigned char kTcbPartition = chre::isSlpiUimgSupported() ?
64    QURT_THREAD_ATTR_TCB_PARTITION_TCM : QURT_THREAD_ATTR_TCB_PARTITION_RAM;
65
66//! The priority to set for the CHRE thread (value between 1-255, with 1 being
67//! the highest).
68//! @see qurt_thread_attr_set_priority
69constexpr unsigned short kThreadPriority = 192;
70
71//! How long we wait (in microseconds) between checks on whether the CHRE thread
72//! has exited after we invoked stop().
73constexpr qurt_timer_duration_t kThreadStatusPollingIntervalUsec = 5000;  // 5ms
74
75//! Buffer to use for the CHRE thread's stack.
76typename std::aligned_storage<kStackSize>::type gStack;
77
78//! QuRT OS handle for the CHRE thread.
79qurt_thread_t gThreadHandle;
80
81//! Protects access to thread metadata, like gThreadRunning, during critical
82//! sections (starting/stopping the CHRE thread).
83Mutex gThreadMutex;
84
85//! Set to true when the CHRE thread starts, and false when it exits normally.
86bool gThreadRunning;
87
88//! A thread-local storage key, which is currently only used to add a thread
89//! destructor callback for the host FastRPC thread.
90int gTlsKey;
91bool gTlsKeyValid;
92
93__attribute__((constructor))
94void onLoad(void) {
95  // Initialize the platform logging as early as possible.
96  chre::PlatformLogSingleton::init();
97}
98
99__attribute__((destructor))
100void onUnload(void) {
101  // Defer platform logging deinitialization to as late as possible.
102  chre::PlatformLogSingleton::deinit();
103}
104
105void performDebugDumpCallback(uint16_t /*eventType*/, void *data) {
106  auto *handle = static_cast<const uint32_t *>(data);
107  UniquePtr<char> dump = chre::EventLoopManagerSingleton::get()->debugDump();
108  ashCommitDebugDump(*handle, dump.get(), true /*done*/);
109}
110
111void onDebugDumpRequested(void * /*cookie*/, uint32_t handle) {
112  static uint32_t debugDumpHandle;
113
114  debugDumpHandle = handle;
115  if (!chre::EventLoopManagerSingleton::get()->deferCallback(
116          chre::SystemCallbackType::PerformDebugDump, &debugDumpHandle,
117          performDebugDumpCallback)) {
118    LOGW("Failed to post event to get debug dump");
119  }
120}
121
122/**
123 * Entry point for the QuRT thread that runs CHRE.
124 *
125 * @param data Argument passed to qurt_thread_create()
126 */
127void chreThreadEntry(void * /*data*/) {
128  EventLoopManagerSingleton::get()->lateInit();
129  EventLoop *eventLoop = &EventLoopManagerSingleton::get()->getEventLoop();
130  chre::loadStaticNanoapps();
131  loadPreloadedNanoapps(eventLoop);
132  ashRegisterDebugDumpCallback("CHRE", onDebugDumpRequested, nullptr);
133  eventLoop->run();
134
135  ashUnregisterDebugDumpCallback(onDebugDumpRequested);
136  chre::deinit();
137  gThreadRunning = false;
138  LOGD("CHRE thread exiting");
139}
140
141void onHostProcessTerminated(void * /*data*/) {
142  LOGW("Host process died, exiting CHRE (running %d)", gThreadRunning);
143  chre_slpi_stop_thread();
144}
145
146}  // anonymous namespace
147
148namespace chre {
149
150bool inEventLoopThread() {
151  return (qurt_thread_get_id() == gThreadHandle);
152}
153
154}  // namespace chre
155
156/**
157 * Invoked over FastRPC to initialize and start the CHRE thread.
158 *
159 * @return 0 on success, nonzero on failure (per FastRPC requirements)
160 */
161extern "C" int chre_slpi_start_thread(void) {
162  // This lock ensures that we only start the thread once
163  LockGuard<Mutex> lock(gThreadMutex);
164  int fastRpcResult = CHRE_FASTRPC_ERROR;
165
166  if (gThreadRunning) {
167    LOGE("CHRE thread already running");
168  } else {
169    // This must complete before we can receive messages that might result in
170    // posting an event
171    chre::init();
172
173    // Human-readable name for the CHRE thread (not const in QuRT API, but they
174    // make a copy)
175    char threadName[] = "CHRE";
176    qurt_thread_attr_t attributes;
177
178    qurt_thread_attr_init(&attributes);
179    qurt_thread_attr_set_name(&attributes, threadName);
180    qurt_thread_attr_set_priority(&attributes, kThreadPriority);
181    qurt_thread_attr_set_stack_addr(&attributes, &gStack);
182    qurt_thread_attr_set_stack_size(&attributes, kStackSize);
183    qurt_thread_attr_set_tcb_partition(&attributes, kTcbPartition);
184
185    gThreadRunning = true;
186    LOGI("Starting CHRE thread");
187    int result = qurt_thread_create(&gThreadHandle, &attributes,
188                                    chreThreadEntry, nullptr);
189    if (result != QURT_EOK) {
190      LOGE("Couldn't create CHRE thread: %d", result);
191      gThreadRunning = false;
192    } else if (HAP_thread_migrate(gThreadHandle) != 0) {
193      FATAL_ERROR("Couldn't migrate thread");
194    } else {
195      LOGD("Started CHRE thread");
196      fastRpcResult = CHRE_FASTRPC_SUCCESS;
197    }
198  }
199
200  return fastRpcResult;
201}
202
203/**
204 * Blocks until the CHRE thread exits. Called over FastRPC to monitor for
205 * abnormal termination of the CHRE thread and/or SLPI as a whole.
206 *
207 * @return Always returns 0, indicating success (per FastRPC requirements)
208 */
209extern "C" int chre_slpi_wait_on_thread_exit(void) {
210  if (!gThreadRunning) {
211    LOGE("Tried monitoring for CHRE thread exit, but thread not running!");
212  } else {
213    int status;
214    int result = qurt_thread_join(gThreadHandle, &status);
215    if (result != QURT_EOK) {
216      LOGE("qurt_thread_join failed with result %d", result);
217    }
218    LOGI("Detected CHRE thread exit");
219  }
220
221  return CHRE_FASTRPC_SUCCESS;
222}
223
224/**
225 * If the CHRE thread is running, requests it to perform graceful shutdown,
226 * waits for it to exit, then completes teardown.
227 *
228 * @return Always returns 0, indicating success (per FastRPC requirements)
229 */
230extern "C" int chre_slpi_stop_thread(void) {
231  // This lock ensures that we will complete shutdown before the thread can be
232  // started again
233  LockGuard<Mutex> lock(gThreadMutex);
234
235  if (!gThreadRunning) {
236    LOGD("Tried to stop CHRE thread, but not running");
237  } else {
238    EventLoopManagerSingleton::get()->getEventLoop().stop();
239    if (gTlsKeyValid) {
240      int ret = qurt_tls_delete_key(gTlsKey);
241      if (ret != QURT_EOK) {
242        // Note: LOGE is not necessarily safe to use after stopping CHRE
243        FARF(ERROR, "Deleting TLS key failed: %d", ret);
244      }
245      gTlsKeyValid = false;
246    }
247
248    // Poll until the thread has stopped; note that we can't use
249    // qurt_thread_join() here because chreMonitorThread() will already be
250    // blocking in it, and attempting to join the same target from two threads
251    // is invalid. Technically, we could use a condition variable, but this is
252    // simpler and we don't care too much about being notified right away.
253    while (gThreadRunning) {
254      qurt_timer_sleep(kThreadStatusPollingIntervalUsec);
255    }
256    gThreadHandle = 0;
257
258    // Perform this as late as possible - if we are shutting down because we
259    // detected exit of the host process, FastRPC will unload us once all our
260    // FastRPC calls have returned. Doing this late helps ensure that the call
261    // to chre_slpi_get_message_to_host() stays open until we're done with
262    // cleanup.
263    chre::HostLinkBase::shutdown();
264  }
265
266  return CHRE_FASTRPC_SUCCESS;
267}
268
269/**
270 * Creates a thread-local storage (TLS) key in QuRT, which we use to inject a
271 * destructor that is called when the current FastRPC thread terminates. This is
272 * used to get a notification when the original FastRPC thread dies for any
273 * reason, so we can stop the CHRE thread.
274 *
275 * Note that this needs to be invoked from a separate thread on the host process
276 * side. It doesn't work if called from a thread that will be blocking inside a
277 * FastRPC call, such as the monitor thread.
278 *
279 * @return 0 on success, nonzero on failure (per FastRPC requirements)
280 */
281extern "C" int chre_slpi_initialize_reverse_monitor(void) {
282  LockGuard<Mutex> lock(gThreadMutex);
283
284  if (!gTlsKeyValid) {
285    int result = qurt_tls_create_key(&gTlsKey, onHostProcessTerminated);
286    if (result != QURT_EOK) {
287      LOGE("Couldn't create TLS key: %d", result);
288    } else {
289      // We need to set the value to something for the destructor to be invoked
290      result = qurt_tls_set_specific(gTlsKey, &gTlsKey);
291      if (result != QURT_EOK) {
292        LOGE("Couldn't set TLS data: %d", result);
293        qurt_tls_delete_key(gTlsKey);
294      } else {
295        gTlsKeyValid = true;
296      }
297    }
298  }
299
300  return (gTlsKeyValid) ? CHRE_FASTRPC_SUCCESS : CHRE_FASTRPC_ERROR;
301}
302