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 "chre/platform/platform_nanoapp.h"
18
19#include "chre/core/event_loop_manager.h"
20#include "chre/platform/assert.h"
21#include "chre/platform/log.h"
22#include "chre/platform/memory.h"
23#include "chre/platform/shared/nanoapp_dso_util.h"
24#include "chre/platform/shared/nanoapp_support_lib_dso.h"
25#include "chre/platform/slpi/memory.h"
26#include "chre/platform/slpi/power_control_util.h"
27#include "chre/util/system/debug_dump.h"
28#include "chre_api/chre/version.h"
29
30#include "dlfcn.h"
31
32#include <inttypes.h>
33#include <string.h>
34
35namespace chre {
36
37PlatformNanoapp::~PlatformNanoapp() {
38  closeNanoapp();
39  if (mAppBinary != nullptr) {
40    memoryFreeBigImage(mAppBinary);
41  }
42}
43
44bool PlatformNanoapp::start() {
45  // Invoke the start entry point after successfully opening the app
46  if (!isUimgApp()) {
47    slpiForceBigImage();
48  }
49
50  return openNanoapp() && mAppInfo->entryPoints.start();
51}
52
53void PlatformNanoapp::handleEvent(uint32_t senderInstanceId,
54                                  uint16_t eventType,
55                                  const void *eventData) {
56  if (!isUimgApp()) {
57    slpiForceBigImage();
58  }
59
60  mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
61}
62
63void PlatformNanoapp::end() {
64  if (!isUimgApp()) {
65    slpiForceBigImage();
66  }
67
68  mAppInfo->entryPoints.end();
69  closeNanoapp();
70}
71
72bool PlatformNanoappBase::loadFromBuffer(uint64_t appId, uint32_t appVersion,
73                                         const void *appBinary,
74                                         size_t appBinaryLen) {
75  CHRE_ASSERT(!isLoaded());
76  bool success = false;
77  constexpr size_t kMaxAppSize = 2 * 1024 * 1024;  // 2 MiB
78
79  if (appBinaryLen > kMaxAppSize) {
80    LOGE("Rejecting app size %zu above limit %zu", appBinaryLen, kMaxAppSize);
81  } else {
82    mAppBinary = memoryAllocBigImage(appBinaryLen);
83    if (mAppBinary == nullptr) {
84      LOGE("Couldn't allocate %zu byte buffer for nanoapp 0x%016" PRIx64,
85           appBinaryLen, appId);
86    } else {
87      mExpectedAppId = appId;
88      mExpectedAppVersion = appVersion;
89      mAppBinaryLen = appBinaryLen;
90      memcpy(mAppBinary, appBinary, appBinaryLen);
91      success = true;
92    }
93  }
94
95  return success;
96}
97
98void PlatformNanoappBase::loadFromFile(uint64_t appId, const char *filename) {
99  CHRE_ASSERT(!isLoaded());
100  mExpectedAppId = appId;
101  mFilename = filename;
102}
103
104void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
105  CHRE_ASSERT(!isLoaded());
106  mIsStatic = true;
107  mAppInfo = appInfo;
108}
109
110bool PlatformNanoappBase::isLoaded() const {
111  return (mIsStatic || mAppBinary != nullptr || mDsoHandle != nullptr);
112}
113
114bool PlatformNanoappBase::isUimgApp() const {
115  return mIsUimgApp;
116}
117
118void PlatformNanoappBase::closeNanoapp() {
119  if (mDsoHandle != nullptr) {
120    if (dlclose(mDsoHandle) != 0) {
121      LOGE("dlclose failed: %s", dlerror());
122    }
123    mDsoHandle = nullptr;
124  }
125}
126
127bool PlatformNanoappBase::openNanoapp() {
128  bool success = false;
129
130  if (mIsStatic) {
131    success = true;
132  } else if (mFilename != nullptr) {
133    success = openNanoappFromFile();
134  } else if (mAppBinary != nullptr) {
135    success = openNanoappFromBuffer();
136  } else {
137    CHRE_ASSERT(false);
138  }
139
140  // Save this flag locally since it may be referenced while the system is in
141  // micro-image
142  if (mAppInfo != nullptr) {
143    mIsUimgApp = mAppInfo->isTcmNanoapp;
144  }
145
146  return success;
147}
148
149bool PlatformNanoappBase::openNanoappFromBuffer() {
150  CHRE_ASSERT(mAppBinary != nullptr);
151  CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
152  bool success = false;
153
154  // Populate a filename string (just a requirement of the dlopenbuf API)
155  constexpr size_t kMaxFilenameLen = 17;
156  char filename[kMaxFilenameLen];
157  snprintf(filename, sizeof(filename), "%016" PRIx64, mExpectedAppId);
158
159  mDsoHandle = dlopenbuf(
160      filename, static_cast<const char *>(mAppBinary),
161      static_cast<int>(mAppBinaryLen), RTLD_NOW);
162  if (mDsoHandle == nullptr) {
163    LOGE("Failed to load nanoapp: %s", dlerror());
164  } else {
165    mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
166        dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
167    if (mAppInfo == nullptr) {
168      LOGE("Failed to find app info symbol: %s", dlerror());
169    } else {
170      success = validateAppInfo(mExpectedAppId, mExpectedAppVersion, mAppInfo);
171      if (!success) {
172        mAppInfo = nullptr;
173      } else {
174        LOGI("Successfully loaded nanoapp: %s (0x%016" PRIx64 ") version 0x%"
175             PRIx32 " uimg %d system %d", mAppInfo->name, mAppInfo->appId,
176             mAppInfo->appVersion, mAppInfo->isTcmNanoapp,
177             mAppInfo->isSystemNanoapp);
178        memoryFreeBigImage(mAppBinary);
179        mAppBinary = nullptr;
180      }
181    }
182  }
183
184  return success;
185}
186
187bool PlatformNanoappBase::openNanoappFromFile() {
188  CHRE_ASSERT(mFilename != nullptr);
189  CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
190  bool success = false;
191
192  mDsoHandle = dlopen(mFilename, RTLD_NOW);
193  if (mDsoHandle == nullptr) {
194    LOGE("Failed to load nanoapp from file %s: %s", mFilename, dlerror());
195  } else {
196    mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
197        dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
198    if (mAppInfo == nullptr) {
199      LOGE("Failed to find app info symbol in %s: %s", mFilename, dlerror());
200    } else {
201      success = validateAppInfo(mExpectedAppId, 0, mAppInfo,
202                                true /* ignoreAppVersion */);
203      if (!success) {
204        mAppInfo = nullptr;
205      } else {
206        LOGI("Successfully loaded nanoapp %s (0x%016" PRIx64 ") version 0x%"
207             PRIx32 " uimg %d system %d from file %s", mAppInfo->name,
208             mAppInfo->appId, mAppInfo->appVersion, mAppInfo->isTcmNanoapp,
209             mAppInfo->isSystemNanoapp, mFilename);
210        // Save the app version field in case this app gets disabled and we
211        // still get a query request for the version later on. We are OK not
212        // knowing the version prior to the first load because we assume that
213        // nanoapps loaded via file are done at CHRE initialization time.
214        mExpectedAppVersion = mAppInfo->appVersion;
215      }
216    }
217  }
218
219  return success;
220}
221
222uint64_t PlatformNanoapp::getAppId() const {
223  return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
224}
225
226uint32_t PlatformNanoapp::getAppVersion() const {
227  return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
228}
229
230uint32_t PlatformNanoapp::getTargetApiVersion() const {
231  return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion : 0;
232}
233
234bool PlatformNanoapp::isSystemNanoapp() const {
235  // Right now, we assume that system nanoapps are always static nanoapps. Since
236  // mAppInfo can only be null either prior to loading the app (in which case
237  // this function is not expected to return a valid value anyway), or when a
238  // dynamic nanoapp is not running, "false" is the correct return value in that
239  // case.
240  return (mAppInfo != nullptr) ? mAppInfo->isSystemNanoapp : false;
241}
242
243bool PlatformNanoapp::logStateToBuffer(char *buffer, size_t *bufferPos,
244                                       size_t bufferSize) const {
245  bool success = true;
246  if (mAppInfo != nullptr) {
247    success &= debugDumpPrint(buffer, bufferPos, bufferSize, " %s: vendor=\"%s\"",
248                              mAppInfo->name, mAppInfo->vendor);
249  }
250  return success;
251}
252
253}  // namespace chre
254