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