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