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
loadFromBuffer(uint64_t appId,uint32_t appVersion,const void * appBinary,size_t appBinaryLen)72 bool PlatformNanoappBase::loadFromBuffer(uint64_t appId, uint32_t appVersion,
73 const void *appBinary,
74 size_t appBinaryLen) {
75 CHRE_ASSERT(!isLoaded());
76 bool success = false;
77 constexpr size_t kMaxAppSize = 2 * 1024 * 1024; // 2 MiB
78
79 if (appBinaryLen > kMaxAppSize) {
80 LOGE("Rejecting app size %zu above limit %zu", appBinaryLen, kMaxAppSize);
81 } else {
82 mAppBinary = memoryAllocBigImage(appBinaryLen);
83 if (mAppBinary == nullptr) {
84 LOGE("Couldn't allocate %zu byte buffer for nanoapp 0x%016" PRIx64,
85 appBinaryLen, appId);
86 } else {
87 mExpectedAppId = appId;
88 mExpectedAppVersion = appVersion;
89 mAppBinaryLen = appBinaryLen;
90 memcpy(mAppBinary, appBinary, appBinaryLen);
91 success = true;
92 }
93 }
94
95 return success;
96 }
97
loadFromFile(uint64_t appId,const char * filename)98 void PlatformNanoappBase::loadFromFile(uint64_t appId, const char *filename) {
99 CHRE_ASSERT(!isLoaded());
100 mExpectedAppId = appId;
101 mFilename = filename;
102 }
103
loadStatic(const struct chreNslNanoappInfo * appInfo)104 void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) {
105 CHRE_ASSERT(!isLoaded());
106 mIsStatic = true;
107 mAppInfo = appInfo;
108 }
109
isLoaded() const110 bool PlatformNanoappBase::isLoaded() const {
111 return (mIsStatic || mAppBinary != nullptr || mDsoHandle != nullptr);
112 }
113
isUimgApp() const114 bool PlatformNanoappBase::isUimgApp() const {
115 return mIsUimgApp;
116 }
117
closeNanoapp()118 void PlatformNanoappBase::closeNanoapp() {
119 if (mDsoHandle != nullptr) {
120 if (dlclose(mDsoHandle) != 0) {
121 LOGE("dlclose failed: %s", dlerror());
122 }
123 mDsoHandle = nullptr;
124 }
125 }
126
openNanoapp()127 bool PlatformNanoappBase::openNanoapp() {
128 bool success = false;
129
130 if (mIsStatic) {
131 success = true;
132 } else if (mFilename != nullptr) {
133 success = openNanoappFromFile();
134 } else if (mAppBinary != nullptr) {
135 success = openNanoappFromBuffer();
136 } else {
137 CHRE_ASSERT(false);
138 }
139
140 // Save this flag locally since it may be referenced while the system is in
141 // micro-image
142 if (mAppInfo != nullptr) {
143 mIsUimgApp = mAppInfo->isTcmNanoapp;
144 }
145
146 return success;
147 }
148
openNanoappFromBuffer()149 bool PlatformNanoappBase::openNanoappFromBuffer() {
150 CHRE_ASSERT(mAppBinary != nullptr);
151 CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
152 bool success = false;
153
154 // Populate a filename string (just a requirement of the dlopenbuf API)
155 constexpr size_t kMaxFilenameLen = 17;
156 char filename[kMaxFilenameLen];
157 snprintf(filename, sizeof(filename), "%016" PRIx64, mExpectedAppId);
158
159 mDsoHandle = dlopenbuf(
160 filename, static_cast<const char *>(mAppBinary),
161 static_cast<int>(mAppBinaryLen), RTLD_NOW);
162 if (mDsoHandle == nullptr) {
163 LOGE("Failed to load nanoapp: %s", dlerror());
164 } else {
165 mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
166 dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
167 if (mAppInfo == nullptr) {
168 LOGE("Failed to find app info symbol: %s", dlerror());
169 } else {
170 success = validateAppInfo(mExpectedAppId, mExpectedAppVersion, mAppInfo);
171 if (!success) {
172 mAppInfo = nullptr;
173 } else {
174 LOGI("Successfully loaded nanoapp: %s (0x%016" PRIx64 ") version 0x%"
175 PRIx32 " uimg %d system %d", mAppInfo->name, mAppInfo->appId,
176 mAppInfo->appVersion, mAppInfo->isTcmNanoapp,
177 mAppInfo->isSystemNanoapp);
178 memoryFreeBigImage(mAppBinary);
179 mAppBinary = nullptr;
180 }
181 }
182 }
183
184 return success;
185 }
186
openNanoappFromFile()187 bool PlatformNanoappBase::openNanoappFromFile() {
188 CHRE_ASSERT(mFilename != nullptr);
189 CHRE_ASSERT_LOG(mDsoHandle == nullptr, "Re-opening nanoapp");
190 bool success = false;
191
192 mDsoHandle = dlopen(mFilename, RTLD_NOW);
193 if (mDsoHandle == nullptr) {
194 LOGE("Failed to load nanoapp from file %s: %s", mFilename, dlerror());
195 } else {
196 mAppInfo = static_cast<const struct chreNslNanoappInfo *>(
197 dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME));
198 if (mAppInfo == nullptr) {
199 LOGE("Failed to find app info symbol in %s: %s", mFilename, dlerror());
200 } else {
201 success = validateAppInfo(mExpectedAppId, 0, mAppInfo,
202 true /* ignoreAppVersion */);
203 if (!success) {
204 mAppInfo = nullptr;
205 } else {
206 LOGI("Successfully loaded nanoapp %s (0x%016" PRIx64 ") version 0x%"
207 PRIx32 " uimg %d system %d from file %s", mAppInfo->name,
208 mAppInfo->appId, mAppInfo->appVersion, mAppInfo->isTcmNanoapp,
209 mAppInfo->isSystemNanoapp, mFilename);
210 // Save the app version field in case this app gets disabled and we
211 // still get a query request for the version later on. We are OK not
212 // knowing the version prior to the first load because we assume that
213 // nanoapps loaded via file are done at CHRE initialization time.
214 mExpectedAppVersion = mAppInfo->appVersion;
215 }
216 }
217 }
218
219 return success;
220 }
221
getAppId() const222 uint64_t PlatformNanoapp::getAppId() const {
223 return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId;
224 }
225
getAppVersion() const226 uint32_t PlatformNanoapp::getAppVersion() const {
227 return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion;
228 }
229
getTargetApiVersion() const230 uint32_t PlatformNanoapp::getTargetApiVersion() const {
231 return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion : 0;
232 }
233
isSystemNanoapp() const234 bool PlatformNanoapp::isSystemNanoapp() const {
235 // Right now, we assume that system nanoapps are always static nanoapps. Since
236 // mAppInfo can only be null either prior to loading the app (in which case
237 // this function is not expected to return a valid value anyway), or when a
238 // dynamic nanoapp is not running, "false" is the correct return value in that
239 // case.
240 return (mAppInfo != nullptr) ? mAppInfo->isSystemNanoapp : false;
241 }
242
logStateToBuffer(char * buffer,size_t * bufferPos,size_t bufferSize) const243 bool PlatformNanoapp::logStateToBuffer(char *buffer, size_t *bufferPos,
244 size_t bufferSize) const {
245 bool success = true;
246 if (mAppInfo != nullptr) {
247 success &= debugDumpPrint(buffer, bufferPos, bufferSize, " %s: vendor=\"%s\"",
248 mAppInfo->name, mAppInfo->vendor);
249 }
250 return success;
251 }
252
253 } // namespace chre
254