Home | History | Annotate | Download | only in slpi
      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