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