blob: 8c55986c7fd6e11e940815a59012049d4dfd46d0 [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 <plat/inc/taggedPtr.h>
#include <plat/inc/rtc.h>
#include <inttypes.h>
#include <string.h>
#include <stdint.h>
#include <sys/endian.h>
#include <atomicBitset.h>
#include <hostIntf.h>
#include <hostIntf_priv.h>
#include <nanohubCommand.h>
#include <nanohubPacket.h>
#include <seos.h>
#include <util.h>
#include <mpu.h>
#include <heap.h>
#include <sensType.h>
#include <timer.h>
#include <crc.h>
#include <rsa.h>
#include <appSec.h>
#include <plat/inc/bl.h>
#include <plat/inc/plat.h>
#include <variant/inc/variant.h>
#define NANOHUB_COMMAND(_reason, _handler, _minReqType, _maxReqType) \
{ .reason = _reason, .handler = _handler, \
.minDataLen = sizeof(_minReqType), .maxDataLen = sizeof(_maxReqType) }
static 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;
} *mDownloadState;
static size_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 size_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 size_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) < 0) {
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)
{
extern const struct StmPlatEeDataGeneric __eedata_start;
extern char __eedata_end[];
const struct StmPlatEeDataGeneric *hdr;
const struct StmPlatEeDataEncrKey *key;
hdr = &__eedata_start;
while (((uintptr_t)&__eedata_end) - ((uintptr_t)hdr) >= sizeof(struct StmPlatEeDataGeneric) && hdr->eeDataType != 0xFFFFF) {
switch (hdr->eeDataType) {
case EE_DATA_TYPE_ENCR_KEY:
key = (const struct StmPlatEeDataEncrKey *)hdr;
if (key->keyID == keyIdx) {
memcpy(keyBuf, key->key, sizeof(key->key));
return APP_SEC_NO_ERROR;
}
break;
}
hdr = (const struct StmPlatEeDataGeneric*)(((((uintptr_t)hdr) + sizeof(struct StmPlatEeDataGeneric) + hdr->eeDataLen) + 3) &~ 3);
}
return APP_SEC_KEY_NOT_FOUND;
}
static void resetDownloadState()
{
if (mDownloadState->appSecState)
appSecDeinit(mDownloadState->appSecState);
mDownloadState->appSecState = appSecInit(writeCbk, pubKeyFindCbk, aesKeyAccessCbk, false);
mDownloadState->srcOffset = 0;
mDownloadState->srcCrc = ~0;
mDownloadState->dstOffset = 4; // skip over header
}
static size_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);
BL.blEraseShared(BL_FLASH_KEY1, BL_FLASH_KEY2);
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
mDownloadState->erase = false;
hostIntfSetInterrupt(NANOHUB_INT_CMD_WAIT);
}
}
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;
AppSecErr ret;
ret = appSecRxDataOver(mDownloadState->appSecState);
if (ret == 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) < 0) {
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
return NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND;
}
} else {
mpuAllowRamExecution(true);
mpuAllowRomWrite(true);
}
if (ret == 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) < 0) {
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) < 0) {
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
return NANOHUB_FIRMWARE_CHUNK_REPLY_RESEND;
}
mpuAllowRomWrite(false);
mpuAllowRamExecution(false);
heapFree(mDownloadState);
mDownloadState = NULL;
return NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
}
static void firmwareWrite(void *cookie)
{
AppSecErr ret;
if (mDownloadState->type == BL_FLASH_APP_ID)
ret = appSecRxData(mDownloadState->appSecState, mDownloadState->data, mDownloadState->len);
else
ret = writeCbk(mDownloadState->data, mDownloadState->len);
if (ret == APP_SEC_NO_ERROR) {
if (mDownloadState->srcOffset == mDownloadState->size && mDownloadState->crc == ~mDownloadState->srcCrc) {
mDownloadState->chunkReply = firmwareFinish(true);
} else {
mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
}
} else {
mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL;
}
hostIntfSetBusy(false);
}
static size_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;
} 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 size_t getInterrupt(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
{
struct NanohubGetInterruptResponse *resp = tx;
ATOMIC_BITSET_DECL(interrupts, MAX_INTERRUPTS,);
hostIntfCopyClearInterrupts(interrupts, MAX_INTERRUPTS);
memcpy(resp->interrupts, interrupts->words, sizeof(resp->interrupts));
return sizeof(*resp);
}
static size_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 size_t unmaskInterrupt(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
{
struct NanohubUnmaskInterruptRequest *req = rx;
struct NanohubUnmaskInterruptResponse *resp = tx;
hostInfClearInterruptMask(req->interrupt);
resp->accepted = true;
return sizeof(*resp);
}
struct EvtPacket
{
uint8_t sensType;
uint8_t length;
uint16_t pad;
uint64_t timestamp;
uint8_t data[NANOHUB_SENSOR_DATA_MAX];
} __attribute__((packed));
#define SYNC_DATAPOINTS 16
#define SYNC_RESET 10000000000ULL /* 10 seconds, ~100us drift */
struct TimeSync
{
uint64_t lastTime;
uint64_t delta[SYNC_DATAPOINTS];
uint64_t avgDelta;
uint8_t cnt;
uint8_t tail;
} __attribute__((packed));
static uint64_t getAvgDelta(struct TimeSync *sync);
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 size_t readEvent(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
{
struct NanohubReadEventRequest *req = rx;
struct NanohubReadEventResponse *resp = tx;
struct EvtPacket *packet = tx;
int length, sensor;
static struct TimeSync timeSync = { };
addDelta(&timeSync, req->apBootTime, timestamp);
if (hostIntfPacketDequeue(packet)) {
length = packet->length + sizeof(resp->evtType);
sensor = packet->sensType;
// TODO combine messages if multiple can fit in a single packet
if (sensor == SENS_TYPE_INVALID) {
#ifdef DEBUG_LOG_EVT
resp->evtType = htole32(DEBUG_LOG_EVT);
#else
resp->evtType = 0x00000000;
#endif
} else {
resp->evtType = htole32(EVT_NO_FIRST_SENSOR_EVENT + sensor);
if (packet->timestamp)
packet->timestamp += getAvgDelta(&timeSync);
}
} else {
length = 0;
}
return length;
}
static size_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;
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 = heapAlloc(rawPacket->dataLen);
if (!packet) {
resp->accepted = false;
} else {
memcpy(packet, rawPacket+1, rawPacket->dataLen);
resp->accepted = osEnqueuePrivateEvt(EVT_APP_FROM_HOST, packet, heapFree, tid);
if (!resp->accepted)
heapFree(packet);
}
} else {
resp->accepted = false;
}
} else {
packet = heapAlloc(rx_len - sizeof(req->evtType));
if (!packet) {
resp->accepted = false;
} else {
memcpy(packet, req->evtData, rx_len - sizeof(req->evtType));
resp->accepted = osEnqueueEvt(le32toh(req->evtType), packet, heapFree);
if (!resp->accepted)
heapFree(packet);
}
}
return sizeof(*resp);
}
const static struct NanohubCommand mBuiltinCommands[] = {
NANOHUB_COMMAND(NANOHUB_REASON_GET_OS_HW_VERSIONS,
getOsHwVersion,
struct NanohubOsHwVersionsRequest,
struct NanohubOsHwVersionsRequest),
NANOHUB_COMMAND(NANOHUB_REASON_GET_APP_VERSIONS,
getAppVersion,
struct NanohubAppVersionsRequest,
struct NanohubAppVersionsRequest),
NANOHUB_COMMAND(NANOHUB_REASON_QUERY_APP_INFO,
queryAppInfo,
struct NanohubAppInfoRequest,
struct NanohubAppInfoRequest),
NANOHUB_COMMAND(NANOHUB_REASON_START_FIRMWARE_UPLOAD,
startFirmwareUpload,
struct NanohubStartFirmwareUploadRequest,
struct NanohubStartFirmwareUploadRequest),
NANOHUB_COMMAND(NANOHUB_REASON_FIRMWARE_CHUNK,
firmwareChunk,
__le32,
struct NanohubFirmwareChunkRequest),
NANOHUB_COMMAND(NANOHUB_REASON_GET_INTERRUPT,
getInterrupt,
struct NanohubGetInterruptRequest,
struct NanohubGetInterruptRequest),
NANOHUB_COMMAND(NANOHUB_REASON_MASK_INTERRUPT,
maskInterrupt,
struct NanohubMaskInterruptRequest,
struct NanohubMaskInterruptRequest),
NANOHUB_COMMAND(NANOHUB_REASON_UNMASK_INTERRUPT,
unmaskInterrupt,
struct NanohubUnmaskInterruptRequest,
struct NanohubUnmaskInterruptRequest),
NANOHUB_COMMAND(NANOHUB_REASON_READ_EVENT,
readEvent,
struct NanohubReadEventRequest,
struct NanohubReadEventRequest),
NANOHUB_COMMAND(NANOHUB_REASON_WRITE_EVENT,
writeEvent,
__le32,
struct NanohubWriteEventRequest),
};
const struct NanohubCommand *nanohubFindCommand(uint32_t packetReason)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(mBuiltinCommands); i++) {
const struct NanohubCommand *cmd = &mBuiltinCommands[i];
if (cmd->reason == packetReason)
return cmd;
}
return NULL;
}