blob: 9f5115ae18f718537d4b48bf9492949455ee90f1 [file] [log] [blame]
/*
* 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 <dlfcn.h>
#include <cinttypes>
#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[] = "<undefined>";
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<uint32_t>(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
//! <descriptor>=<nanoapp_name>@<build_id>. 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<uint8_t *>(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<const struct chreNslNanoappInfo *>(
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