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