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/util/system/napp_permissions.h"
29 #include "chre_api/chre/version.h"
30
31 #include "dlfcn.h"
32
33 #include <inttypes.h>
34 #include <string.h>
35
36 namespace chre {
37
~PlatformNanoapp()38 PlatformNanoapp::~PlatformNanoapp() {
39 closeNanoapp();
40 if (mAppBinary != nullptr) {
41 memoryFreeBigImage(mAppBinary);
42 }
43 }
44
start()45 bool PlatformNanoapp::start() {
46 // Invoke the start entry point after successfully opening the app
47 if (!isUimgApp()) {
48 slpiForceBigImage();
49 }
50
51 return openNanoapp() && mAppInfo->entryPoints.start();
52 }
53
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)54 void PlatformNanoapp::handleEvent(uint32_t senderInstanceId, uint16_t eventType,
55 const void *eventData) {
56 if (!isUimgApp()) {
57 slpiForceBigImage();
58 }
59
60 mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData);
61 }
62
end()63 void PlatformNanoapp::end() {
64 if (!isUimgApp()) {
65 slpiForceBigImage();
66 }
67
68 mAppInfo->entryPoints.end();
69 closeNanoapp();
70 }
71
setAppInfo(uint64_t appId,uint32_t appVersion,const char * appFilename,uint32_t targetApiVersion)72 bool PlatformNanoappBase::setAppInfo(uint64_t appId, uint32_t appVersion,
73 const char *appFilename,
74 uint32_t targetApiVersion) {
75 CHRE_ASSERT(!isLoaded());
76 mExpectedAppId = appId;
77 mExpectedAppVersion = appVersion;
78 mExpectedTargetApiVersion = targetApiVersion;
79 size_t appFilenameLen = strlen(appFilename) + 1;
80 mAppFilename = static_cast<char *>(memoryAllocBigImage(appFilenameLen));
81
82 bool success = false;
83 if (mAppFilename == nullptr) {
84 LOG_OOM();
85 } else {
86 memcpy(static_cast<void *>(mAppFilename), appFilename, appFilenameLen);
87 success = true;
88 }
89
90 return success;
91 }
92
reserveBuffer(uint64_t appId,uint32_t appVersion,uint32_t,size_t appBinaryLen,uint32_t targetApiVersion)93 bool PlatformNanoappBase::reserveBuffer(uint64_t appId, uint32_t appVersion,
94 uint32_t /* appFlags */,
95 size_t appBinaryLen,
96 uint32_t targetApiVersion) {
97 CHRE_ASSERT(!isLoaded());
98 bool success = false;
99 constexpr size_t kMaxAppSize = 2 * 1024 * 1024; // 2 MiB
100
101 if (appBinaryLen > kMaxAppSize) {
102 LOGE("Rejecting app size %zu above limit %zu", appBinaryLen, kMaxAppSize);
103 } else {
104 mAppBinary = memoryAllocBigImage(appBinaryLen);
105 if (mAppBinary == nullptr) {
106 LOGE("Couldn't allocate %zu byte buffer for nanoapp 0x%016" PRIx64,
107 appBinaryLen, appId);
108 } else {
109 mExpectedAppId = appId;
110 mExpectedAppVersion = appVersion;
111 mExpectedTargetApiVersion = targetApiVersion;
112 mAppBinaryLen = appBinaryLen;
113 success = true;
114 }
115 }
116
117 return success;
118 }
119
copyNanoappFragment(const void * buffer,size_t bufferLen)120 bool PlatformNanoappBase::copyNanoappFragment(const void *buffer,
121 size_t bufferLen) {
122 CHRE_ASSERT(!isLoaded());
123
124 bool success = true;
125 if (mBytesLoaded + bufferLen > mAppBinaryLen) {
126 LOGE("Overflow: cannot load %zu bytes to %zu/%zu nanoapp binary buffer",
127 bufferLen, mBytesLoaded, mAppBinaryLen);
128 success = false;
129 } else {
130 uint8_t *binaryBuffer = static_cast<uint8_t *>(mAppBinary) + mBytesLoaded;
131 memcpy(binaryBuffer, buffer, bufferLen);
132 mBytesLoaded += bufferLen;
133 }
134
135 return success;
136 }
137
loadStatic(const struct chreNslNanoappInfo * appInfo)138 void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
139 CHRE_ASSERT(!isLoaded());
140 mIsStatic = true;
141 mAppInfo = appInfo;
142 }
143
isLoaded() const144 bool PlatformNanoappBase::isLoaded() const {
145 return (mIsStatic ||
146 (mAppBinary != nullptr && mBytesLoaded == mAppBinaryLen) ||
147 mDsoHandle != nullptr || mAppFilename != nullptr);
148 }
149
isUimgApp() const150 bool PlatformNanoappBase::isUimgApp() const {
151 return mIsUimgApp;
152 }
153
closeNanoapp()154 void PlatformNanoappBase::closeNanoapp() {
155 if (mDsoHandle != nullptr) {
156 mAppInfo = nullptr;
157 if (dlclose(mDsoHandle) != 0) {
158 LOGE("dlclose failed: %s", dlerror());
159 }
160 mDsoHandle = nullptr;
161 }
162 }
163
openNanoapp()164 bool PlatformNanoappBase::openNanoapp() {
165 bool success = false;
166
167 if (mIsStatic) {
168 success = true;
169 } else if (mAppBinary != nullptr) {
170 success = openNanoappFromBuffer();
171 } else if (mAppFilename != nullptr) {
172 success = openNanoappFromFile();
173 } else {
174 CHRE_ASSERT(false);
175 }
176
177 // Ensure any allocated memory hanging around is properly cleaned up.
178 if (!success) {
179 closeNanoapp();
180 }
181
182 // Save this flag locally since it may be referenced while the system is in
183 // micro-image
184 if (mAppInfo != nullptr) {
185 mIsUimgApp = mAppInfo->isTcmNanoapp;
186 }
187
188 return success;
189 }
190
openNanoappFromBuffer()191 bool PlatformNanoappBase::openNanoappFromBuffer() {
192 CHRE_ASSERT(mAppBinary != nullptr);
193 CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
194
195 // Populate a filename string (just a requirement of the dlopenbuf API)
196 constexpr size_t kMaxFilenameLen = 17;
197 char filename[kMaxFilenameLen];
198 snprintf(filename, sizeof(filename), "%016" PRIx64, mExpectedAppId);
199
200 mDsoHandle = dlopenbuf(filename, static_cast<const char *>(mAppBinary),
201 static_cast<int>(mAppBinaryLen), RTLD_NOW);
202 memoryFreeBigImage(mAppBinary);
203 mAppBinary = nullptr;
204
205 return verifyNanoappInfo();
206 }
207
openNanoappFromFile()208 bool PlatformNanoappBase::openNanoappFromFile() {
209 CHRE_ASSERT(mAppFilename != nullptr);
210 CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
211
212 mDsoHandle = dlopen(mAppFilename, RTLD_NOW);
213 memoryFreeBigImage(mAppFilename);
214 mAppFilename = nullptr;
215
216 return verifyNanoappInfo();
217 }
218
verifyNanoappInfo()219 bool PlatformNanoappBase::verifyNanoappInfo() {
220 bool success = false;
221
222 if (mDsoHandle == nullptr) {
223 LOGE("No nanoapp info to verify: %s", dlerror());
224 } else {
225 mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
226 dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
227 if (mAppInfo == nullptr) {
228 LOGE("Failed to find app info symbol: %s", dlerror());
229 } else {
230 success = validateAppInfo(mExpectedAppId, mExpectedAppVersion,
231 mExpectedTargetApiVersion, mAppInfo);
232 if (!success) {
233 mAppInfo = nullptr;
234 } else {
235 LOGI("Nanoapp loaded: %s (0x%016" PRIx64 ") version 0x%" PRIx32
236 " (%s) uimg %d system %d",
237 mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion,
238 getAppVersionString(), mAppInfo->isTcmNanoapp,
239 mAppInfo->isSystemNanoapp);
240 if (mAppInfo->structMinorVersion >=
241 CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION_3) {
242 LOGI("Nanoapp permissions: 0x%" PRIx32, mAppInfo->appPermissions);
243 }
244 }
245 }
246 }
247
248 return success;
249 }
250
getAppVersionString() const251 const char *PlatformNanoappBase::getAppVersionString() const {
252 const char *versionString = "<undefined>";
253 if (mAppInfo != nullptr && mAppInfo->structMinorVersion >= 2 &&
254 mAppInfo->appVersionString != NULL) {
255 size_t appVersionStringLength = strlen(mAppInfo->appVersionString);
256
257 size_t offset = 0;
258 for (size_t i = 0; i < appVersionStringLength; i++) {
259 size_t newOffset = i + 1;
260 if (mAppInfo->appVersionString[i] == '@' &&
261 newOffset < appVersionStringLength) {
262 offset = newOffset;
263 break;
264 }
265 }
266
267 versionString = &mAppInfo->appVersionString[offset];
268 }
269
270 return versionString;
271 }
272
getAppId() const273 uint64_t PlatformNanoapp::getAppId() const {
274 return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
275 }
276
getAppVersion() const277 uint32_t PlatformNanoapp::getAppVersion() const {
278 return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
279 }
280
getTargetApiVersion() const281 uint32_t PlatformNanoapp::getTargetApiVersion() const {
282 return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion
283 : mExpectedTargetApiVersion;
284 }
285
supportsAppPermissions() const286 bool PlatformNanoapp::supportsAppPermissions() const {
287 return (mAppInfo != nullptr) ? (mAppInfo->structMinorVersion >=
288 CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION_3)
289 : false;
290 }
291
getAppPermissions() const292 uint32_t PlatformNanoapp::getAppPermissions() const {
293 return (supportsAppPermissions())
294 ? mAppInfo->appPermissions
295 : static_cast<uint32_t>(chre::NanoappPermissions::CHRE_PERMS_NONE);
296 }
297
getAppName() const298 const char *PlatformNanoapp::getAppName() const {
299 return (mAppInfo != nullptr) ? mAppInfo->name : "Unknown";
300 }
301
isSystemNanoapp() const302 bool PlatformNanoapp::isSystemNanoapp() const {
303 // Right now, we assume that system nanoapps are always static nanoapps. Since
304 // mAppInfo can only be null either prior to loading the app (in which case
305 // this function is not expected to return a valid value anyway), or when a
306 // dynamic nanoapp is not running, "false" is the correct return value in that
307 // case.
308 return (mAppInfo != nullptr) ? mAppInfo->isSystemNanoapp : false;
309 }
310
logStateToBuffer(DebugDumpWrapper & debugDump) const311 void PlatformNanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const {
312 if (mAppInfo != nullptr) {
313 debugDump.print("%s (%s) @ %s", mAppInfo->name, mAppInfo->vendor,
314 getAppVersionString());
315 }
316 }
317
318 } // namespace chre
319