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