/* * 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. */ #include <inttypes.h> #include <string.h> #include <stdint.h> #include <sys/endian.h> #include <variant/inc/variant.h> #include <eventnums.h> #include <plat/inc/taggedPtr.h> #include <plat/inc/bl.h> #include <plat/inc/plat.h> #include <nanohub/crc.h> #include <nanohub/rsa.h> #include <atomicBitset.h> #include <atomic.h> #include <hostIntf.h> #include <hostIntf_priv.h> #include <nanohubCommand.h> #include <nanohubPacket.h> #include <eeData.h> #include <seos.h> #include <util.h> #include <mpu.h> #include <heap.h> #include <slab.h> #include <sensType.h> #include <timer.h> #include <appSec.h> #include <cpu.h> #include <cpu/inc/cpuMath.h> #include <algos/ap_hub_sync.h> #define NANOHUB_COMMAND(_reason, _fastHandler, _handler, _minReqType, _maxReqType) \ { .reason = _reason, .fastHandler = _fastHandler, .handler = _handler, \ .minDataLen = sizeof(_minReqType), .maxDataLen = sizeof(_maxReqType) } #define NANOHUB_HAL_COMMAND(_msg, _handler) \ { .msg = _msg, .handler = _handler } // maximum number of bytes to feed into appSecRxData at once // The bigger the number, the more time we block other event processing // appSecRxData only feeds 16 bytes at a time into writeCbk, so large // numbers don't buy us that much #define MAX_APP_SEC_RX_DATA_LEN 64 #define REQUIRE_SIGNED_IMAGE true #define DEBUG_APHUB_TIME_SYNC false #if DEBUG_APHUB_TIME_SYNC static void syncDebugAdd(uint64_t, uint64_t); #endif struct DownloadState { struct AppSecState *appSecState; uint32_t size; // document size, as reported by client uint32_t srcOffset; // bytes received from client uint32_t dstOffset; // bytes sent to flash struct AppHdr *start; // start of flash segment, where to write uint32_t crc; // document CRC-32, as reported by client uint32_t srcCrc; // current state of CRC-32 we generate from input uint8_t data[NANOHUB_PACKET_PAYLOAD_MAX]; uint8_t len; uint8_t lenLeft; uint8_t chunkReply; bool erase; bool eraseScheduled; }; static struct DownloadState *mDownloadState; static AppSecErr mAppSecStatus; static struct SlabAllocator *mEventSlab; static struct HostIntfDataBuffer mTxCurr, mTxNext; static uint8_t mTxCurrLength, mTxNextLength; static uint8_t mPrefetchActive, mPrefetchTx; static uint32_t mTxWakeCnt[2]; static struct ApHubSync mTimeSync; static inline bool isSensorEvent(uint32_t evtType) { return evtType > EVT_NO_FIRST_SENSOR_EVENT && evtType <= EVT_NO_FIRST_SENSOR_EVENT + SENS_TYPE_LAST_USER; } static void slabFree(void *ptr) { slabAllocatorFree(mEventSlab, ptr); } void nanohubInitCommand(void) { mEventSlab = slabAllocatorNew(NANOHUB_PACKET_PAYLOAD_MAX-sizeof(__le32), 4, 2); } static uint32_t getOsHwVersion(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubOsHwVersionsResponse *resp = tx; resp->hwType = htole16(platHwType()); resp->hwVer = htole16(platHwVer()); resp->blVer = htole16(platBlVer()); resp->osVer = htole16(OS_VER); resp->variantVer = htole32(VARIANT_VER); return sizeof(*resp); } static uint32_t getAppVersion(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubAppVersionsRequest *req = rx; struct NanohubAppVersionsResponse *resp = tx; uint32_t appIdx, appVer, appSize; if (osAppInfoById(le64toh(req->appId), &appIdx, &appVer, &appSize)) { resp->appVer = htole32(appVer); return sizeof(*resp); } return 0; } static uint32_t queryAppInfo(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubAppInfoRequest *req = rx; struct NanohubAppInfoResponse *resp = tx; uint64_t appId; uint32_t appVer, appSize; if (osAppInfoByIndex(le32toh(req->appIdx), &appId, &appVer, &appSize)) { resp->appId = htole64(appId); resp->appVer = htole32(appVer); resp->appSize = htole32(appSize); return sizeof(*resp); } return 0; } static AppSecErr writeCbk(const void *data, uint32_t len) { AppSecErr ret = APP_SEC_BAD; if (osWriteShared((uint8_t*)(mDownloadState->start) + mDownloadState->dstOffset, data, len)) { ret = APP_SEC_NO_ERROR; mDownloadState->dstOffset += len; } return ret; } static AppSecErr pubKeyFindCbk(const uint32_t *gotKey, bool *foundP) { const uint32_t *ptr; uint32_t numKeys, i; *foundP = false; ptr = BL.blGetPubKeysInfo(&numKeys); for (i = 0; ptr && i < numKeys; i++, ptr += RSA_LIMBS) { if (!memcmp(gotKey, ptr, RSA_BYTES)) { *foundP = true; break; } } return APP_SEC_NO_ERROR; } static AppSecErr osSecretKeyLookup(uint64_t keyId, void *keyBuf) { struct SeosEedataEncrKeyData kd; void *state = NULL; while(1) { uint32_t sz = sizeof(struct SeosEedataEncrKeyData); if (!eeDataGetAllVersions(EE_DATA_NAME_ENCR_KEY, &kd, &sz, &state)) break; if (sz == sizeof(struct SeosEedataEncrKeyData) && kd.keyID == keyId) { if (keyBuf) memcpy(keyBuf, kd.key, sizeof(kd.key)); return APP_SEC_NO_ERROR; } } return APP_SEC_KEY_NOT_FOUND; } static AppSecErr osSecretKeyDelete(uint64_t keyId) { struct SeosEedataEncrKeyData kd; void *state = NULL; bool good = true; int count = 0; while(1) { uint32_t sz = sizeof(struct SeosEedataEncrKeyData); void *addr = eeDataGetAllVersions(EE_DATA_NAME_ENCR_KEY, &kd, &sz, &state); if (!addr) break; if (sz == sizeof(kd) && kd.keyID == keyId) { good = eeDataEraseOldVersion(EE_DATA_NAME_ENCR_KEY, addr) && good; count++; } } return count == 0 ? APP_SEC_KEY_NOT_FOUND : good ? APP_SEC_NO_ERROR : APP_SEC_BAD; } static AppSecErr osSecretKeyAdd(uint64_t keyId, void *keyBuf) { struct SeosEedataEncrKeyData kd; // do not add key if it already exists if (osSecretKeyLookup(keyId, NULL) != APP_SEC_KEY_NOT_FOUND) return APP_SEC_BAD; memcpy(&kd.key, keyBuf, 32); kd.keyID = keyId; return eeDataSet(EE_DATA_NAME_ENCR_KEY, &kd, sizeof(kd)) ? APP_SEC_NO_ERROR : APP_SEC_BAD; } static void freeDownloadState() { if (mDownloadState->appSecState) appSecDeinit(mDownloadState->appSecState); heapFree(mDownloadState); mDownloadState = NULL; } static void resetDownloadState(bool initial) { bool doCreate = true; mAppSecStatus = APP_SEC_NO_ERROR; if (mDownloadState->appSecState) appSecDeinit(mDownloadState->appSecState); mDownloadState->appSecState = appSecInit(writeCbk, pubKeyFindCbk, osSecretKeyLookup, REQUIRE_SIGNED_IMAGE); mDownloadState->srcOffset = 0; mDownloadState->srcCrc = ~0; if (!initial) { // if no data was written, we can reuse the same segment if (mDownloadState->dstOffset) osAppSegmentClose(mDownloadState->start, mDownloadState->dstOffset, SEG_ST_ERASED); else doCreate = false; } if (doCreate) mDownloadState->start = osAppSegmentCreate(mDownloadState->size); if (!mDownloadState->start) mDownloadState->erase = true; mDownloadState->dstOffset = 0; } static bool doStartFirmwareUpload(struct NanohubStartFirmwareUploadRequest *req) { if (!mDownloadState) { mDownloadState = heapAlloc(sizeof(struct DownloadState)); if (!mDownloadState) return false; else memset(mDownloadState, 0x00, sizeof(struct DownloadState)); } mDownloadState->size = le32toh(req->size); mDownloadState->crc = le32toh(req->crc); mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED; resetDownloadState(true); return true; } static uint32_t startFirmwareUpload(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubStartFirmwareUploadRequest *req = rx; struct NanohubStartFirmwareUploadResponse *resp = tx; resp->accepted = doStartFirmwareUpload(req); return sizeof(*resp); } static void deferredUpdateOs(void *cookie) { const struct AppHdr *app = cookie; struct OsUpdateHdr *os = (struct OsUpdateHdr *)(&(app->hdr) + 1); uint32_t uploadStatus = OS_UPDT_HDR_CHECK_FAILED; uint8_t marker = OS_UPDT_MARKER_DOWNLOADED; struct Segment *seg = osGetSegment(app); uint32_t segSize = osSegmentGetSize(seg); osLog(LOG_INFO, "%s: checking OS image @ %p\n", __func__, os); // some sanity checks before asking BL to do image lookup hostIntfSetBusy(true); if (segSize >= (sizeof(*app) + sizeof(*os)) && segSize > os->size) { if (osWriteShared(&os->marker, &marker, sizeof(os->marker))) uploadStatus = BL.blVerifyOsUpdate(); else osLog(LOG_ERROR, "%s: could not set marker on OS image\n", __func__); } hostIntfSetBusy(false); osLog(LOG_INFO, "%s: status=%" PRIu32 "\n", __func__, uploadStatus); } static AppSecErr updateKey(const struct AppHdr *app) { AppSecErr ret; struct KeyInfo *ki = (struct KeyInfo *)(&(app->hdr) + 1); uint8_t *data = (uint8_t *)(ki + 1); uint64_t keyId = KEY_ID_MAKE(APP_ID_GET_VENDOR(app->hdr.appId), ki->id); const char *op; if ((app->hdr.fwFlags & FL_KEY_HDR_DELETE) != 0) { // removing existing key ret = osSecretKeyDelete(keyId); op = "Removing"; } else { // adding new key ret = osSecretKeyAdd(keyId, data); op = "Adding"; } osLog(LOG_INFO, "%s: %s key: id=%016" PRIX64 "; ret=%" PRIu32 "\n", __func__, op, keyId, ret); return ret; } static uint32_t appSecErrToNanohubReply(AppSecErr status) { uint32_t reply; switch (status) { case APP_SEC_NO_ERROR: reply = NANOHUB_FIRMWARE_UPLOAD_SUCCESS; break; case APP_SEC_KEY_NOT_FOUND: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_KEY_NOT_FOUND; break; case APP_SEC_HEADER_ERROR: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_HEADER_ERROR; break; case APP_SEC_TOO_MUCH_DATA: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_MUCH_DATA; break; case APP_SEC_TOO_LITTLE_DATA: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_LITTLE_DATA; break; case APP_SEC_SIG_VERIFY_FAIL: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_VERIFY_FAIL; break; case APP_SEC_SIG_DECODE_FAIL: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_DECODE_FAIL; break; case APP_SEC_SIG_ROOT_UNKNOWN: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_ROOT_UNKNOWN; break; case APP_SEC_MEMORY_ERROR: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_MEMORY_ERROR; break; case APP_SEC_INVALID_DATA: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_INVALID_DATA; break; case APP_SEC_VERIFY_FAILED: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_VERIFY_FAILED; break; default: reply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_BAD; break; } return reply; } static uint32_t firmwareFinish(bool valid) { struct AppHdr *app; struct Segment *storageSeg; uint32_t segState; uint32_t ret = NANOHUB_FIRMWARE_UPLOAD_SUCCESS; if (!mDownloadState) { ret = appSecErrToNanohubReply(mAppSecStatus); osLog(LOG_INFO, "%s: no DL status; decoding secure status: %" PRIu32 "\n", __func__, ret); return ret; } app = mDownloadState->start; storageSeg = osGetSegment(app); if (mAppSecStatus == APP_SEC_NO_ERROR && valid) { osLog(LOG_INFO, "%s: Secure verification passed\n", __func__); if (storageSeg->state != SEG_ST_RESERVED || mDownloadState->size < sizeof(struct FwCommonHdr) || app->hdr.magic != APP_HDR_MAGIC || app->hdr.fwVer != APP_HDR_VER_CUR) { segState = SEG_ST_ERASED; osLog(LOG_INFO, "%s: Header verification failed\n", __func__); } else { segState = SEG_ST_VALID; } } else { segState = SEG_ST_ERASED; osLog(LOG_INFO, "%s: Secure verification failed: valid=%d; status=%" PRIu32 "\n", __func__, valid, mAppSecStatus); } if (!osAppSegmentClose(app, mDownloadState->dstOffset, segState)) { osLog(LOG_INFO, "%s: Failed to close segment\n", __func__); valid = false; } else { segState = osAppSegmentGetState(app); valid = (segState == SEG_ST_VALID); } osLog(LOG_INFO, "Loaded %s image type %" PRIu8 ": %" PRIu32 " bytes @ %p; state=%02" PRIX32 "\n", valid ? "valid" : "invalid", app->hdr.payInfoType, mDownloadState->size, mDownloadState->start, segState); freeDownloadState(); // no more access to mDownloadState if (!valid) ret = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_BAD; // take extra care about some special payload types if (ret == NANOHUB_FIRMWARE_UPLOAD_SUCCESS) { switch(app->hdr.payInfoType) { case LAYOUT_OS: osLog(LOG_INFO, "Performing OS update\n"); // we want to give this message a chance to reach host before we start erasing stuff osDefer(deferredUpdateOs, (void*)app, false); break; case LAYOUT_KEY: ret = appSecErrToNanohubReply(updateKey(app)); break; } } if (ret != NANOHUB_FIRMWARE_UPLOAD_SUCCESS || (app->hdr.fwFlags & FL_APP_HDR_VOLATILE)) { if ((app->hdr.fwFlags & FL_APP_HDR_SECURE)) osAppWipeData((struct AppHdr*)app); osAppSegmentSetState(app, SEG_ST_ERASED); } // if any error happened after we downloaded and verified image, we say it is unknown fault // we don't have download status, so e have to save returned value in secure status field, because // host may request the same status multiple times if (ret != NANOHUB_FIRMWARE_UPLOAD_SUCCESS) mAppSecStatus = APP_SEC_BAD; return ret; } static void firmwareErase(void *cookie) { if (mDownloadState->erase == true) { osLog(LOG_INFO, "%s: erasing shared area\n", __func__); osEraseShared(); mDownloadState->start = osAppSegmentCreate(mDownloadState->size); if (!mDownloadState->start) firmwareFinish(false); mDownloadState->erase = false; hostIntfSetInterrupt(NANOHUB_INT_CMD_WAIT); } mDownloadState->eraseScheduled = false; } static void firmwareWrite(void *cookie) { bool valid; bool finished = false; struct NanohubHalContUploadTx *resp = cookie; // only check crc when cookie is NULL (write came from kernel, not HAL) bool checkCrc = !cookie; if (mAppSecStatus == APP_SEC_NEED_MORE_TIME) { mAppSecStatus = appSecDoSomeProcessing(mDownloadState->appSecState); } else if (mDownloadState->lenLeft) { const uint8_t *data = mDownloadState->data + mDownloadState->len - mDownloadState->lenLeft; uint32_t len = mDownloadState->lenLeft, lenLeft, lenRem = 0; if (len > MAX_APP_SEC_RX_DATA_LEN) { lenRem = len - MAX_APP_SEC_RX_DATA_LEN; len = MAX_APP_SEC_RX_DATA_LEN; } mAppSecStatus = appSecRxData(mDownloadState->appSecState, data, len, &lenLeft); mDownloadState->lenLeft = lenLeft + lenRem; } valid = (mAppSecStatus == APP_SEC_NO_ERROR); if (mAppSecStatus == APP_SEC_NEED_MORE_TIME || mDownloadState->lenLeft) { osDefer(firmwareWrite, cookie, false); return; } else if (valid) { if (mDownloadState->srcOffset == mDownloadState->size) { finished = true; valid = !checkCrc || mDownloadState->crc == ~mDownloadState->srcCrc; } else if (mDownloadState->srcOffset > mDownloadState->size) { valid = false; } } if (!valid) finished = true; if (finished) { if (firmwareFinish(valid) != NANOHUB_FIRMWARE_UPLOAD_SUCCESS) valid = false; } if (resp) { resp->success = valid; osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree); } } static uint32_t doFirmwareChunk(uint8_t *data, uint32_t offset, uint32_t len, void *cookie) { uint32_t reply; if (!mDownloadState) { reply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY; } else if (mAppSecStatus == APP_SEC_NEED_MORE_TIME || mDownloadState->lenLeft) { reply = NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND; } else if (mDownloadState->chunkReply != NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED) { reply = mDownloadState->chunkReply; firmwareFinish(false); } else { if (mDownloadState->erase == true) { reply = NANOHUB_FIRMWARE_CHUNK_REPLY_WAIT; if (!mDownloadState->eraseScheduled) mDownloadState->eraseScheduled = osDefer(firmwareErase, NULL, false); } else if (!mDownloadState->start) { // this means we can't allocate enough space even after we did erase reply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY; firmwareFinish(false); } else if (offset != mDownloadState->srcOffset) { reply = NANOHUB_FIRMWARE_CHUNK_REPLY_RESTART; resetDownloadState(false); } else { if (!cookie) mDownloadState->srcCrc = crc32(data, len, mDownloadState->srcCrc); mDownloadState->srcOffset += len; memcpy(mDownloadState->data, data, len); mDownloadState->lenLeft = mDownloadState->len = len; reply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED; osDefer(firmwareWrite, cookie, false); } } return reply; } static uint32_t firmwareChunk(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubFirmwareChunkRequest *req = rx; struct NanohubFirmwareChunkResponse *resp = tx; uint32_t offset = le32toh(req->offset); uint8_t len = rx_len - sizeof(req->offset); resp->chunkReply = doFirmwareChunk(req->data, offset, len, NULL); return sizeof(*resp); } static uint32_t doFinishFirmwareUpload() { uint32_t reply; if (!mDownloadState) { reply = appSecErrToNanohubReply(mAppSecStatus); } else if (mDownloadState->srcOffset == mDownloadState->size) { reply = NANOHUB_FIRMWARE_UPLOAD_PROCESSING; } else { reply = firmwareFinish(false); } return reply; } static uint32_t finishFirmwareUpload(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubFinishFirmwareUploadResponse *resp = tx; resp->uploadReply = doFinishFirmwareUpload(); if (resp->uploadReply != NANOHUB_FIRMWARE_UPLOAD_PROCESSING) osLog(LOG_INFO, "%s: reply=%" PRIu8 "\n", __func__, resp->uploadReply); return sizeof(*resp); } static uint32_t getInterrupt(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubGetInterruptRequest *req = rx; struct NanohubGetInterruptResponse *resp = tx; int i; if (rx_len == sizeof(struct NanohubGetInterruptRequest)) { for (i = 0; i < HOSTINTF_MAX_INTERRUPTS; i++) { if (req->clear[i/32] & (1UL << (i & 31))) hostIntfClearInterrupt(i); } } hostIntfCopyInterrupts(resp->interrupts, HOSTINTF_MAX_INTERRUPTS); return sizeof(*resp); } static uint32_t maskInterrupt(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubMaskInterruptRequest *req = rx; struct NanohubMaskInterruptResponse *resp = tx; hostIntfSetInterruptMask(req->interrupt); resp->accepted = true; return sizeof(*resp); } static uint32_t unmaskInterrupt(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubUnmaskInterruptRequest *req = rx; struct NanohubUnmaskInterruptResponse *resp = tx; hostIntfClearInterruptMask(req->interrupt); resp->accepted = true; return sizeof(*resp); } static void addDelta(struct ApHubSync *sync, uint64_t apTime, uint64_t hubTime) { #if DEBUG_APHUB_TIME_SYNC syncDebugAdd(apTime, hubTime); #endif apHubSyncAddDelta(sync, apTime, hubTime); } static int64_t getAvgDelta(struct ApHubSync *sync) { return apHubSyncGetDelta(sync, sensorGetTime()); } static int fillBuffer(void *tx, uint32_t totLength, uint32_t *wakeup, uint32_t *nonwakeup) { struct HostIntfDataBuffer *packet = &mTxNext; struct HostIntfDataBuffer *firstPacket = tx; uint8_t *buf = tx; uint32_t length; uint32_t prevWakeup, prevNonWakeup; prevWakeup = *wakeup; prevNonWakeup = *nonwakeup; while (hostIntfPacketDequeue(&mTxNext, wakeup, nonwakeup)) { length = packet->length + sizeof(packet->evtType); if (packet->sensType == SENS_TYPE_INVALID) { switch (packet->dataType) { case HOSTINTF_DATA_TYPE_APP_TO_HOST: packet->evtType = htole32(EVT_APP_TO_HOST); break; case HOSTINTF_DATA_TYPE_RESET_REASON: packet->evtType = htole32(EVT_RESET_REASON); break; #ifdef DEBUG_LOG_EVT case HOSTINTF_DATA_TYPE_LOG: packet->evtType = htole32(HOST_EVT_DEBUG_LOG); break; #endif default: packet->evtType = htole32(0x00000000); break; } } else { packet->evtType = htole32(EVT_NO_FIRST_SENSOR_EVENT + packet->sensType); if (packet->referenceTime) packet->referenceTime += getAvgDelta(&mTimeSync); if (*wakeup > 0) packet->firstSample.interrupt = NANOHUB_INT_WAKEUP; } if ((!totLength || (isSensorEvent(firstPacket->evtType) && isSensorEvent(packet->evtType))) && totLength + length <= sizeof(struct HostIntfDataBuffer)) { memcpy(buf + totLength, &mTxNext, length); totLength += length; if (isSensorEvent(packet->evtType) && packet->firstSample.interrupt == NANOHUB_INT_WAKEUP) firstPacket->firstSample.interrupt = NANOHUB_INT_WAKEUP; } else { mTxNextLength = length; *wakeup = prevWakeup; *nonwakeup = prevNonWakeup; break; } prevWakeup = *wakeup; prevNonWakeup = *nonwakeup; } return totLength; } static void updateInterrupts(void) { uint32_t wakeup = atomicRead32bits(&mTxWakeCnt[0]); uint32_t nonwakeup = atomicRead32bits(&mTxWakeCnt[1]); bool wakeupStatus = hostIntfGetInterrupt(NANOHUB_INT_WAKEUP); bool nonwakeupStatus = hostIntfGetInterrupt(NANOHUB_INT_NONWAKEUP); if (!wakeup && wakeupStatus) hostIntfClearInterrupt(NANOHUB_INT_WAKEUP); else if (wakeup && !wakeupStatus) hostIntfSetInterrupt(NANOHUB_INT_WAKEUP); if (!nonwakeup && nonwakeupStatus) hostIntfClearInterrupt(NANOHUB_INT_NONWAKEUP); else if (nonwakeup && !nonwakeupStatus) hostIntfSetInterrupt(NANOHUB_INT_NONWAKEUP); } void nanohubPrefetchTx(uint32_t interrupt, uint32_t wakeup, uint32_t nonwakeup) { uint64_t state; if (wakeup < atomicRead32bits(&mTxWakeCnt[0])) wakeup = atomicRead32bits(&mTxWakeCnt[0]); if (nonwakeup < atomicRead32bits(&mTxWakeCnt[1])) nonwakeup = atomicRead32bits(&mTxWakeCnt[1]); if (interrupt == HOSTINTF_MAX_INTERRUPTS && !hostIntfGetInterrupt(NANOHUB_INT_WAKEUP) && !hostIntfGetInterrupt(NANOHUB_INT_NONWAKEUP)) return; atomicWriteByte(&mPrefetchActive, 1); if (interrupt < HOSTINTF_MAX_INTERRUPTS) hostIntfSetInterrupt(interrupt); do { if (atomicReadByte(&mTxCurrLength) == 0 && mTxNextLength > 0) { memcpy(&mTxCurr, &mTxNext, mTxNextLength); atomicWriteByte(&mTxCurrLength, mTxNextLength); mTxNextLength = 0; } if (mTxNextLength == 0) { atomicWriteByte(&mTxCurrLength, fillBuffer(&mTxCurr, atomicReadByte(&mTxCurrLength), &wakeup, &nonwakeup)); atomicWrite32bits(&mTxWakeCnt[0], wakeup); atomicWrite32bits(&mTxWakeCnt[1], nonwakeup); } atomicWriteByte(&mPrefetchActive, 0); if (atomicReadByte(&mPrefetchTx)) { state = cpuIntsOff(); // interrupt occured during this call // take care of it hostIntfTxAck(&mTxCurr, atomicReadByte(&mTxCurrLength)); atomicWriteByte(&mPrefetchTx, 0); atomicWriteByte(&mTxCurrLength, 0); cpuIntsRestore(state); updateInterrupts(); } else { break; } } while (mTxNextLength > 0); } static void nanohubPrefetchTxDefer(void *cookie) { nanohubPrefetchTx(HOSTINTF_MAX_INTERRUPTS, 0, 0); } static uint32_t readEventFast(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubReadEventRequest *req = rx; uint8_t ret = 0; if (atomicReadByte(&mPrefetchActive)) { atomicWriteByte(&mPrefetchTx, 1); return NANOHUB_FAST_DONT_ACK; } else { if ((ret = atomicReadByte(&mTxCurrLength))) { addDelta(&mTimeSync, req->apBootTime, timestamp); memcpy(tx, &mTxCurr, ret); atomicWriteByte(&mTxCurrLength, 0); updateInterrupts(); osDefer(nanohubPrefetchTxDefer, NULL, true); } else { return NANOHUB_FAST_UNHANDLED_ACK; } } return ret; } static uint32_t readEvent(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubReadEventRequest *req = rx; uint8_t *buf = tx; uint32_t length, wakeup, nonwakeup; uint32_t totLength = 0; addDelta(&mTimeSync, req->apBootTime, timestamp); if ((totLength = atomicReadByte(&mTxCurrLength))) { memcpy(tx, &mTxCurr, totLength); atomicWriteByte(&mTxCurrLength, 0); updateInterrupts(); return totLength; } wakeup = atomicRead32bits(&mTxWakeCnt[0]); nonwakeup = atomicRead32bits(&mTxWakeCnt[1]); if (mTxNextLength > 0) { length = mTxNextLength; memcpy(buf, &mTxNext, length); totLength = length; mTxNextLength = 0; } totLength = fillBuffer(buf, totLength, &wakeup, &nonwakeup); atomicWrite32bits(&mTxWakeCnt[0], wakeup); atomicWrite32bits(&mTxWakeCnt[1], nonwakeup); if (totLength) { updateInterrupts(); } else { hostIntfClearInterrupt(NANOHUB_INT_WAKEUP); hostIntfClearInterrupt(NANOHUB_INT_NONWAKEUP); } return totLength; } static uint32_t writeEvent(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp) { struct NanohubWriteEventRequest *req = rx; struct NanohubWriteEventResponse *resp = tx; uint8_t *packet; struct HostHubRawPacket *rawPacket; uint32_t tid; EventFreeF free = slabFree; if (le32toh(req->evtType) == EVT_APP_FROM_HOST) { rawPacket = (struct HostHubRawPacket *)req->evtData; if (rx_len >= sizeof(req->evtType) + sizeof(struct HostHubRawPacket) && rx_len == sizeof(req->evtType) + sizeof(struct HostHubRawPacket) + rawPacket->dataLen && osTidById(rawPacket->appId, &tid)) { packet = slabAllocatorAlloc(mEventSlab); if (!packet) { packet = heapAlloc(rawPacket->dataLen + 1); free = heapFree; } if (!packet) { resp->accepted = false; } else { packet[0] = rawPacket->dataLen; memcpy(packet + 1, rawPacket + 1, rawPacket->dataLen); resp->accepted = osEnqueuePrivateEvt(EVT_APP_FROM_HOST, packet, free, tid); if (!resp->accepted) free(packet); } } else { resp->accepted = false; } } else { packet = slabAllocatorAlloc(mEventSlab); if (!packet) { packet = heapAlloc(rx_len - sizeof(req->evtType)); free = heapFree; } if (!packet) { resp->accepted = false; } else { memcpy(packet, req->evtData, rx_len - sizeof(req->evtType)); resp->accepted = osEnqueueEvtOrFree(le32toh(req->evtType), packet, free); } } return sizeof(*resp); } const static struct NanohubCommand mBuiltinCommands[] = { NANOHUB_COMMAND(NANOHUB_REASON_GET_OS_HW_VERSIONS, getOsHwVersion, getOsHwVersion, struct NanohubOsHwVersionsRequest, struct NanohubOsHwVersionsRequest), NANOHUB_COMMAND(NANOHUB_REASON_GET_APP_VERSIONS, NULL, getAppVersion, struct NanohubAppVersionsRequest, struct NanohubAppVersionsRequest), NANOHUB_COMMAND(NANOHUB_REASON_QUERY_APP_INFO, NULL, queryAppInfo, struct NanohubAppInfoRequest, struct NanohubAppInfoRequest), NANOHUB_COMMAND(NANOHUB_REASON_START_FIRMWARE_UPLOAD, NULL, startFirmwareUpload, struct NanohubStartFirmwareUploadRequest, struct NanohubStartFirmwareUploadRequest), NANOHUB_COMMAND(NANOHUB_REASON_FIRMWARE_CHUNK, NULL, firmwareChunk, __le32, struct NanohubFirmwareChunkRequest), NANOHUB_COMMAND(NANOHUB_REASON_FINISH_FIRMWARE_UPLOAD, NULL, finishFirmwareUpload, struct NanohubFinishFirmwareUploadRequest, struct NanohubFinishFirmwareUploadRequest), NANOHUB_COMMAND(NANOHUB_REASON_GET_INTERRUPT, getInterrupt, getInterrupt, 0, struct NanohubGetInterruptRequest), NANOHUB_COMMAND(NANOHUB_REASON_MASK_INTERRUPT, maskInterrupt, maskInterrupt, struct NanohubMaskInterruptRequest, struct NanohubMaskInterruptRequest), NANOHUB_COMMAND(NANOHUB_REASON_UNMASK_INTERRUPT, unmaskInterrupt, unmaskInterrupt, struct NanohubUnmaskInterruptRequest, struct NanohubUnmaskInterruptRequest), NANOHUB_COMMAND(NANOHUB_REASON_READ_EVENT, readEventFast, readEvent, struct NanohubReadEventRequest, struct NanohubReadEventRequest), NANOHUB_COMMAND(NANOHUB_REASON_WRITE_EVENT, writeEvent, writeEvent, __le32, struct NanohubWriteEventRequest), }; const struct NanohubCommand *nanohubFindCommand(uint32_t packetReason) { uint32_t i; for (i = 0; i < ARRAY_SIZE(mBuiltinCommands); i++) { const struct NanohubCommand *cmd = &mBuiltinCommands[i]; if (cmd->reason == packetReason) return cmd; } return NULL; } static void halSendMgmtResponse(uint32_t cmd, uint32_t status) { struct NanohubHalMgmtTx *resp; resp = heapAlloc(sizeof(*resp)); if (resp) { resp->hdr = (struct NanohubHalHdr) { .appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 0), .len = sizeof(*resp) - sizeof(resp->hdr) + sizeof(resp->hdr.msg), .msg = cmd, }; resp->status = htole32(status); osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree); } } static void halExtAppsOn(void *rx, uint8_t rx_len) { struct NanohubHalMgmtRx *req = rx; halSendMgmtResponse(NANOHUB_HAL_EXT_APPS_ON, osExtAppStartApps(le64toh(req->appId))); } static void halExtAppsOff(void *rx, uint8_t rx_len) { struct NanohubHalMgmtRx *req = rx; halSendMgmtResponse(NANOHUB_HAL_EXT_APPS_OFF, osExtAppStopApps(le64toh(req->appId))); } static void halExtAppDelete(void *rx, uint8_t rx_len) { struct NanohubHalMgmtRx *req = rx; halSendMgmtResponse(NANOHUB_HAL_EXT_APP_DELETE, osExtAppEraseApps(le64toh(req->appId))); } static void halQueryMemInfo(void *rx, uint8_t rx_len) { } static void halQueryApps(void *rx, uint8_t rx_len) { struct NanohubHalQueryAppsRx *req = rx; struct NanohubHalQueryAppsTx *resp; struct NanohubHalHdr *hdr; uint64_t appId; uint32_t appVer, appSize; if (osAppInfoByIndex(le32toh(req->idx), &appId, &appVer, &appSize)) { resp = heapAlloc(sizeof(*resp)); resp->hdr.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 0); resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1; resp->hdr.msg = NANOHUB_HAL_QUERY_APPS; resp->appId = appId; resp->version = appVer; resp->flashUse = appSize; resp->ramUse = 0; osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree); } else { hdr = heapAlloc(sizeof(*hdr)); hdr->appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 0); hdr->len = 1; hdr->msg = NANOHUB_HAL_QUERY_APPS; osEnqueueEvtOrFree(EVT_APP_TO_HOST, hdr, heapFree); } } static void halQueryRsaKeys(void *rx, uint8_t rx_len) { struct NanohubHalQueryRsaKeysRx *req = rx; struct NanohubHalQueryRsaKeysTx *resp; int len = 0; const uint32_t *ptr; uint32_t numKeys; if (!(resp = heapAlloc(sizeof(*resp) + NANOHUB_RSA_KEY_CHUNK_LEN))) return; ptr = BL.blGetPubKeysInfo(&numKeys); if (ptr && numKeys * RSA_BYTES > req->offset) { len = numKeys * RSA_BYTES - req->offset; if (len > NANOHUB_RSA_KEY_CHUNK_LEN) len = NANOHUB_RSA_KEY_CHUNK_LEN; memcpy(resp->data, (uint8_t *)ptr + req->offset, len); } resp->hdr.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 0); resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1 + len; resp->hdr.msg = NANOHUB_HAL_QUERY_RSA_KEYS; osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree); } static void halStartUpload(void *rx, uint8_t rx_len) { struct NanohubHalStartUploadRx *req = rx; struct NanohubStartFirmwareUploadRequest hwReq = { .size= req->length }; struct NanohubHalStartUploadTx *resp; if (!(resp = heapAlloc(sizeof(*resp)))) return; resp->hdr.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 0); resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1; resp->hdr.msg = NANOHUB_HAL_START_UPLOAD; resp->success = doStartFirmwareUpload(&hwReq); osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree); } static void halContUpload(void *rx, uint8_t rx_len) { uint32_t offset; uint32_t reply; uint8_t len; struct NanohubHalContUploadRx *req = rx; struct NanohubHalContUploadTx *resp; if (!(resp = heapAlloc(sizeof(*resp)))) return; resp->hdr.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 0); resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1; resp->hdr.msg = NANOHUB_HAL_CONT_UPLOAD; if (!mDownloadState) { reply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY; } else { offset = le32toh(req->offset); len = rx_len - sizeof(req->offset); reply = doFirmwareChunk(req->data, offset, len, resp); } if (reply != NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED) { osLog(LOG_ERROR, "%s: reply=%" PRIu32 "\n", __func__, reply); resp->success = false; osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree); } } static void halFinishUpload(void *rx, uint8_t rx_len) { struct NanohubHalFinishUploadTx *resp; uint32_t reply; if (!(resp = heapAlloc(sizeof(*resp)))) return; resp->hdr.appId = APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 0); resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1; resp->hdr.msg = NANOHUB_HAL_FINISH_UPLOAD; reply = doFinishFirmwareUpload(); osLog(LOG_INFO, "%s: reply=%" PRIu32 "\n", __func__, reply); resp->success = (reply == NANOHUB_FIRMWARE_UPLOAD_SUCCESS); osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree); } static void halReboot(void *rx, uint8_t rx_len) { BL.blReboot(); } const static struct NanohubHalCommand mBuiltinHalCommands[] = { NANOHUB_HAL_COMMAND(NANOHUB_HAL_EXT_APPS_ON, halExtAppsOn), NANOHUB_HAL_COMMAND(NANOHUB_HAL_EXT_APPS_OFF, halExtAppsOff), NANOHUB_HAL_COMMAND(NANOHUB_HAL_EXT_APP_DELETE, halExtAppDelete), NANOHUB_HAL_COMMAND(NANOHUB_HAL_QUERY_MEMINFO, halQueryMemInfo), NANOHUB_HAL_COMMAND(NANOHUB_HAL_QUERY_APPS, halQueryApps), NANOHUB_HAL_COMMAND(NANOHUB_HAL_QUERY_RSA_KEYS, halQueryRsaKeys), NANOHUB_HAL_COMMAND(NANOHUB_HAL_START_UPLOAD, halStartUpload), NANOHUB_HAL_COMMAND(NANOHUB_HAL_CONT_UPLOAD, halContUpload), NANOHUB_HAL_COMMAND(NANOHUB_HAL_FINISH_UPLOAD, halFinishUpload), NANOHUB_HAL_COMMAND(NANOHUB_HAL_REBOOT, halReboot), }; const struct NanohubHalCommand *nanohubHalFindCommand(uint8_t msg) { uint32_t i; for (i = 0; i < ARRAY_SIZE(mBuiltinHalCommands); i++) { const struct NanohubHalCommand *cmd = &mBuiltinHalCommands[i]; if (cmd->msg == msg) return cmd; } return NULL; } uint64_t hostGetTime(void) { int64_t delta = getAvgDelta(&mTimeSync); if (!delta || delta == INT64_MIN) return 0ULL; else return sensorGetTime() + delta; } #if DEBUG_APHUB_TIME_SYNC #define N_APHUB_SYNC_DATA 256 #define PRINT_DELAY 20000000 // unit ns, 20ms struct ApHubSyncDebug { uint64_t apFirst; uint64_t hubFirst; uint32_t apDelta[N_APHUB_SYNC_DATA]; // us uint32_t hubDelta[N_APHUB_SYNC_DATA]; // us int printIndex; //negative means not printing int writeIndex; uint32_t printTimer; }; struct ApHubSyncDebug mApHubSyncDebug = {0}; static void syncDebugCallback(uint32_t timerId, void *data) { if (mApHubSyncDebug.printIndex >= mApHubSyncDebug.writeIndex || mApHubSyncDebug.printIndex >= N_APHUB_SYNC_DATA) { timTimerCancel(mApHubSyncDebug.printTimer); osLog(LOG_DEBUG, "APHUB Done printing %d items", mApHubSyncDebug.printIndex); mApHubSyncDebug.writeIndex = 0; mApHubSyncDebug.printIndex = -1; mApHubSyncDebug.printTimer = 0; } else { if (mApHubSyncDebug.printIndex == 0) { osLog(LOG_DEBUG, "APHUB init %" PRIu64 " %" PRIu64, mApHubSyncDebug.apFirst, mApHubSyncDebug.hubFirst); } osLog(LOG_DEBUG, "APHUB %d %" PRIu32 " %" PRIu32, mApHubSyncDebug.printIndex, mApHubSyncDebug.apDelta[mApHubSyncDebug.printIndex], mApHubSyncDebug.hubDelta[mApHubSyncDebug.printIndex]); mApHubSyncDebug.printIndex++; } } static void syncDebugTriggerPrint() { if (mApHubSyncDebug.printTimer) { //printing already going return; } mApHubSyncDebug.printIndex = 0; syncDebugCallback(0, NULL); if (!(mApHubSyncDebug.printTimer = timTimerSet(PRINT_DELAY, 0, 50, syncDebugCallback, NULL, false /*oneShot*/))) { osLog(LOG_WARN, "Cannot get timer for printing"); mApHubSyncDebug.writeIndex = 0; // discard all data mApHubSyncDebug.printIndex = -1; // not printing } } static void syncDebugAdd(uint64_t ap, uint64_t hub) { if (mApHubSyncDebug.writeIndex >= N_APHUB_SYNC_DATA) { //full syncDebugTriggerPrint(); return; } if (mApHubSyncDebug.writeIndex == 0) { mApHubSyncDebug.apFirst = ap; mApHubSyncDebug.hubFirst = hub; } // convert ns to us mApHubSyncDebug.apDelta[mApHubSyncDebug.writeIndex] = (uint32_t) U64_DIV_BY_CONST_U16((ap - mApHubSyncDebug.apFirst), 1000u); mApHubSyncDebug.hubDelta[mApHubSyncDebug.writeIndex] = (uint32_t) U64_DIV_BY_CONST_U16((hub - mApHubSyncDebug.hubFirst), 1000u); ++mApHubSyncDebug.writeIndex; } #endif