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