1 /*
2  * Copyright (C) 2017 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 <inttypes.h>
18 #include <limits.h>
19 
20 #include "qurt.h"
21 
22 #include "chre/core/event_loop_manager.h"
23 #include "chre/core/host_comms_manager.h"
24 #include "chre/platform/context.h"
25 #include "chre/platform/memory.h"
26 #include "chre/platform/log.h"
27 #include "chre/platform/shared/host_protocol_chre.h"
28 #include "chre/platform/slpi/fastrpc.h"
29 #include "chre/util/fixed_size_blocking_queue.h"
30 #include "chre/util/macros.h"
31 #include "chre/util/unique_ptr.h"
32 #include "chre_api/chre/version.h"
33 
34 using flatbuffers::FlatBufferBuilder;
35 
36 namespace chre {
37 
38 namespace {
39 
40 constexpr size_t kOutboundQueueSize = 32;
41 
42 //! Used to pass the client ID through the user data pointer in deferCallback
43 union HostClientIdCallbackData {
44   uint16_t hostClientId;
45   void *ptr;
46 };
47 static_assert(sizeof(uint16_t) <= sizeof(void*),
48               "Pointer must at least fit a u16 for passing the host client ID");
49 
50 struct LoadNanoappCallbackData {
51   uint64_t appId;
52   uint32_t transactionId;
53   uint16_t hostClientId;
54   UniquePtr<Nanoapp> nanoapp = MakeUnique<Nanoapp>();
55 };
56 
57 enum class PendingMessageType {
58   Shutdown,
59   NanoappMessageToHost,
60   HubInfoResponse,
61   NanoappListResponse,
62   LoadNanoappResponse,
63 };
64 
65 struct PendingMessage {
PendingMessagechre::__anonaa635afe0111::PendingMessage66   PendingMessage(PendingMessageType msgType, uint16_t hostClientId) {
67     type = msgType;
68     data.hostClientId = hostClientId;
69   }
70 
PendingMessagechre::__anonaa635afe0111::PendingMessage71   PendingMessage(PendingMessageType msgType,
72                  const MessageToHost *msgToHost = nullptr) {
73     type = msgType;
74     data.msgToHost = msgToHost;
75   }
76 
PendingMessagechre::__anonaa635afe0111::PendingMessage77   PendingMessage(PendingMessageType msgType, FlatBufferBuilder *builder) {
78     type = msgType;
79     data.builder = builder;
80   }
81 
82   PendingMessageType type;
83   union {
84     const MessageToHost *msgToHost;
85     uint16_t hostClientId;
86     FlatBufferBuilder *builder;
87   } data;
88 };
89 
90 FixedSizeBlockingQueue<PendingMessage, kOutboundQueueSize>
91     gOutboundQueue;
92 
copyToHostBuffer(const FlatBufferBuilder & builder,unsigned char * buffer,size_t bufferSize,unsigned int * messageLen)93 int copyToHostBuffer(const FlatBufferBuilder& builder, unsigned char *buffer,
94                      size_t bufferSize, unsigned int *messageLen) {
95   uint8_t *data = builder.GetBufferPointer();
96   size_t size = builder.GetSize();
97   int result;
98 
99   if (size > bufferSize) {
100     LOGE("Encoded structure size %zu too big for host buffer %zu; dropping",
101          size, bufferSize);
102     result = CHRE_FASTRPC_ERROR;
103   } else {
104     memcpy(buffer, data, size);
105     *messageLen = size;
106     result = CHRE_FASTRPC_SUCCESS;
107   }
108 
109   return result;
110 }
111 
constructNanoappListCallback(uint16_t,void * deferCbData)112 void constructNanoappListCallback(uint16_t /*eventType*/, void *deferCbData) {
113   constexpr size_t kFixedOverhead = 56;
114   constexpr size_t kPerNanoappSize = 16;
115 
116   HostClientIdCallbackData clientIdCbData;
117   clientIdCbData.ptr = deferCbData;
118 
119   // TODO: need to add support for getting apps from multiple event loops
120   bool pushed = false;
121   EventLoop *eventLoop = getCurrentEventLoop();
122   size_t expectedNanoappCount = eventLoop->getNanoappCount();
123   DynamicVector<NanoappListEntryOffset> nanoappEntries;
124 
125   auto *builder = memoryAlloc<FlatBufferBuilder>(
126       kFixedOverhead + expectedNanoappCount * kPerNanoappSize);
127   if (builder == nullptr) {
128     LOGE("Couldn't allocate builder for nanoapp list response");
129   } else if (!nanoappEntries.reserve(expectedNanoappCount)) {
130     LOGE("Couldn't reserve space for list of nanoapp offsets");
131   } else {
132     struct CallbackData {
133       CallbackData(FlatBufferBuilder& builder_,
134                    DynamicVector<NanoappListEntryOffset>& nanoappEntries_)
135           : builder(builder_), nanoappEntries(nanoappEntries_) {}
136 
137       FlatBufferBuilder& builder;
138       DynamicVector<NanoappListEntryOffset>& nanoappEntries;
139     };
140 
141     auto callback = [](const Nanoapp *nanoapp, void *untypedData) {
142       auto *data = static_cast<CallbackData *>(untypedData);
143       HostProtocolChre::addNanoappListEntry(
144           data->builder, data->nanoappEntries, nanoapp->getAppId(),
145           nanoapp->getAppVersion(), true /*enabled*/,
146           nanoapp->isSystemNanoapp());
147     };
148 
149     // Add a NanoappListEntry to the FlatBuffer for each nanoapp
150     CallbackData cbData(*builder, nanoappEntries);
151     eventLoop->forEachNanoapp(callback, &cbData);
152     HostProtocolChre::finishNanoappListResponse(*builder, nanoappEntries,
153                                                 clientIdCbData.hostClientId);
154 
155     pushed = gOutboundQueue.push(
156         PendingMessage(PendingMessageType::NanoappListResponse, builder));
157     if (!pushed) {
158       LOGE("Couldn't push list response");
159     }
160   }
161 
162   if (!pushed && builder != nullptr) {
163     memoryFree(builder);
164   }
165 }
166 
finishLoadingNanoappCallback(uint16_t,void * data)167 void finishLoadingNanoappCallback(uint16_t /*eventType*/, void *data) {
168   UniquePtr<LoadNanoappCallbackData> cbData(
169       static_cast<LoadNanoappCallbackData *>(data));
170 
171   EventLoop *eventLoop = getCurrentEventLoop();
172   bool startedSuccessfully = (cbData->nanoapp->isLoaded()) ?
173       eventLoop->startNanoapp(cbData->nanoapp) : false;
174 
175   constexpr size_t kInitialBufferSize = 48;
176   auto *builder = memoryAlloc<FlatBufferBuilder>(kInitialBufferSize);
177   if (builder == nullptr) {
178     LOGE("Couldn't allocate memory for load nanoapp response");
179   } else {
180     HostProtocolChre::encodeLoadNanoappResponse(
181         *builder, cbData->hostClientId, cbData->transactionId,
182         startedSuccessfully);
183 
184     // TODO: if this fails, ideally we should block for some timeout until
185     // there's space in the queue (like up to 1 second)
186     if (!gOutboundQueue.push(PendingMessage(
187             PendingMessageType::LoadNanoappResponse, builder))) {
188       LOGE("Couldn't push load nanoapp response to outbound queue");
189       memoryFree(builder);
190     }
191   }
192 }
193 
generateMessageToHost(const MessageToHost * msgToHost,unsigned char * buffer,size_t bufferSize,unsigned int * messageLen)194 int generateMessageToHost(const MessageToHost *msgToHost, unsigned char *buffer,
195                           size_t bufferSize, unsigned int *messageLen) {
196   // TODO: ideally we'd construct our flatbuffer directly in the
197   // host-supplied buffer
198   constexpr size_t kFixedSizePortion = 56;
199   FlatBufferBuilder builder(msgToHost->message.size() + kFixedSizePortion);
200   HostProtocolChre::encodeNanoappMessage(
201     builder, msgToHost->appId, msgToHost->toHostData.messageType,
202     msgToHost->toHostData.hostEndpoint, msgToHost->message.data(),
203     msgToHost->message.size());
204 
205   int result = copyToHostBuffer(builder, buffer, bufferSize, messageLen);
206 
207   auto& hostCommsManager =
208       EventLoopManagerSingleton::get()->getHostCommsManager();
209   hostCommsManager.onMessageToHostComplete(msgToHost);
210 
211   return result;
212 }
213 
generateHubInfoResponse(uint16_t hostClientId,unsigned char * buffer,size_t bufferSize,unsigned int * messageLen)214 int generateHubInfoResponse(uint16_t hostClientId, unsigned char *buffer,
215                             size_t bufferSize, unsigned int *messageLen) {
216   constexpr size_t kInitialBufferSize = 192;
217 
218   constexpr char kHubName[] = "CHRE on SLPI";
219   constexpr char kVendor[] = "Google";
220   constexpr char kToolchain[] = "Hexagon Tools 8.0 (clang "
221     STRINGIFY(__clang_major__) "."
222     STRINGIFY(__clang_minor__) "."
223     STRINGIFY(__clang_patchlevel__) ")";
224   constexpr uint32_t kLegacyPlatformVersion = 0;
225   constexpr uint32_t kLegacyToolchainVersion =
226     ((__clang_major__ & 0xFF) << 24) |
227     ((__clang_minor__ & 0xFF) << 16) |
228     (__clang_patchlevel__ & 0xFFFF);
229   constexpr float kPeakMips = 350;
230   constexpr float kStoppedPower = 0;
231   constexpr float kSleepPower = 1;
232   constexpr float kPeakPower = 15;
233 
234   FlatBufferBuilder builder(kInitialBufferSize);
235   HostProtocolChre::encodeHubInfoResponse(
236       builder, kHubName, kVendor, kToolchain, kLegacyPlatformVersion,
237       kLegacyToolchainVersion, kPeakMips, kStoppedPower, kSleepPower,
238       kPeakPower, CHRE_MESSAGE_TO_HOST_MAX_SIZE, chreGetPlatformId(),
239       chreGetVersion(), hostClientId);
240 
241   return copyToHostBuffer(builder, buffer, bufferSize, messageLen);
242 }
243 
generateMessageFromBuilder(FlatBufferBuilder * builder,unsigned char * buffer,size_t bufferSize,unsigned int * messageLen)244 int generateMessageFromBuilder(
245     FlatBufferBuilder *builder, unsigned char *buffer, size_t bufferSize,
246     unsigned int *messageLen) {
247   CHRE_ASSERT(builder != nullptr);
248   int result = copyToHostBuffer(*builder, buffer, bufferSize, messageLen);
249   memoryFree(builder);
250   return result;
251 }
252 
253 /**
254  * FastRPC method invoked by the host to block on messages
255  *
256  * @param buffer Output buffer to populate with message data
257  * @param bufferLen Size of the buffer, in bytes
258  * @param messageLen Output parameter to populate with the size of the message
259  *        in bytes upon success
260  *
261  * @return 0 on success, nonzero on failure
262  */
chre_slpi_get_message_to_host(unsigned char * buffer,int bufferLen,unsigned int * messageLen)263 extern "C" int chre_slpi_get_message_to_host(
264     unsigned char *buffer, int bufferLen, unsigned int *messageLen) {
265   CHRE_ASSERT(buffer != nullptr);
266   CHRE_ASSERT(bufferLen > 0);
267   CHRE_ASSERT(messageLen != nullptr);
268   int result = CHRE_FASTRPC_ERROR;
269 
270   if (bufferLen <= 0 || buffer == nullptr || messageLen == nullptr) {
271     // Note that we can't use regular logs here as they can result in sending
272     // a message, leading to an infinite loop if the error is persistent
273     FARF(FATAL, "Invalid buffer size %d or bad pointers (buf %d len %d)",
274          bufferLen, (buffer == nullptr), (messageLen == nullptr));
275   } else {
276     size_t bufferSize = static_cast<size_t>(bufferLen);
277     PendingMessage pendingMsg = gOutboundQueue.pop();
278 
279     switch (pendingMsg.type) {
280       case PendingMessageType::Shutdown:
281         result = CHRE_FASTRPC_ERROR_SHUTTING_DOWN;
282         break;
283 
284       case PendingMessageType::NanoappMessageToHost:
285         result = generateMessageToHost(pendingMsg.data.msgToHost, buffer,
286                                        bufferSize, messageLen);
287         break;
288 
289       case PendingMessageType::HubInfoResponse:
290         result = generateHubInfoResponse(pendingMsg.data.hostClientId, buffer,
291                                          bufferSize, messageLen);
292         break;
293 
294       case PendingMessageType::NanoappListResponse:
295       case PendingMessageType::LoadNanoappResponse:
296         result = generateMessageFromBuilder(pendingMsg.data.builder,
297                                             buffer, bufferSize, messageLen);
298         break;
299 
300       default:
301         CHRE_ASSERT_LOG(false, "Unexpected pending message type");
302     }
303   }
304 
305   LOGD("Returning message to host (result %d length %u)", result, *messageLen);
306   return result;
307 }
308 
309 /**
310  * FastRPC method invoked by the host to send a message to the system
311  *
312  * @param buffer
313  * @param size
314  *
315  * @return 0 on success, nonzero on failure
316  */
chre_slpi_deliver_message_from_host(const unsigned char * message,int messageLen)317 extern "C" int chre_slpi_deliver_message_from_host(const unsigned char *message,
318                                                    int messageLen) {
319   CHRE_ASSERT(message != nullptr);
320   CHRE_ASSERT(messageLen > 0);
321   int result = CHRE_FASTRPC_ERROR;
322 
323   if (message == nullptr || messageLen <= 0) {
324     LOGE("Got null or invalid size (%d) message from host", messageLen);
325   } else if (!HostProtocolChre::decodeMessageFromHost(
326       message, static_cast<size_t>(messageLen))) {
327     LOGE("Failed to decode/handle message");
328   } else {
329     result = CHRE_FASTRPC_SUCCESS;
330   }
331 
332   return result;
333 }
334 
335 }  // anonymous namespace
336 
sendMessage(const MessageToHost * message)337 bool HostLink::sendMessage(const MessageToHost *message) {
338   return gOutboundQueue.push(
339       PendingMessage(PendingMessageType::NanoappMessageToHost, message));
340 }
341 
shutdown()342 void HostLinkBase::shutdown() {
343   constexpr qurt_timer_duration_t kPollingIntervalUsec = 5000;
344 
345   // Push a null message so the blocking call in chre_slpi_get_message_to_host()
346   // returns and the host can exit cleanly. If the queue is full, try again to
347   // avoid getting stuck (no other new messages should be entering the queue at
348   // this time). Don't wait too long as the host-side binary may have died in
349   // a state where it's not blocked in chre_slpi_get_message_to_host().
350   int retryCount = 5;
351   FARF(MEDIUM, "Shutting down host link");
352   while (!gOutboundQueue.push(PendingMessage(PendingMessageType::Shutdown))
353          && --retryCount > 0) {
354     qurt_timer_sleep(kPollingIntervalUsec);
355   }
356 
357   if (retryCount <= 0) {
358     // Don't use LOGE, as it may involve trying to send a message
359     FARF(ERROR, "No room in outbound queue for shutdown message and host not "
360          "draining queue!");
361   } else {
362     FARF(MEDIUM, "Draining message queue");
363 
364     // We were able to push the shutdown message. Wait for the queue to
365     // completely flush before returning.
366     int waitCount = 5;
367     while (!gOutboundQueue.empty() && --waitCount > 0) {
368       qurt_timer_sleep(kPollingIntervalUsec);
369     }
370 
371     if (waitCount <= 0) {
372       FARF(ERROR, "Host took too long to drain outbound queue; exiting anyway");
373     } else {
374       FARF(MEDIUM, "Finished draining queue");
375     }
376   }
377 }
378 
handleNanoappMessage(uint64_t appId,uint32_t messageType,uint16_t hostEndpoint,const void * messageData,size_t messageDataLen)379 void HostMessageHandlers::handleNanoappMessage(
380     uint64_t appId, uint32_t messageType, uint16_t hostEndpoint,
381     const void *messageData, size_t messageDataLen) {
382   LOGD("Parsed nanoapp message from host: app ID 0x%016" PRIx64 ", endpoint "
383        "0x%" PRIx16 ", msgType %" PRIu32 ", payload size %zu",
384        appId, hostEndpoint, messageType, messageDataLen);
385 
386   HostCommsManager& manager =
387       EventLoopManagerSingleton::get()->getHostCommsManager();
388   manager.sendMessageToNanoappFromHost(
389       appId, messageType, hostEndpoint, messageData, messageDataLen);
390 }
391 
handleHubInfoRequest(uint16_t hostClientId)392 void HostMessageHandlers::handleHubInfoRequest(uint16_t hostClientId) {
393   // We generate the response in the context of chre_slpi_get_message_to_host
394   LOGD("Got hub info request from client ID %" PRIu16, hostClientId);
395   gOutboundQueue.push(PendingMessage(
396       PendingMessageType::HubInfoResponse, hostClientId));
397 }
398 
handleNanoappListRequest(uint16_t hostClientId)399 void HostMessageHandlers::handleNanoappListRequest(uint16_t hostClientId) {
400   LOGD("Got nanoapp list request from client ID %" PRIu16, hostClientId);
401   HostClientIdCallbackData cbData = {};
402   cbData.hostClientId = hostClientId;
403   EventLoopManagerSingleton::get()->deferCallback(
404       SystemCallbackType::NanoappListResponse, cbData.ptr,
405       constructNanoappListCallback);
406 }
407 
handleLoadNanoappRequest(uint16_t hostClientId,uint32_t transactionId,uint64_t appId,uint32_t appVersion,uint32_t targetApiVersion,const void * appBinary,size_t appBinaryLen)408 void HostMessageHandlers::handleLoadNanoappRequest(
409     uint16_t hostClientId, uint32_t transactionId, uint64_t appId,
410     uint32_t appVersion, uint32_t targetApiVersion, const void *appBinary,
411     size_t appBinaryLen) {
412   auto cbData = MakeUnique<LoadNanoappCallbackData>();
413 
414   LOGD("Got load nanoapp request (txnId %" PRIu32 ") for appId 0x%016" PRIx64
415        " version 0x%" PRIx32 " target API version 0x%08" PRIx32 " size %zu",
416        transactionId, appId, appVersion, targetApiVersion, appBinaryLen);
417   if (cbData.isNull() || cbData->nanoapp.isNull()) {
418     LOGE("Couldn't allocate load nanoapp callback data");
419   } else {
420     cbData->transactionId = transactionId;
421     cbData->hostClientId  = hostClientId;
422     cbData->appId = appId;
423 
424     // Note that if this fails, we'll generate the error response in
425     // the normal deferred callback
426     cbData->nanoapp->loadFromBuffer(appId, appVersion, appBinary, appBinaryLen);
427     if (!EventLoopManagerSingleton::get()->deferCallback(
428             SystemCallbackType::FinishLoadingNanoapp, cbData.get(),
429             finishLoadingNanoappCallback)) {
430       LOGE("Couldn't post callback to finish loading nanoapp");
431     } else {
432       cbData.release();
433     }
434   }
435 }
436 
437 }  // namespace chre
438