blob: e61eb612f8d2b1660f2e4e6abb12b1f794b9a48d [file] [log] [blame]
/*
* 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/rtc.h>
#include <plat/inc/bl.h>
#include <plat/inc/plat.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 <crc.h>
#include <rsa.h>
#include <appSec.h>
#include <cpu.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 }
#define SYNC_DATAPOINTS 16
#define SYNC_RESET 10000000000ULL /* 10 seconds, ~100us drift */
struct DownloadState
{
struct AppSecState *appSecState;
uint32_t size;
uint32_t srcOffset;
uint32_t dstOffset;
uint8_t *start;
uint32_t crc;
uint32_t srcCrc;
uint8_t data[NANOHUB_PACKET_PAYLOAD_MAX];
uint8_t len;
uint8_t chunkReply;
uint8_t type;
bool erase;
};
struct TimeSync
{
uint64_t lastTime;
uint64_t delta[SYNC_DATAPOINTS];
uint64_t avgDelta;
uint8_t cnt;
uint8_t tail;
};
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 TimeSync 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;
mpuAllowRamExecution(true);
mpuAllowRomWrite(true);
if (!BL.blProgramShared(mDownloadState->start + mDownloadState->dstOffset, (uint8_t *)data, len, BL_FLASH_KEY1, BL_FLASH_KEY2)) {
ret = APP_SEC_BAD;
} else {
ret = APP_SEC_NO_ERROR;
mDownloadState->dstOffset += len;
}
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
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 aesKeyAccessCbk(uint64_t keyIdx, 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 == keyIdx) {
memcpy(keyBuf, kd.key, sizeof(kd.key));
return APP_SEC_NO_ERROR;
}
}
return APP_SEC_KEY_NOT_FOUND;
}
static void freeDownloadState()
{
if (mDownloadState->appSecState)
appSecDeinit(mDownloadState->appSecState);
heapFree(mDownloadState);
mDownloadState = NULL;
}
static void resetDownloadState()
{
mAppSecStatus = APP_SEC_NO_ERROR;
if (mDownloadState->appSecState)
appSecDeinit(mDownloadState->appSecState);
mDownloadState->appSecState = appSecInit(writeCbk, pubKeyFindCbk, aesKeyAccessCbk, true);
mDownloadState->srcOffset = 0;
mDownloadState->srcCrc = ~0;
mDownloadState->dstOffset = 4; // skip over header
}
static uint32_t startFirmwareUpload(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
{
struct NanohubStartFirmwareUploadRequest *req = rx;
struct NanohubStartFirmwareUploadResponse *resp = tx;
uint8_t *shared, *shared_start, *shared_end;
int len, total_len;
uint32_t sharedSz;
shared_start = platGetSharedAreaInfo(&sharedSz);
shared_end = shared_start + sharedSz;
if (!mDownloadState) {
mDownloadState = heapAlloc(sizeof(struct DownloadState));
if (!mDownloadState) {
resp->accepted = false;
return sizeof(*resp);
} else {
memset(mDownloadState, 0x00, sizeof(struct DownloadState));
}
}
mDownloadState->type = req->type;
mDownloadState->size = le32toh(req->size);
mDownloadState->crc = le32toh(req->crc);
mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
for (shared = shared_start;
shared < shared_end && shared[0] != 0xFF;
shared += total_len) {
len = (shared[1] << 16) | (shared[2] << 8) | shared[3];
total_len = sizeof(uint32_t) + ((len + 3) & ~3) + sizeof(uint32_t);
}
if (shared + sizeof(uint32_t) + ((mDownloadState->size + 3) & ~3) + sizeof(uint32_t) < shared_end) {
mDownloadState->start = shared;
mDownloadState->erase = false;
} else {
mDownloadState->start = shared_start;
mDownloadState->erase = true;
}
resetDownloadState();
resp->accepted = true;
return sizeof(*resp);
}
static void firmwareErase(void *cookie)
{
osLog(LOG_INFO, "hostIntfFirmwareErase: Firmware Erase\n");
if (mDownloadState->erase == true) {
mpuAllowRamExecution(true);
mpuAllowRomWrite(true);
(void)BL.blEraseShared(BL_FLASH_KEY1, BL_FLASH_KEY2);
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
mDownloadState->erase = false;
hostIntfSetInterrupt(NANOHUB_INT_CMD_WAIT);
}
}
static AppSecErr giveAppSecTimeIfNeeded(struct AppSecState *state, AppSecErr prevRet)
{
/* XXX: this will need to go away for real asynchronicity */
while (prevRet == APP_SEC_NEED_MORE_TIME)
prevRet = appSecDoSomeProcessing(state);
return prevRet;
}
static uint8_t firmwareFinish(bool valid)
{
uint8_t buffer[7];
int padlen;
uint32_t crc;
uint16_t marker;
static const char magic[] = APP_HDR_MAGIC;
const struct AppHdr *app;
mAppSecStatus = appSecRxDataOver(mDownloadState->appSecState);
mAppSecStatus = giveAppSecTimeIfNeeded(mDownloadState->appSecState, mAppSecStatus);
if (mAppSecStatus == APP_SEC_NO_ERROR && valid && mDownloadState->type == BL_FLASH_APP_ID) {
app = (const struct AppHdr *)&mDownloadState->start[4];
if (app->marker != APP_HDR_MARKER_UPLOADING ||
mDownloadState->size < sizeof(uint32_t) + sizeof(struct AppHdr) ||
memcmp(magic, app->magic, sizeof(magic)-1) ||
app->fmtVer != APP_HDR_VER_CUR) {
marker = APP_HDR_MARKER_DELETED;
} else {
marker = APP_HDR_MARKER_VALID;
}
osLog(LOG_INFO, "Loaded %s app: %ld bytes @ %p\n", marker == APP_HDR_MARKER_VALID ? "valid" : "invalid", mDownloadState->size, mDownloadState->start);
mpuAllowRamExecution(true);
mpuAllowRomWrite(true);
if (!BL.blProgramShared((uint8_t *)&app->marker, (uint8_t *)&marker, sizeof(marker), BL_FLASH_KEY1, BL_FLASH_KEY2)) {
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
return NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND;
}
} else {
mpuAllowRamExecution(true);
mpuAllowRomWrite(true);
}
if (mAppSecStatus == APP_SEC_NO_ERROR && valid)
buffer[0] = ((mDownloadState->type & 0xF) << 4) | (mDownloadState->type & 0xF);
else
buffer[0] = 0x00;
buffer[1] = (mDownloadState->dstOffset - 4) >> 16;
buffer[2] = (mDownloadState->dstOffset - 4) >> 8;
buffer[3] = (mDownloadState->dstOffset - 4);
if (!BL.blProgramShared(mDownloadState->start, buffer, 4, BL_FLASH_KEY1, BL_FLASH_KEY2)) {
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
return NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND;
}
crc = ~crc32(mDownloadState->start, mDownloadState->dstOffset, ~0);
padlen = (4 - (mDownloadState->dstOffset & 3)) & 3;
memset(buffer, 0x00, padlen);
memcpy(&buffer[padlen], &crc, sizeof(uint32_t));
mDownloadState->size = mDownloadState->dstOffset + padlen + sizeof(uint32_t);
if (!BL.blProgramShared(mDownloadState->start + mDownloadState->dstOffset, buffer, padlen + sizeof(uint32_t), BL_FLASH_KEY1, BL_FLASH_KEY2)) {
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
return NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND;
}
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
freeDownloadState();
return NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
}
static void firmwareWrite(void *cookie)
{
uint32_t reply;
if (mDownloadState->type == BL_FLASH_APP_ID) {
/* XXX: this will need to change for real asynchronicity */
const uint8_t *data = mDownloadState->data;
uint32_t len = mDownloadState->len, lenLeft;
mAppSecStatus = APP_SEC_NO_ERROR;
while (len) {
mAppSecStatus = appSecRxData(mDownloadState->appSecState, data, len, &lenLeft);
data += len - lenLeft;
len = lenLeft;
mAppSecStatus = giveAppSecTimeIfNeeded(mDownloadState->appSecState, mAppSecStatus);
}
}
else
mAppSecStatus = writeCbk(mDownloadState->data, mDownloadState->len);
if (mAppSecStatus == APP_SEC_NO_ERROR) {
if (mDownloadState->srcOffset == mDownloadState->size && mDownloadState->crc == ~mDownloadState->srcCrc) {
reply = firmwareFinish(true);
if (mDownloadState)
mDownloadState->chunkReply = reply;
} else {
mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
}
} else {
freeDownloadState();
}
hostIntfSetBusy(false);
}
static uint32_t firmwareChunk(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
{
uint32_t offset;
uint8_t len;
struct NanohubFirmwareChunkRequest *req = rx;
struct NanohubFirmwareChunkResponse *resp = tx;
offset = le32toh(req->offset);
len = rx_len - sizeof(req->offset);
if (!mDownloadState) {
resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY;
} else if (mDownloadState->chunkReply != NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED) {
resp->chunkReply = mDownloadState->chunkReply;
freeDownloadState();
} else if (mDownloadState->erase == true) {
resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_WAIT;
osDefer(firmwareErase, NULL, false);
} else if (offset != mDownloadState->srcOffset) {
resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_RESTART;
resetDownloadState();
} else {
mDownloadState->srcCrc = crc32(req->data, len, mDownloadState->srcCrc);
mDownloadState->srcOffset += len;
if ((mDownloadState->srcOffset == mDownloadState->size && mDownloadState->crc != ~mDownloadState->srcCrc) || (mDownloadState->srcOffset > mDownloadState->size)) {
firmwareFinish(false);
resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL;
} else {
memcpy(mDownloadState->data, req->data, len);
mDownloadState->len = len;
resp->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
hostIntfSetBusy(true);
osDefer(firmwareWrite, NULL, false);
}
}
return sizeof(*resp);
}
static uint32_t finishFirmwareUpload(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
{
struct NanohubFinishFirmwareUploadResponse *resp = tx;
if (!mDownloadState) {
switch (mAppSecStatus) {
case APP_SEC_NO_ERROR:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_SUCCESS;
break;
case APP_SEC_KEY_NOT_FOUND:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_KEY_NOT_FOUND;
break;
case APP_SEC_HEADER_ERROR:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_HEADER_ERROR;
break;
case APP_SEC_TOO_MUCH_DATA:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_MUCH_DATA;
break;
case APP_SEC_TOO_LITTLE_DATA:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_LITTLE_DATA;
break;
case APP_SEC_SIG_VERIFY_FAIL:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_VERIFY_FAIL;
break;
case APP_SEC_SIG_DECODE_FAIL:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_DECODE_FAIL;
break;
case APP_SEC_SIG_ROOT_UNKNOWN:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_ROOT_UNKNOWN;
break;
case APP_SEC_MEMORY_ERROR:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_MEMORY_ERROR;
break;
case APP_SEC_INVALID_DATA:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_INVALID_DATA;
break;
default:
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_BAD;
break;
}
} else if (mDownloadState->srcOffset == mDownloadState->size) {
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_PROCESSING;
} else {
resp->uploadReply = NANOHUB_FIRMWARE_UPLOAD_WAITING_FOR_DATA;
}
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 TimeSync *sync, uint64_t apTime, uint64_t hubTime)
{
if (apTime - sync->lastTime > SYNC_RESET) {
sync->tail = 0;
sync->cnt = 0;
}
sync->delta[sync->tail++] = apTime - hubTime;
sync->lastTime = apTime;
if (sync->tail >= SYNC_DATAPOINTS)
sync->tail = 0;
if (sync->cnt < SYNC_DATAPOINTS)
sync->cnt ++;
sync->avgDelta = 0ULL;
}
static uint64_t getAvgDelta(struct TimeSync *sync)
{
int i;
int32_t avg;
if (!sync->cnt)
return 0ULL;
else if (!sync->avgDelta) {
for (i=1, avg=0; i<sync->cnt; i++)
avg += (int32_t)(sync->delta[i] - sync->delta[0]);
sync->avgDelta = (avg / sync->cnt) + sync->delta[0];
}
return sync->avgDelta;
}
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;
}
if (mTxNextLength > 0) {
length = mTxNextLength;
wakeup = atomicRead32bits(&mTxWakeCnt[0]);
nonwakeup = atomicRead32bits(&mTxWakeCnt[1]);
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 NanohubHalStartUploadTx *resp;
uint8_t *shared, *shared_start, *shared_end;
int len, total_len;
uint32_t sharedSz;
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;
shared_start = platGetSharedAreaInfo(&sharedSz);
shared_end = shared_start + sharedSz;
if (!mDownloadState) {
mDownloadState = heapAlloc(sizeof(struct DownloadState));
if (!mDownloadState) {
resp->success = false;
osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
return;
} else {
memset(mDownloadState, 0x00, sizeof(struct DownloadState));
}
}
mDownloadState->type = req->isOs ? BL_FLASH_KERNEL_ID : BL_FLASH_APP_ID;
mDownloadState->size = le32toh(req->length);
mDownloadState->crc = le32toh(0x00000000);
mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
for (shared = shared_start;
shared < shared_end && shared[0] != 0xFF;
shared += total_len) {
len = (shared[1] << 16) | (shared[2] << 8) | shared[3];
total_len = sizeof(uint32_t) + ((len + 3) & ~3) + sizeof(uint32_t);
}
if (shared + sizeof(uint32_t) + ((mDownloadState->size + 3) & ~3) + sizeof(uint32_t) < shared_end) {
mDownloadState->start = shared;
mDownloadState->erase = false;
} else {
mDownloadState->start = shared_start;
mDownloadState->erase = true;
}
resetDownloadState();
resp->success = true;
osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
}
static void halContUpload(void *rx, uint8_t rx_len)
{
struct NanohubHalContUploadRx *req = rx;
struct NanohubHalContUploadTx *resp;
uint32_t offset;
uint8_t len;
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;
offset = le32toh(req->offset);
len = rx_len - sizeof(req->offset);
if (!mDownloadState) {
resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY;
} else if (mDownloadState->erase == true) {
resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_WAIT;
firmwareErase(NULL);
} else if (offset != mDownloadState->srcOffset) {
resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_RESTART;
resetDownloadState();
} else if (mDownloadState->srcOffset + len <= mDownloadState->size) {
mDownloadState->srcOffset += len;
memcpy(mDownloadState->data, req->data, len);
mDownloadState->len = len;
hostIntfSetBusy(true);
firmwareWrite(NULL);
if (mDownloadState)
resp->success = mDownloadState->chunkReply;
else
resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
} else {
resp->success = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY;
}
resp->success = !resp->success;
osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
}
static void halFinishUpload(void *rx, uint8_t rx_len)
{
struct NanohubHalFinishUploadTx *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_FINISH_UPLOAD;
if (!mDownloadState) {
switch (mAppSecStatus) {
case APP_SEC_NO_ERROR:
resp->success = NANOHUB_FIRMWARE_UPLOAD_SUCCESS;
break;
case APP_SEC_KEY_NOT_FOUND:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_KEY_NOT_FOUND;
break;
case APP_SEC_HEADER_ERROR:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_HEADER_ERROR;
break;
case APP_SEC_TOO_MUCH_DATA:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_MUCH_DATA;
break;
case APP_SEC_TOO_LITTLE_DATA:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_TOO_LITTLE_DATA;
break;
case APP_SEC_SIG_VERIFY_FAIL:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_VERIFY_FAIL;
break;
case APP_SEC_SIG_DECODE_FAIL:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_DECODE_FAIL;
break;
case APP_SEC_SIG_ROOT_UNKNOWN:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_SIG_ROOT_UNKNOWN;
break;
case APP_SEC_MEMORY_ERROR:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_MEMORY_ERROR;
break;
case APP_SEC_INVALID_DATA:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_INVALID_DATA;
break;
default:
resp->success = NANOHUB_FIRMWARE_UPLOAD_APP_SEC_BAD;
break;
}
} else if (mDownloadState->srcOffset == mDownloadState->size) {
resp->success = NANOHUB_FIRMWARE_UPLOAD_PROCESSING;
} else {
resp->success = NANOHUB_FIRMWARE_UPLOAD_WAITING_FOR_DATA;
}
resp->success = !resp->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;
}