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