| /* |
| * Copyright (C) 2016 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. |
| */ |
| |
| #define LOG_TAG "NanohubHAL" |
| |
| #include <cassert> |
| #include <cerrno> |
| #include <cinttypes> |
| |
| #include <endian.h> |
| |
| #include <vector> |
| |
| #include <utils/Log.h> |
| |
| #include <endian.h> |
| |
| #include <hardware/context_hub.h> |
| #include "nanohub_perdevice.h" |
| #include "system_comms.h" |
| #include "nanohubhal.h" |
| |
| namespace android { |
| |
| namespace nanohub { |
| |
| static void readAppName(MessageBuf &buf, hub_app_name_t &name) |
| { |
| name.id = buf.readU64(); |
| } |
| |
| static void writeAppName(MessageBuf &buf, const hub_app_name_t &name) |
| { |
| buf.writeU64(name.id); |
| } |
| |
| static void readNanohubAppInfo(MessageBuf &buf, NanohubAppInfo &info) |
| { |
| size_t pos = buf.getPos(); |
| readAppName(buf, info.name); |
| info.version = buf.readU32(); |
| info.flashUse = buf.readU32(); |
| info.ramUse = buf.readU32(); |
| if ((buf.getPos() - pos) != sizeof(info)) { |
| ALOGE("%s: failed to read object", __func__); |
| } |
| } |
| |
| static void readNanohubMemInfo(MessageBuf &buf, NanohubMemInfo &mi) |
| { |
| size_t pos = buf.getPos(); |
| mi.flashSz = buf.readU32(); |
| mi.blSz = buf.readU32(); |
| mi.osSz = buf.readU32(); |
| mi.sharedSz = buf.readU32(); |
| mi.eeSz = buf.readU32(); |
| mi.ramSz = buf.readU32(); |
| |
| mi.blUse = buf.readU32(); |
| mi.osUse = buf.readU32(); |
| mi.sharedUse = buf.readU32(); |
| mi.eeUse = buf.readU32(); |
| mi.ramUse = buf.readU32(); |
| if ((buf.getPos() - pos) != sizeof(mi)) { |
| ALOGE("%s: failed to read object", __func__); |
| } |
| } |
| |
| NanohubRsp::NanohubRsp(MessageBuf &buf, bool no_status) |
| { |
| // all responses start with command |
| // most of them have 4-byte status (result code) |
| cmd = buf.readU8(); |
| if (!buf.getSize()) { |
| status = -EINVAL; |
| } else if (no_status) { |
| status = 0; |
| } else { |
| status = buf.readU32(); |
| } |
| } |
| |
| int SystemComm::sendToSystem(const void *data, size_t len) |
| { |
| if (NanoHub::messageTracingEnabled()) { |
| dumpBuffer("HAL -> SYS", getSystem()->mHostIfAppName, 0, data, len); |
| } |
| return NanoHub::sendToDevice(&getSystem()->mHostIfAppName, data, len); |
| } |
| |
| int SystemComm::AppInfoSession::setup(const hub_message_t *) |
| { |
| std::lock_guard<std::mutex> _l(mLock); |
| int suggestedSize = mAppInfo.size() ? mAppInfo.size() : 20; |
| |
| mAppInfo.clear(); |
| mAppInfo.reserve(suggestedSize); |
| setState(SESSION_USER); |
| |
| return requestNext(); |
| } |
| |
| inline hub_app_name_t deviceAppNameToHost(const hub_app_name_t src) |
| { |
| hub_app_name_t res = { .id = le64toh(src.id) }; |
| return res; |
| } |
| |
| inline hub_app_name_t hostAppNameToDevice(const hub_app_name_t src) |
| { |
| hub_app_name_t res = { .id = htole64(src.id) }; |
| return res; |
| } |
| |
| int SystemComm::AppInfoSession::handleRx(MessageBuf &buf) |
| { |
| std::lock_guard<std::mutex> _l(mLock); |
| |
| NanohubRsp rsp(buf, true); |
| if (rsp.cmd != NANOHUB_QUERY_APPS) { |
| return 1; |
| } |
| size_t len = buf.getRoom(); |
| if (len != sizeof(NanohubAppInfo) && len) { |
| ALOGE("%s: Invalid data size; have %zu, need %zu", __func__, |
| len, sizeof(NanohubAppInfo)); |
| return -EINVAL; |
| } |
| if (getState() != SESSION_USER) { |
| ALOGE("%s: Invalid state; have %d, need %d", __func__, getState(), SESSION_USER); |
| return -EINVAL; |
| } |
| if (len) { |
| NanohubAppInfo info; |
| readNanohubAppInfo(buf, info); |
| hub_app_info appInfo; |
| appInfo.num_mem_ranges = 0; |
| if (info.flashUse != NANOHUB_MEM_SZ_UNKNOWN) { |
| mem_range_t &range = appInfo.mem_usage[appInfo.num_mem_ranges++]; |
| range.type = HUB_MEM_TYPE_MAIN; |
| range.total_bytes = info.flashUse; |
| } |
| if (info.ramUse != NANOHUB_MEM_SZ_UNKNOWN) { |
| mem_range_t &range = appInfo.mem_usage[appInfo.num_mem_ranges++]; |
| range.type = HUB_MEM_TYPE_RAM; |
| range.total_bytes = info.ramUse; |
| } |
| |
| appInfo.app_name = info.name; |
| appInfo.version = info.version; |
| |
| mAppInfo.push_back(appInfo); |
| return requestNext(); |
| } else { |
| sendToApp(CONTEXT_HUB_QUERY_APPS, |
| static_cast<const void *>(mAppInfo.data()), |
| mAppInfo.size() * sizeof(mAppInfo[0])); |
| complete(); |
| } |
| |
| return 0; |
| } |
| |
| int SystemComm::AppInfoSession::requestNext() |
| { |
| char data[MAX_RX_PACKET]; |
| MessageBuf buf(data, sizeof(data)); |
| buf.writeU8(NANOHUB_QUERY_APPS); |
| buf.writeU32(mAppInfo.size()); |
| return sendToSystem(buf.getData(), buf.getPos()); |
| } |
| |
| int SystemComm::GlobalSession::setup(const hub_message_t *) |
| { |
| std::lock_guard<std::mutex> _l(mLock); |
| setState(SESSION_USER); |
| return 0; |
| } |
| |
| int SystemComm::GlobalSession::handleRx(MessageBuf &buf) |
| { |
| std::lock_guard<std::mutex> _l(mLock); |
| |
| NanohubRsp rsp(buf); |
| if (rsp.cmd != NANOHUB_REBOOT) { |
| return 1; |
| } |
| |
| ALOGW("Nanohub reboot status [UNSOLICITED]: %08" PRIX32, rsp.status); |
| sendToApp(CONTEXT_HUB_OS_REBOOT, &rsp.status, sizeof(rsp.status)); |
| |
| return 0; |
| } |
| |
| int SystemComm::MemInfoSession::setup(const hub_message_t *) |
| { |
| std::lock_guard<std::mutex> _l(mLock); |
| char data[MAX_RX_PACKET]; |
| MessageBuf buf(data, sizeof(data)); |
| buf.writeU8(NANOHUB_QUERY_MEMINFO); |
| |
| setState(SESSION_USER); |
| return sendToSystem(buf.getData(), buf.getPos()); |
| } |
| |
| int SystemComm::MemInfoSession::handleRx(MessageBuf &buf) |
| { |
| std::lock_guard<std::mutex> _l(mLock); |
| NanohubRsp rsp(buf, true); |
| |
| if (rsp.cmd != NANOHUB_QUERY_MEMINFO) |
| return 1; |
| |
| size_t len = buf.getRoom(); |
| |
| if (len != sizeof(NanohubMemInfo)) { |
| ALOGE("%s: Invalid data size: %zu", __func__, len); |
| return -EINVAL; |
| } |
| if (getState() != SESSION_USER) { |
| ALOGE("%s: Invalid state; have %d, need %d", __func__, getState(), SESSION_USER); |
| return -EINVAL; |
| } |
| |
| NanohubMemInfo mi; |
| readNanohubMemInfo(buf, mi); |
| std::vector<mem_range_t> ranges; |
| ranges.reserve(4); |
| |
| //if each is valid, copy to output area |
| if (mi.sharedSz != NANOHUB_MEM_SZ_UNKNOWN && |
| mi.sharedUse != NANOHUB_MEM_SZ_UNKNOWN) |
| ranges.push_back({ |
| .type = HUB_MEM_TYPE_MAIN, |
| .total_bytes = mi.sharedSz, |
| .free_bytes = mi.sharedSz - mi.sharedUse, |
| }); |
| |
| if (mi.osSz != NANOHUB_MEM_SZ_UNKNOWN && |
| mi.osUse != NANOHUB_MEM_SZ_UNKNOWN) |
| ranges.push_back({ |
| .type = HUB_MEM_TYPE_OS, |
| .total_bytes = mi.osSz, |
| .free_bytes = mi.osSz - mi.osUse, |
| }); |
| |
| if (mi.eeSz != NANOHUB_MEM_SZ_UNKNOWN && |
| mi.eeUse != NANOHUB_MEM_SZ_UNKNOWN) |
| ranges.push_back({ |
| .type = HUB_MEM_TYPE_EEDATA, |
| .total_bytes = mi.eeSz, |
| .free_bytes = mi.eeSz - mi.eeUse, |
| }); |
| |
| if (mi.ramSz != NANOHUB_MEM_SZ_UNKNOWN && |
| mi.ramUse != NANOHUB_MEM_SZ_UNKNOWN) |
| ranges.push_back({ |
| .type = HUB_MEM_TYPE_RAM, |
| .total_bytes = mi.ramSz, |
| .free_bytes = mi.ramSz - mi.ramUse, |
| }); |
| |
| //send it out |
| sendToApp(CONTEXT_HUB_QUERY_MEMORY, |
| static_cast<const void *>(ranges.data()), |
| ranges.size() * sizeof(ranges[0])); |
| |
| complete(); |
| |
| return 0; |
| } |
| |
| int SystemComm::AppMgmtSession::setup(const hub_message_t *appMsg) |
| { |
| std::lock_guard<std::mutex> _l(mLock); |
| |
| char data[MAX_RX_PACKET]; |
| MessageBuf buf(data, sizeof(data)); |
| const uint8_t *msgData = static_cast<const uint8_t*>(appMsg->message); |
| |
| mCmd = appMsg->message_type; |
| mLen = appMsg->message_len; |
| mPos = 0; |
| |
| switch (mCmd) { |
| case CONTEXT_HUB_APPS_ENABLE: |
| return setupMgmt(appMsg, NANOHUB_EXT_APPS_ON); |
| case CONTEXT_HUB_APPS_DISABLE: |
| return setupMgmt(appMsg, NANOHUB_EXT_APPS_OFF); |
| case CONTEXT_HUB_UNLOAD_APP: |
| return setupMgmt(appMsg, NANOHUB_EXT_APP_DELETE); |
| case CONTEXT_HUB_LOAD_OS: |
| case CONTEXT_HUB_LOAD_APP: |
| mData.clear(); |
| mData = std::vector<uint8_t>(msgData, msgData + mLen); |
| setState(TRANSFER); |
| |
| buf.writeU8(NANOHUB_START_UPLOAD); |
| buf.writeU8(mCmd == CONTEXT_HUB_LOAD_OS ? 1 : 0); |
| buf.writeU32(mLen); |
| return sendToSystem(buf.getData(), buf.getPos()); |
| |
| case CONTEXT_HUB_OS_REBOOT: |
| setState(REBOOT); |
| buf.writeU8(NANOHUB_REBOOT); |
| return sendToSystem(buf.getData(), buf.getPos()); |
| } |
| |
| return -EINVAL; |
| } |
| |
| int SystemComm::AppMgmtSession::setupMgmt(const hub_message_t *appMsg, uint32_t cmd) |
| { |
| const hub_app_name_t &appName = *static_cast<const hub_app_name_t*>(appMsg->message); |
| if (appMsg->message_len != sizeof(appName)) { |
| return -EINVAL; |
| } |
| |
| char data[MAX_RX_PACKET]; |
| MessageBuf buf(data, sizeof(data)); |
| buf.writeU8(cmd); |
| writeAppName(buf, appName); |
| setState(MGMT); |
| |
| return sendToSystem(buf.getData(), buf.getPos()); |
| } |
| |
| int SystemComm::AppMgmtSession::handleRx(MessageBuf &buf) |
| { |
| int ret = 0; |
| std::lock_guard<std::mutex> _l(mLock); |
| NanohubRsp rsp(buf); |
| |
| switch (getState()) { |
| case TRANSFER: |
| ret = handleTransfer(rsp); |
| break; |
| case FINISH: |
| ret = handleFinish(rsp); |
| break; |
| case RELOAD: |
| ret = handleReload(rsp); |
| break; |
| case REBOOT: |
| ret = handleReboot(rsp); |
| break; |
| case MGMT: |
| ret = handleMgmt(rsp); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| int SystemComm::AppMgmtSession::handleTransfer(NanohubRsp &rsp) |
| { |
| if (rsp.cmd != NANOHUB_CONT_UPLOAD && rsp.cmd != NANOHUB_START_UPLOAD) |
| return 1; |
| |
| char data[MAX_RX_PACKET]; |
| MessageBuf buf(data, sizeof(data)); |
| |
| static_assert(NANOHUB_UPLOAD_CHUNK_SZ_MAX <= (MAX_RX_PACKET-5), |
| "Invalid chunk size"); |
| |
| if (mPos < mLen) { |
| uint32_t chunkSize = mLen - mPos; |
| |
| if (chunkSize > NANOHUB_UPLOAD_CHUNK_SZ_MAX) { |
| chunkSize = NANOHUB_UPLOAD_CHUNK_SZ_MAX; |
| } |
| |
| buf.writeU8(NANOHUB_CONT_UPLOAD); |
| buf.writeU32(mPos); |
| buf.writeRaw(&mData[mPos], chunkSize); |
| mPos += chunkSize; |
| } else { |
| buf.writeU8(NANOHUB_FINISH_UPLOAD); |
| setState(FINISH); |
| } |
| |
| return sendToSystem(buf.getData(), buf.getPos()); |
| } |
| |
| int SystemComm::AppMgmtSession::handleFinish(NanohubRsp &rsp) |
| { |
| if (rsp.cmd != NANOHUB_FINISH_UPLOAD) |
| return 1; |
| |
| int ret = 0; |
| const bool success = rsp.status != 0; |
| mData.clear(); |
| |
| if (success) { |
| char data[MAX_RX_PACKET]; |
| MessageBuf buf(data, sizeof(data)); |
| // until app header is passed, we don't know who to start, so we reboot |
| buf.writeU8(NANOHUB_REBOOT); |
| setState(RELOAD); |
| ret = sendToSystem(buf.getData(), buf.getPos()); |
| } else { |
| int32_t result = NANOHUB_APP_NOT_LOADED; |
| |
| sendToApp(mCmd, &result, sizeof(result)); |
| complete(); |
| } |
| |
| return ret; |
| } |
| |
| /* reboot notification, when triggered as part of App reload sequence */ |
| int SystemComm::AppMgmtSession::handleReload(NanohubRsp &rsp) |
| { |
| int32_t result = NANOHUB_APP_LOADED; |
| |
| ALOGI("Nanohub reboot status [NEW APP START]: %08" PRIX32, rsp.status); |
| |
| sendToApp(mCmd, &result, sizeof(result)); |
| |
| // in addition to sending response to the CONTEXT_HUB_LOAD_APP command, |
| // we should send unsolicited reboot notification; |
| // I choose to do it here rather than delegate it to global session |
| // because I want the log to clearly differentiate between UNSOLICITED reboots |
| // (meaning FW faults) and REQUESTED reboots. |
| sendToApp(CONTEXT_HUB_OS_REBOOT, &rsp.status, sizeof(rsp.status)); |
| |
| complete(); |
| |
| return 0; |
| } |
| |
| /* reboot notification, when triggered by App request */ |
| int SystemComm::AppMgmtSession::handleReboot(NanohubRsp &rsp) |
| { |
| ALOGI("Nanohub reboot status [USER REQ]: %08" PRIX32, rsp.status); |
| |
| sendToApp(mCmd, &rsp.status, sizeof(rsp.status)); |
| complete(); |
| |
| return 0; |
| } |
| |
| int SystemComm::AppMgmtSession::handleMgmt(NanohubRsp &rsp) |
| { |
| bool valid = false; |
| |
| ALOGI("Nanohub MGMT response: CMD=%02X; STATUS=%08" PRIX32, rsp.cmd, rsp.status); |
| |
| switch (rsp.cmd) { |
| case NANOHUB_EXT_APPS_OFF: |
| valid = mCmd == CONTEXT_HUB_APPS_DISABLE; |
| break; |
| case NANOHUB_EXT_APPS_ON: |
| valid = mCmd == CONTEXT_HUB_APPS_ENABLE; |
| break; |
| case NANOHUB_EXT_APP_DELETE: |
| valid = mCmd == CONTEXT_HUB_UNLOAD_APP; |
| break; |
| default: |
| return 1; |
| } |
| |
| if (!valid) { |
| ALOGE("Invalid response for this state: APP CMD=%02X", mCmd); |
| return -EINVAL; |
| } |
| |
| sendToApp(mCmd, &rsp.status, sizeof(rsp.status)); |
| complete(); |
| |
| return 0; |
| } |
| |
| int SystemComm::KeyInfoSession::setup(const hub_message_t *) { |
| std::lock_guard<std::mutex> _l(mLock); |
| mRsaKeyData.clear(); |
| setState(SESSION_USER); |
| mStatus = -EBUSY; |
| return requestRsaKeys(); |
| } |
| |
| int SystemComm::KeyInfoSession::handleRx(MessageBuf &buf) |
| { |
| std::lock_guard<std::mutex> _l(mLock); |
| NanohubRsp rsp(buf, true); |
| |
| if (getState() != SESSION_USER) { |
| // invalid state |
| mStatus = -EFAULT; |
| return mStatus; |
| } |
| |
| if (buf.getRoom()) { |
| mRsaKeyData.insert(mRsaKeyData.end(), |
| buf.getData() + buf.getPos(), |
| buf.getData() + buf.getSize()); |
| return requestRsaKeys(); |
| } else { |
| mStatus = 0; |
| complete(); |
| return 0; |
| } |
| } |
| |
| int SystemComm::KeyInfoSession::requestRsaKeys(void) |
| { |
| char data[MAX_RX_PACKET]; |
| MessageBuf buf(data, sizeof(data)); |
| |
| buf.writeU8(NANOHUB_QUERY_APPS); |
| buf.writeU32(mRsaKeyData.size()); |
| |
| return sendToSystem(buf.getData(), buf.getPos()); |
| } |
| |
| int SystemComm::doHandleRx(const nano_message *msg) |
| { |
| //we only care for messages from HostIF |
| if (msg->hdr.appId != mHostIfAppName.id) |
| return 1; |
| |
| //they must all be at least 1 byte long |
| if (!msg->hdr.len) { |
| return -EINVAL; |
| } |
| MessageBuf buf(reinterpret_cast<const char*>(msg->data), msg->hdr.len); |
| if (NanoHub::messageTracingEnabled()) { |
| dumpBuffer("SYS -> HAL", mHostIfAppName, 0, buf.getData(), buf.getSize()); |
| } |
| int status = mSessions.handleRx(buf); |
| if (status) { |
| // provide default handler for any system message, that is not properly handled |
| dumpBuffer(status > 0 ? "HAL (not handled)" : "HAL (error)", |
| mHostIfAppName, 0, buf.getData(), buf.getSize(), status); |
| status = status > 0 ? 0 : status; |
| } |
| |
| return status; |
| } |
| |
| int SystemComm::SessionManager::handleRx(MessageBuf &buf) |
| { |
| int status = 1; |
| |
| // pass message to all active sessions, in arbitrary order |
| // 1st session that handles the message terminates the loop |
| for (auto pos = sessions_.begin(); |
| pos != sessions_.end() && status > 0; next(pos)) { |
| Session *session = pos->second; |
| status = session->handleRx(buf); |
| if (status < 0) { |
| session->complete(); |
| } |
| } |
| if (status > 0) { |
| status = mGlobal.handleRx(buf); |
| } |
| |
| return status; |
| } |
| |
| int SystemComm::doHandleTx(const hub_message_t *appMsg) |
| { |
| int status = 0; |
| |
| switch (appMsg->message_type) { |
| case CONTEXT_HUB_LOAD_APP: |
| if (!mKeySession.haveKeys()) { |
| status = mSessions.setup_and_add(CONTEXT_HUB_LOAD_APP, &mKeySession, appMsg); |
| if (status < 0) { |
| break; |
| } |
| mKeySession.wait(); |
| status = mKeySession.getStatus(); |
| if (status < 0) { |
| break; |
| } |
| } |
| status = mSessions.setup_and_add(CONTEXT_HUB_LOAD_APP, &mAppMgmtSession, appMsg); |
| break; |
| case CONTEXT_HUB_APPS_ENABLE: |
| case CONTEXT_HUB_APPS_DISABLE: |
| case CONTEXT_HUB_UNLOAD_APP: |
| // all APP-modifying commands share session key, to ensure they can't happen at the same time |
| status = mSessions.setup_and_add(CONTEXT_HUB_LOAD_APP, &mAppMgmtSession, appMsg); |
| break; |
| |
| case CONTEXT_HUB_QUERY_APPS: |
| status = mSessions.setup_and_add(CONTEXT_HUB_QUERY_APPS, &mAppInfoSession, appMsg); |
| break; |
| |
| case CONTEXT_HUB_QUERY_MEMORY: |
| status = mSessions.setup_and_add(CONTEXT_HUB_QUERY_MEMORY, &mMemInfoSession, appMsg); |
| break; |
| |
| default: |
| ALOGW("Unknown os message type %u\n", appMsg->message_type); |
| return -EINVAL; |
| } |
| |
| return status; |
| } |
| |
| }; // namespace nanohub |
| |
| }; // namespace android |