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_api/chre/version.h"
29
30 #include "dlfcn.h"
31
32 #include <inttypes.h>
33 #include <string.h>
34
35 namespace chre {
36
~PlatformNanoapp()37 PlatformNanoapp::~PlatformNanoapp() {
38 closeNanoapp();
39 if (mAppBinary != nullptr) {
40 memoryFreeBigImage(mAppBinary);
41 }
42 }
43
start()44 bool PlatformNanoapp::start() {
45 // Invoke the start entry point after successfully opening the app
46 if (!isUimgApp()) {
47 slpiForceBigImage();
48 }
49
50 return openNanoapp() && mAppInfo->entryPoints.start();
51 }
52
handleEvent(uint32_t senderInstanceId,uint16_t eventType,const void * eventData)53 void PlatformNanoapp::handleEvent(uint32_t senderInstanceId,
54 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
reserveBuffer(uint64_t appId,uint32_t appVersion,size_t appBinaryLen)72 bool PlatformNanoappBase::reserveBuffer(
73 uint64_t appId, uint32_t appVersion, size_t appBinaryLen) {
74 CHRE_ASSERT(!isLoaded());
75 bool success = false;
76 constexpr size_t kMaxAppSize = 2 * 1024 * 1024; // 2 MiB
77
78 if (appBinaryLen > kMaxAppSize) {
79 LOGE("Rejecting app size %zu above limit %zu", appBinaryLen, kMaxAppSize);
80 } else {
81 mAppBinary = memoryAllocBigImage(appBinaryLen);
82 if (mAppBinary == nullptr) {
83 LOGE("Couldn't allocate %zu byte buffer for nanoapp 0x%016" PRIx64,
84 appBinaryLen, appId);
85 } else {
86 mExpectedAppId = appId;
87 mExpectedAppVersion = appVersion;
88 mAppBinaryLen = appBinaryLen;
89 success = true;
90 }
91 }
92
93 return success;
94 }
95
copyNanoappFragment(const void * buffer,size_t bufferLen)96 bool PlatformNanoappBase::copyNanoappFragment(
97 const void *buffer, size_t bufferLen) {
98 CHRE_ASSERT(!isLoaded());
99
100 bool success = true;
101 if (mBytesLoaded + bufferLen > mAppBinaryLen) {
102 LOGE("Overflow: cannot load %zu bytes to %zu/%zu nanoapp binary buffer",
103 bufferLen, mBytesLoaded, mAppBinaryLen);
104 success = false;
105 } else {
106 uint8_t *binaryBuffer = static_cast<uint8_t *>(mAppBinary) + mBytesLoaded;
107 memcpy(binaryBuffer, buffer, bufferLen);
108 mBytesLoaded += bufferLen;
109 }
110
111 return success;
112 }
113
loadFromFile(uint64_t appId,const char * filename)114 void PlatformNanoappBase::loadFromFile(uint64_t appId, const char *filename) {
115 CHRE_ASSERT(!isLoaded());
116 mExpectedAppId = appId;
117 mFilename = filename;
118 }
119
loadStatic(const struct chreNslNanoappInfo * appInfo)120 void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
121 CHRE_ASSERT(!isLoaded());
122 mIsStatic = true;
123 mAppInfo = appInfo;
124 }
125
isLoaded() const126 bool PlatformNanoappBase::isLoaded() const {
127 return (mIsStatic || (mAppBinary != nullptr && mBytesLoaded == mAppBinaryLen)
128 || mDsoHandle != nullptr);
129 }
130
isUimgApp() const131 bool PlatformNanoappBase::isUimgApp() const {
132 return mIsUimgApp;
133 }
134
closeNanoapp()135 void PlatformNanoappBase::closeNanoapp() {
136 if (mDsoHandle != nullptr) {
137 if (dlclose(mDsoHandle) != 0) {
138 LOGE("dlclose failed: %s", dlerror());
139 }
140 mDsoHandle = nullptr;
141 }
142 }
143
openNanoapp()144 bool PlatformNanoappBase::openNanoapp() {
145 bool success = false;
146
147 if (mIsStatic) {
148 success = true;
149 } else if (mFilename != nullptr) {
150 success = openNanoappFromFile();
151 } else if (mAppBinary != nullptr) {
152 success = openNanoappFromBuffer();
153 } else {
154 CHRE_ASSERT(false);
155 }
156
157 // Save this flag locally since it may be referenced while the system is in
158 // micro-image
159 if (mAppInfo != nullptr) {
160 mIsUimgApp = mAppInfo->isTcmNanoapp;
161 }
162
163 return success;
164 }
165
openNanoappFromBuffer()166 bool PlatformNanoappBase::openNanoappFromBuffer() {
167 CHRE_ASSERT(mAppBinary != nullptr);
168 CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
169 bool success = false;
170
171 // Populate a filename string (just a requirement of the dlopenbuf API)
172 constexpr size_t kMaxFilenameLen = 17;
173 char filename[kMaxFilenameLen];
174 snprintf(filename, sizeof(filename), "%016" PRIx64, mExpectedAppId);
175
176 mDsoHandle = dlopenbuf(
177 filename, static_cast<const char *>(mAppBinary),
178 static_cast<int>(mAppBinaryLen), RTLD_NOW);
179 if (mDsoHandle == nullptr) {
180 LOGE("Failed to load nanoapp: %s", dlerror());
181 } else {
182 mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
183 dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
184 if (mAppInfo == nullptr) {
185 LOGE("Failed to find app info symbol: %s", dlerror());
186 } else {
187 success = validateAppInfo(mExpectedAppId, mExpectedAppVersion, mAppInfo);
188 if (!success) {
189 mAppInfo = nullptr;
190 } else {
191 LOGI("Successfully loaded nanoapp: %s (0x%016" PRIx64 ") version 0x%"
192 PRIx32 " uimg %d system %d", mAppInfo->name, mAppInfo->appId,
193 mAppInfo->appVersion, mAppInfo->isTcmNanoapp,
194 mAppInfo->isSystemNanoapp);
195 memoryFreeBigImage(mAppBinary);
196 mAppBinary = nullptr;
197 }
198 }
199 }
200
201 return success;
202 }
203
openNanoappFromFile()204 bool PlatformNanoappBase::openNanoappFromFile() {
205 CHRE_ASSERT(mFilename != nullptr);
206 CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
207 bool success = false;
208
209 mDsoHandle = dlopen(mFilename, RTLD_NOW);
210 if (mDsoHandle == nullptr) {
211 LOGE("Failed to load nanoapp from file %s: %s", mFilename, dlerror());
212 } else {
213 mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
214 dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
215 if (mAppInfo == nullptr) {
216 LOGE("Failed to find app info symbol in %s: %s", mFilename, dlerror());
217 } else {
218 success = validateAppInfo(mExpectedAppId, 0, mAppInfo,
219 true /* ignoreAppVersion */);
220 if (!success) {
221 mAppInfo = nullptr;
222 } else {
223 LOGI("Successfully loaded nanoapp %s (0x%016" PRIx64 ") version 0x%"
224 PRIx32 " uimg %d system %d from file %s", mAppInfo->name,
225 mAppInfo->appId, mAppInfo->appVersion, mAppInfo->isTcmNanoapp,
226 mAppInfo->isSystemNanoapp, mFilename);
227 // Save the app version field in case this app gets disabled and we
228 // still get a query request for the version later on. We are OK not
229 // knowing the version prior to the first load because we assume that
230 // nanoapps loaded via file are done at CHRE initialization time.
231 mExpectedAppVersion = mAppInfo->appVersion;
232 }
233 }
234 }
235
236 return success;
237 }
238
getAppId() const239 uint64_t PlatformNanoapp::getAppId() const {
240 return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
241 }
242
getAppVersion() const243 uint32_t PlatformNanoapp::getAppVersion() const {
244 return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
245 }
246
getTargetApiVersion() const247 uint32_t PlatformNanoapp::getTargetApiVersion() const {
248 return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion : 0;
249 }
250
isSystemNanoapp() const251 bool PlatformNanoapp::isSystemNanoapp() const {
252 // Right now, we assume that system nanoapps are always static nanoapps. Since
253 // mAppInfo can only be null either prior to loading the app (in which case
254 // this function is not expected to return a valid value anyway), or when a
255 // dynamic nanoapp is not running, "false" is the correct return value in that
256 // case.
257 return (mAppInfo != nullptr) ? mAppInfo->isSystemNanoapp : false;
258 }
259
logStateToBuffer(char * buffer,size_t * bufferPos,size_t bufferSize) const260 bool PlatformNanoapp::logStateToBuffer(char *buffer, size_t *bufferPos,
261 size_t bufferSize) const {
262 bool success = true;
263 if (mAppInfo != nullptr) {
264 success &= debugDumpPrint(buffer, bufferPos, bufferSize, " %s: vendor=\"%s\"",
265 mAppInfo->name, mAppInfo->vendor);
266 }
267 return success;
268 }
269
270 } // namespace chre
271