• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
performDebugDumpCallback(uint16_t,void * data)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 
onDebugDumpRequested(void *,uint32_t handle)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  */
chreThreadEntry(void *)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 
onHostProcessTerminated(void *)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 
inEventLoopThread()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  */
chre_slpi_start_thread(void)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  */
chre_slpi_wait_on_thread_exit(void)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  */
chre_slpi_stop_thread(void)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  */
chre_slpi_initialize_reverse_monitor(void)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