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