/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "chre/platform/platform_nanoapp.h" #include #include #include "chre/platform/assert.h" #include "chre/platform/log.h" #include "chre/platform/shared/authentication.h" #include "chre/platform/shared/nanoapp_dso_util.h" #include "chre/platform/shared/nanoapp_loader.h" #include "chre/util/macros.h" #include "chre/util/system/napp_header_utils.h" #include "chre/util/system/napp_permissions.h" #include "chre_api/chre/version.h" namespace chre { namespace { const char kDefaultAppVersionString[] = ""; size_t kDefaultAppVersionStringSize = ARRAY_SIZE(kDefaultAppVersionString); } // namespace PlatformNanoapp::~PlatformNanoapp() { closeNanoapp(); if (mAppBinary != nullptr) { forceDramAccess(); nanoappBinaryDramFree(mAppBinary); } } bool PlatformNanoapp::start() { //! Always force DRAM access when starting since nanoapps are loaded via DRAM. forceDramAccess(); bool success = false; if (!openNanoapp()) { LOGE("Failed to open nanoapp"); } else if (mAppInfo == nullptr) { LOGE("Null app info!"); } else { success = mAppInfo->entryPoints.start(); } return success; } void PlatformNanoapp::handleEvent(uint32_t senderInstanceId, uint16_t eventType, const void *eventData) { enableDramAccessIfRequired(); mAppInfo->entryPoints.handleEvent(senderInstanceId, eventType, eventData); } void PlatformNanoapp::end() { enableDramAccessIfRequired(); mAppInfo->entryPoints.end(); closeNanoapp(); } uint64_t PlatformNanoapp::getAppId() const { // TODO (karthikmb/stange): Ideally, we should store the metadata as // variables in TCM, to avoid bumping into DRAM for basic queries. enableDramAccessIfRequired(); return (mAppInfo != nullptr) ? mAppInfo->appId : mExpectedAppId; } uint32_t PlatformNanoapp::getAppVersion() const { enableDramAccessIfRequired(); return (mAppInfo != nullptr) ? mAppInfo->appVersion : mExpectedAppVersion; } bool PlatformNanoapp::supportsAppPermissions() const { return (mAppInfo != nullptr) ? (mAppInfo->structMinorVersion >= CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION) : false; } uint32_t PlatformNanoapp::getAppPermissions() const { return (supportsAppPermissions()) ? mAppInfo->appPermissions : static_cast(chre::NanoappPermissions::CHRE_PERMS_NONE); } const char *PlatformNanoapp::getAppName() const { enableDramAccessIfRequired(); return (mAppInfo != nullptr) ? mAppInfo->name : "Unknown"; } uint32_t PlatformNanoapp::getTargetApiVersion() const { enableDramAccessIfRequired(); return (mAppInfo != nullptr) ? mAppInfo->targetApiVersion : mExpectedTargetApiVersion; } bool PlatformNanoapp::isSystemNanoapp() const { enableDramAccessIfRequired(); return (mAppInfo != nullptr && mAppInfo->isSystemNanoapp); } void PlatformNanoapp::logStateToBuffer(DebugDumpWrapper &debugDump) const { if (mAppInfo != nullptr) { enableDramAccessIfRequired(); size_t versionLen = 0; const char *version = getAppVersionString(&versionLen); debugDump.print("%s (%s) @ build: %.*s", mAppInfo->name, mAppInfo->vendor, versionLen, version); } } const char *PlatformNanoappBase::getAppVersionString(size_t *length) const { const char *versionString = kDefaultAppVersionString; *length = kDefaultAppVersionStringSize; enableDramAccessIfRequired(); if (mAppUnstableId != nullptr) { size_t appVersionStringLength = strlen(mAppUnstableId); //! The unstable ID is expected to be in the format of //! =@. Use this expected layout //! knowledge to parse the string and only return the build ID portion that //! should be printed. size_t startOffset = appVersionStringLength; for (size_t i = 0; i < appVersionStringLength; i++) { size_t offset = i + 1; if (startOffset == appVersionStringLength && mAppUnstableId[i] == '@') { startOffset = offset; } } if (startOffset < appVersionStringLength) { versionString = &mAppUnstableId[startOffset]; *length = appVersionStringLength - startOffset; } } return versionString; } bool PlatformNanoappBase::isLoaded() const { return (mIsStatic || (mAppBinary != nullptr && mBytesLoaded == mAppBinaryLen) || mDsoHandle != nullptr || mAppFilename != nullptr); } bool PlatformNanoappBase::isTcmApp() const { return mIsTcmNanoapp; } void PlatformNanoappBase::loadStatic(const struct chreNslNanoappInfo *appInfo) { CHRE_ASSERT(!isLoaded()); mIsStatic = true; mAppInfo = appInfo; } bool PlatformNanoappBase::reserveBuffer(uint64_t appId, uint32_t appVersion, uint32_t appFlags, size_t appBinaryLen, uint32_t targetApiVersion) { CHRE_ASSERT(!isLoaded()); forceDramAccess(); bool success = false; mAppBinary = nanoappBinaryDramAlloc(appBinaryLen); bool isSigned = IS_BIT_SET(appFlags, CHRE_NAPP_HEADER_SIGNED); if (!isSigned) { LOGE("Unable to load unsigned nanoapps"); } else if (mAppBinary == nullptr) { LOG_OOM(); } else { bool tcmCapable = IS_BIT_SET(appFlags, CHRE_NAPP_HEADER_TCM_CAPABLE); mExpectedAppId = appId; mExpectedAppVersion = appVersion; mExpectedTargetApiVersion = targetApiVersion; mExpectedTcmCapable = tcmCapable; mAppBinaryLen = appBinaryLen; success = true; } return success; } bool PlatformNanoappBase::copyNanoappFragment(const void *buffer, size_t bufferLen) { CHRE_ASSERT(!isLoaded()); forceDramAccess(); bool success = true; if ((mBytesLoaded + bufferLen) > mAppBinaryLen) { LOGE("Overflow: cannot load %zu bytes to %zu/%zu nanoapp binary buffer", bufferLen, mBytesLoaded, mAppBinaryLen); success = false; } else { uint8_t *binaryBuffer = static_cast(mAppBinary) + mBytesLoaded; memcpy(binaryBuffer, buffer, bufferLen); mBytesLoaded += bufferLen; } return success; } bool PlatformNanoappBase::verifyNanoappInfo() { bool success = false; if (mDsoHandle == nullptr) { LOGE("No nanoapp info to verify"); } else { mAppInfo = static_cast( dlsym(mDsoHandle, CHRE_NSL_DSO_NANOAPP_INFO_SYMBOL_NAME)); if (mAppInfo == nullptr) { LOGE("Failed to find app info symbol"); } else { mAppUnstableId = mAppInfo->appVersionString; if (mAppUnstableId == nullptr) { LOGE("Failed to find unstable ID symbol"); } else { success = validateAppInfo(mExpectedAppId, mExpectedAppVersion, mExpectedTargetApiVersion, mAppInfo); if (success && mAppInfo->isTcmNanoapp != mExpectedTcmCapable) { success = false; LOGE("Expected TCM nanoapp %d found %d", mExpectedTcmCapable, mAppInfo->isTcmNanoapp); } if (!success) { mAppInfo = nullptr; } else { LOGI("Nanoapp loaded: %s (0x%016" PRIx64 ") version 0x%" PRIx32 " (%s) uimg %d system %d", mAppInfo->name, mAppInfo->appId, mAppInfo->appVersion, mAppInfo->appVersionString, mAppInfo->isTcmNanoapp, mAppInfo->isSystemNanoapp); if (mAppInfo->structMinorVersion >= CHRE_NSL_NANOAPP_INFO_STRUCT_MINOR_VERSION) { LOGI("Nanoapp permissions: 0x%" PRIx32, mAppInfo->appPermissions); } } } } } return success; } bool PlatformNanoappBase::openNanoapp() { bool success = false; if (mIsStatic) { success = true; } else if (mAppBinary != nullptr) { //! The true start of the binary will be after the authentication header. //! Use the returned value from authenticateBinary to ensure dlopenbuf has //! the starting address to a valid ELF. void *binaryStart = mAppBinary; if (!authenticateBinary(mAppBinary, &binaryStart)) { LOGE("Unable to authenticate 0x%" PRIx64 " not loading", mExpectedAppId); } else if (mDsoHandle != nullptr) { LOGE("Trying to reopen an existing buffer"); } else { mDsoHandle = dlopenbuf(binaryStart, mExpectedTcmCapable); success = verifyNanoappInfo(); } } if (!success) { closeNanoapp(); } if (mAppBinary != nullptr) { nanoappBinaryDramFree(mAppBinary); mAppBinary = nullptr; } // Save this flag locally since it may be referenced while the system is in // TCM-only mode. if (mAppInfo != nullptr) { mIsTcmNanoapp = mAppInfo->isTcmNanoapp; } return success; } void PlatformNanoappBase::closeNanoapp() { if (mDsoHandle != nullptr) { // Force DRAM access since dl* functions are only safe to call with DRAM // available. forceDramAccess(); mAppInfo = nullptr; if (dlclose(mDsoHandle) != 0) { LOGE("dlclose failed"); } mDsoHandle = nullptr; } } } // namespace chre