• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 <dlfcn.h>
20 #include <cinttypes>
21 
22 #include "chre/platform/assert.h"
23 #include "chre/platform/log.h"
24 #include "chre/platform/shared/authentication.h"
25 #include "chre/platform/shared/nanoapp_dso_util.h"
26 #include "chre/platform/shared/nanoapp_loader.h"
27 #include "chre/util/macros.h"
28 #include "chre/util/system/napp_header_utils.h"
29 #include "chre/util/system/napp_permissions.h"
30 #include "chre_api/chre/version.h"
31 
32 namespace chre {
33 namespace {
34 
35 const char kDefaultAppVersionString[] = "<undefined>";
36 size_t kDefaultAppVersionStringSize = ARRAY_SIZE(kDefaultAppVersionString);
37 
38 }  // namespace
39 
~PlatformNanoapp()40 PlatformNanoapp::~PlatformNanoapp() {
41   closeNanoapp();
42 
43   if (mAppBinary != nullptr) {
44     forceDramAccess();
45     nanoappBinaryDramFree(mAppBinary);
46   }
47 }
48 
start()49 bool PlatformNanoapp::start() {
50   //! Always force DRAM access when starting since nanoapps are loaded via DRAM.
51   forceDramAccess();
52 
53   bool success = false;
54   if (!openNanoapp()) {
55     LOGE("Failed to open nanoapp");
56   } else if (mAppInfo == nullptr) {
57     LOGE("Null app info!");
58   } else {
59     success = mAppInfo->entryPoints.start();
60   }
61 
62   return success;
63 }
64 
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)65 void PlatformNanoapp::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
66                                   const void *eventData) {
67   enableDramAccessIfRequired();
68   mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
69 }
70 
end()71 void PlatformNanoapp::end() {
72   enableDramAccessIfRequired();
73   mAppInfo->entryPoints.end();
74   closeNanoapp();
75 }
76 
getAppId() const77 uint64_t PlatformNanoapp::getAppId() const {
78   // TODO (karthikmb/stange): Ideally, we should store the metadata as
79   // variables in TCM, to avoid bumping into DRAM for basic queries.
80   enableDramAccessIfRequired();
81   return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
82 }
83 
getAppVersion() const84 uint32_t PlatformNanoapp::getAppVersion() const {
85   enableDramAccessIfRequired();
86   return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
87 }
88 
supportsAppPermissions() const89 bool PlatformNanoapp::supportsAppPermissions() const {
90   return (mAppInfo != nullptr) ? (mAppInfo->structMinorVersion >=
91                                   CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION)
92                                : false;
93 }
94 
getAppPermissions() const95 uint32_t PlatformNanoapp::getAppPermissions() const {
96   return (supportsAppPermissions())
97              ? mAppInfo->appPermissions
98              : static_cast<uint32_t>(chre::NanoappPermissions::CHRE_PERMS_NONE);
99 }
100 
getAppName() const101 const char *PlatformNanoapp::getAppName() const {
102   enableDramAccessIfRequired();
103   return (mAppInfo != nullptr) ? mAppInfo->name : "Unknown";
104 }
105 
getTargetApiVersion() const106 uint32_t PlatformNanoapp::getTargetApiVersion() const {
107   enableDramAccessIfRequired();
108   return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion
109                                : mExpectedTargetApiVersion;
110 }
111 
isSystemNanoapp() const112 bool PlatformNanoapp::isSystemNanoapp() const {
113   enableDramAccessIfRequired();
114   return (mAppInfo != nullptr && mAppInfo->isSystemNanoapp);
115 }
116 
logStateToBuffer(DebugDumpWrapper & debugDump) const117 void PlatformNanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const {
118   if (mAppInfo != nullptr) {
119     enableDramAccessIfRequired();
120     size_t versionLen = 0;
121     const char *version = getAppVersionString(&versionLen);
122     debugDump.print("%s (%s) @ build: %.*s", mAppInfo->name, mAppInfo->vendor,
123                     versionLen, version);
124   }
125 }
126 
getAppVersionString(size_t * length) const127 const char *PlatformNanoappBase::getAppVersionString(size_t *length) const {
128   const char *versionString = kDefaultAppVersionString;
129   *length = kDefaultAppVersionStringSize;
130   enableDramAccessIfRequired();
131 
132   if (mAppUnstableId != nullptr) {
133     size_t appVersionStringLength = strlen(mAppUnstableId);
134 
135     //! The unstable ID is expected to be in the format of
136     //! <descriptor>=<nanoapp_name>@<build_id>. Use this expected layout
137     //! knowledge to parse the string and only return the build ID portion that
138     //! should be printed.
139     size_t startOffset = appVersionStringLength;
140     for (size_t i = 0; i < appVersionStringLength; i++) {
141       size_t offset = i + 1;
142       if (startOffset == appVersionStringLength && mAppUnstableId[i] == '@') {
143         startOffset = offset;
144       }
145     }
146 
147     if (startOffset < appVersionStringLength) {
148       versionString = &mAppUnstableId[startOffset];
149       *length = appVersionStringLength - startOffset;
150     }
151   }
152 
153   return versionString;
154 }
155 
isLoaded() const156 bool PlatformNanoappBase::isLoaded() const {
157   return (mIsStatic ||
158           (mAppBinary != nullptr && mBytesLoaded == mAppBinaryLen) ||
159           mDsoHandle != nullptr || mAppFilename != nullptr);
160 }
161 
isTcmApp() const162 bool PlatformNanoappBase::isTcmApp() const {
163   return mIsTcmNanoapp;
164 }
165 
loadStatic(const struct chreNslNanoappInfo * appInfo)166 void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
167   CHRE_ASSERT(!isLoaded());
168   mIsStatic = true;
169   mAppInfo = appInfo;
170 }
171 
reserveBuffer(uint64_t appId,uint32_t appVersion,uint32_t appFlags,size_t appBinaryLen,uint32_t targetApiVersion)172 bool PlatformNanoappBase::reserveBuffer(uint64_t appId, uint32_t appVersion,
173                                         uint32_t appFlags, size_t appBinaryLen,
174                                         uint32_t targetApiVersion) {
175   CHRE_ASSERT(!isLoaded());
176 
177   forceDramAccess();
178 
179   bool success = false;
180   mAppBinary = nanoappBinaryDramAlloc(appBinaryLen);
181 
182   bool isSigned = IS_BIT_SET(appFlags, CHRE_NAPP_HEADER_SIGNED);
183   if (!isSigned) {
184     LOGE("Unable to load unsigned nanoapps");
185   } else if (mAppBinary == nullptr) {
186     LOG_OOM();
187   } else {
188     bool tcmCapable = IS_BIT_SET(appFlags, CHRE_NAPP_HEADER_TCM_CAPABLE);
189     mExpectedAppId = appId;
190     mExpectedAppVersion = appVersion;
191     mExpectedTargetApiVersion = targetApiVersion;
192     mExpectedTcmCapable = tcmCapable;
193     mAppBinaryLen = appBinaryLen;
194     success = true;
195   }
196 
197   return success;
198 }
199 
copyNanoappFragment(const void * buffer,size_t bufferLen)200 bool PlatformNanoappBase::copyNanoappFragment(const void *buffer,
201                                               size_t bufferLen) {
202   CHRE_ASSERT(!isLoaded());
203 
204   forceDramAccess();
205 
206   bool success = true;
207 
208   if ((mBytesLoaded + bufferLen) > mAppBinaryLen) {
209     LOGE("Overflow: cannot load %zu bytes to %zu/%zu nanoapp binary buffer",
210          bufferLen, mBytesLoaded, mAppBinaryLen);
211     success = false;
212   } else {
213     uint8_t *binaryBuffer = static_cast<uint8_t *>(mAppBinary) + mBytesLoaded;
214     memcpy(binaryBuffer, buffer, bufferLen);
215     mBytesLoaded += bufferLen;
216   }
217 
218   return success;
219 }
220 
verifyNanoappInfo()221 bool PlatformNanoappBase::verifyNanoappInfo() {
222   bool success = false;
223 
224   if (mDsoHandle == nullptr) {
225     LOGE("No nanoapp info to verify");
226   } else {
227     mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
228         dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
229     if (mAppInfo == nullptr) {
230       LOGE("Failed to find app info symbol");
231     } else {
232       mAppUnstableId = mAppInfo->appVersionString;
233       if (mAppUnstableId == nullptr) {
234         LOGE("Failed to find unstable ID symbol");
235       } else {
236         success = validateAppInfo(mExpectedAppId, mExpectedAppVersion,
237                                   mExpectedTargetApiVersion, mAppInfo);
238         if (success && mAppInfo->isTcmNanoapp != mExpectedTcmCapable) {
239           success = false;
240           LOGE("Expected TCM nanoapp %d found %d", mExpectedTcmCapable,
241                mAppInfo->isTcmNanoapp);
242         }
243 
244         if (!success) {
245           mAppInfo = nullptr;
246         } else {
247           LOGI("Nanoapp loaded: %s (0x%016" PRIx64 ") version 0x%" PRIx32
248                " (%s) uimg %d system %d",
249                mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion,
250                mAppInfo->appVersionString, mAppInfo->isTcmNanoapp,
251                mAppInfo->isSystemNanoapp);
252           if (mAppInfo->structMinorVersion >=
253               CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION) {
254             LOGI("Nanoapp permissions: 0x%" PRIx32, mAppInfo->appPermissions);
255           }
256         }
257       }
258     }
259   }
260   return success;
261 }
262 
openNanoapp()263 bool PlatformNanoappBase::openNanoapp() {
264   bool success = false;
265   if (mIsStatic) {
266     success = true;
267   } else if (mAppBinary != nullptr) {
268     //! The true start of the binary will be after the authentication header.
269     //! Use the returned value from authenticateBinary to ensure dlopenbuf has
270     //! the starting address to a valid ELF.
271     void *binaryStart = mAppBinary;
272     if (!authenticateBinary(mAppBinary, &binaryStart)) {
273       LOGE("Unable to authenticate 0x%" PRIx64 " not loading", mExpectedAppId);
274     } else if (mDsoHandle != nullptr) {
275       LOGE("Trying to reopen an existing buffer");
276     } else {
277       mDsoHandle = dlopenbuf(binaryStart, mExpectedTcmCapable);
278       success = verifyNanoappInfo();
279     }
280   }
281 
282   if (!success) {
283     closeNanoapp();
284   }
285 
286   if (mAppBinary != nullptr) {
287     nanoappBinaryDramFree(mAppBinary);
288     mAppBinary = nullptr;
289   }
290 
291   // Save this flag locally since it may be referenced while the system is in
292   // TCM-only mode.
293   if (mAppInfo != nullptr) {
294     mIsTcmNanoapp = mAppInfo->isTcmNanoapp;
295   }
296 
297   return success;
298 }
299 
closeNanoapp()300 void PlatformNanoappBase::closeNanoapp() {
301   if (mDsoHandle != nullptr) {
302     // Force DRAM access since dl* functions are only safe to call with DRAM
303     // available.
304     forceDramAccess();
305     mAppInfo = nullptr;
306     if (dlclose(mDsoHandle) != 0) {
307       LOGE("dlclose failed");
308     }
309     mDsoHandle = nullptr;
310   }
311 }
312 
313 }  // namespace chre
314