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 35namespace chre { 36 37PlatformNanoapp::~PlatformNanoapp() { 38 closeNanoapp(); 39 if (mAppBinary != nullptr) { 40 memoryFreeBigImage(mAppBinary); 41 } 42} 43 44bool 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 53void 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 63void PlatformNanoapp::end() { 64 if (!isUimgApp()) { 65 slpiForceBigImage(); 66 } 67 68 mAppInfo->entryPoints.end(); 69 closeNanoapp(); 70} 71 72bool 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 98void PlatformNanoappBase::loadFromFile(uint64_t appId, const char *filename) { 99 CHRE_ASSERT(!isLoaded()); 100 mExpectedAppId = appId; 101 mFilename = filename; 102} 103 104void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) { 105 CHRE_ASSERT(!isLoaded()); 106 mIsStatic = true; 107 mAppInfo = appInfo; 108} 109 110bool PlatformNanoappBase::isLoaded() const { 111 return (mIsStatic || mAppBinary != nullptr || mDsoHandle != nullptr); 112} 113 114bool PlatformNanoappBase::isUimgApp() const { 115 return mIsUimgApp; 116} 117 118void PlatformNanoappBase::closeNanoapp() { 119 if (mDsoHandle != nullptr) { 120 if (dlclose(mDsoHandle) != 0) { 121 LOGE("dlclose failed: %s", dlerror()); 122 } 123 mDsoHandle = nullptr; 124 } 125} 126 127bool 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 149bool 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 187bool 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 222uint64_t PlatformNanoapp::getAppId() const { 223 return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId; 224} 225 226uint32_t PlatformNanoapp::getAppVersion() const { 227 return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion; 228} 229 230uint32_t PlatformNanoapp::getTargetApiVersion() const { 231 return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion : 0; 232} 233 234bool 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 243bool 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