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 { 66 PendingMessage(PendingMessageType msgType, uint16_t hostClientId) { 67 type = msgType; 68 data.hostClientId = hostClientId; 69 } 70 71 PendingMessage(PendingMessageType msgType, 72 const MessageToHost *msgToHost = nullptr) { 73 type = msgType; 74 data.msgToHost = msgToHost; 75 } 76 77 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 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 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 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 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 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 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 */ 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 */ 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 337 bool HostLink::sendMessage(const MessageToHost *message) { 338 return gOutboundQueue.push( 339 PendingMessage(PendingMessageType::NanoappMessageToHost, message)); 340 } 341 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 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 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 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 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