nanohub: new contexthub interface using chre messaging

range check messages
do not auto erase flash when writing an nanoapp that doesn't fit
enable crc calculation/checking for nanoapps
do not auto start chre 1.1+ nanoapps

Bug: 69980445
Test: check out new commands and make sure they work
Change-Id: I98af1f3d5c848be19a1d3687d1d51a58145ff956
Signed-off-by: Ben Fennema <fennema@google.com>
diff --git a/firmware/Android.mk b/firmware/Android.mk
index db2fb86..3f4b909 100644
--- a/firmware/Android.mk
+++ b/firmware/Android.mk
@@ -88,6 +88,7 @@
     libnanohub_os \
 
 LOCAL_STATIC_LIBRARIES := \
+    libnanohub_common_os \
     libnanomath_os \
     libnanolibc_os \
 
diff --git a/firmware/firmware.mk b/firmware/firmware.mk
index 9e02634..221ee0c 100644
--- a/firmware/firmware.mk
+++ b/firmware/firmware.mk
@@ -85,9 +85,7 @@
 #some help for bootloader
 SRCS_bl += os/core/printf.c
 
-ifndef PLATFORM_HAS_HARDWARE_CRC
 SRCS_os += ../lib/nanohub/softcrc.c
-endif
 
 #extra deps
 DEPS += $(wildcard inc/*.h)
diff --git a/firmware/os/core/heap.c b/firmware/os/core/heap.c
index 2f38a0d..f6e5067 100644
--- a/firmware/os/core/heap.c
+++ b/firmware/os/core/heap.c
@@ -229,3 +229,50 @@
 
     return count;
 }
+
+int heapGetFreeSize(int *numChunks, int *largestChunk)
+{
+    struct HeapNode *node;
+    bool haveLock;
+    int bytes = 0;
+    *numChunks = *largestChunk = 0;
+
+    // this can only fail if called from interrupt
+    haveLock = trylockTryTake(&gHeapLock);
+    if (!haveLock)
+        return -1;
+
+    for (node = gHeapHead; node; node = heapPrvGetNext(node)) {
+        if (!node->used) {
+            if (node->size > *largestChunk)
+                *largestChunk = node->size;
+            bytes += node->size + sizeof(struct HeapNode);
+            (*numChunks)++;
+        }
+    }
+    trylockRelease(&gHeapLock);
+
+    return bytes;
+}
+
+int heapGetTaskSize(uint32_t tid)
+{
+    struct HeapNode *node;
+    bool haveLock;
+    int bytes = 0;
+
+    // this can only fail if called from interrupt
+    haveLock = trylockTryTake(&gHeapLock);
+    if (!haveLock)
+        return -1;
+
+    tid &= TIDX_MASK;
+    for (node = gHeapHead; node; node = heapPrvGetNext(node)) {
+        if (node->used && node->tidx == tid) {
+            bytes += node->size + sizeof(struct HeapNode);
+        }
+    }
+    trylockRelease(&gHeapLock);
+
+    return bytes;
+}
diff --git a/firmware/os/core/hostIntf.c b/firmware/os/core/hostIntf.c
index 325bf6f..ccd571d 100644
--- a/firmware/os/core/hostIntf.c
+++ b/firmware/os/core/hostIntf.c
@@ -35,6 +35,7 @@
 #include <nanohubCommand.h>
 #include <nanohubPacket.h>
 #include <seos.h>
+#include <seos_priv.h>
 #include <util.h>
 #include <atomicBitset.h>
 #include <atomic.h>
@@ -1097,18 +1098,36 @@
 
 static void hostIntfNotifyReboot(uint32_t reason)
 {
-    struct NanohubHalRebootTx *resp = heapAlloc(sizeof(*resp));
     __le32 raw_reason = htole32(reason);
 
+    struct NanohubHalSysMgmtTx *resp;
+    resp = heapAlloc(sizeof(*resp));
     if (resp) {
-        resp->hdr = (struct NanohubHalHdr){
+        resp->hdr = (struct NanohubHalHdr) {
             .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
-            .len = sizeof(*resp) - sizeof(resp->hdr) + sizeof(resp->hdr.msg),
-            .msg = NANOHUB_HAL_REBOOT,
+            .len = sizeof(*resp) - sizeof(resp->hdr),
         };
-        memcpy(&resp->reason, &raw_reason, sizeof(resp->reason));
-        osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
+        resp->ret = (struct NanohubHalRet) {
+            .msg = NANOHUB_HAL_SYS_MGMT,
+            .status = raw_reason,
+        };
+        resp->cmd = NANOHUB_HAL_SYS_MGMT_REBOOT;
+        osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
     }
+
+#ifdef LEGACY_HAL_ENABLED
+    struct NanohubHalLegacyRebootTx *respLegacy;
+    respLegacy = heapAlloc(sizeof(*respLegacy));
+    if (respLegacy) {
+        respLegacy->hdr = (struct NanohubHalLegacyHdr) {
+            .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+            .len = sizeof(*respLegacy) - sizeof(respLegacy->hdr) + sizeof(respLegacy->hdr.msg),
+            .msg = NANOHUB_HAL_LEGACY_REBOOT,
+        };
+        memcpy(&respLegacy->reason, &raw_reason, sizeof(respLegacy->reason));
+        osEnqueueEvtOrFree(EVT_APP_TO_HOST, respLegacy, heapFree);
+    }
+#endif
 }
 
 static void queueFlush(struct ActiveSensor *sensor)
@@ -1203,12 +1222,43 @@
     }
 }
 
+#ifdef LEGACY_HAL_ENABLED
+static void handleLegacyHalCmd(const uint8_t *halData, uint8_t size)
+{
+    const struct NanohubHalLegacyCommand *halCmd = nanohubHalLegacyFindCommand(halData[0]);
+    if (halCmd)
+        halCmd->handler((void *)&halData[1], size - 1);
+}
+
 static void onEvtAppFromHost(const void *evtData)
 {
     const uint8_t *halMsg = evtData;
-    const struct NanohubHalCommand *halCmd = nanohubHalFindCommand(halMsg[1]);
-    if (halCmd)
-        halCmd->handler((void *)&halMsg[2], halMsg[0] - 1);
+    handleLegacyHalCmd(&halMsg[1], halMsg[0]);
+}
+#endif
+
+static void onEvtAppFromHostChre(const void *evtData)
+{
+    const struct NanohubMsgChreHdr *halMsg = (const struct NanohubMsgChreHdr *)evtData;
+    const struct NanohubHalCommand *halCmd;
+    const uint8_t *halData = (const uint8_t *)(halMsg+1);
+    uint8_t len;
+    uint32_t transactionId;
+
+    memcpy(&transactionId, &halMsg->appEvent, sizeof(halMsg->appEvent));
+
+    if (halMsg->size >= 1) {
+        len = halMsg->size - 1;
+        halCmd = nanohubHalFindCommand(halData[0]);
+        if (halCmd) {
+            if (len >= halCmd->minDataLen && len <= halCmd->maxDataLen)
+                halCmd->handler((void *)&halData[1], len, transactionId);
+            return;
+        }
+    }
+#ifdef LEGACY_HAL_ENABLED
+    handleLegacyHalCmd(halData, halMsg->size);
+#endif
 }
 
 #ifdef DEBUG_LOG_EVT
@@ -1502,7 +1552,7 @@
 
 static void hostIntfHandleEvent(uint32_t evtType, const void* evtData)
 {
-    switch (evtType) {
+    switch (EVENT_GET_EVENT(evtType)) {
     case EVT_APP_START:
         onEvtAppStart(evtData);
         break;
@@ -1512,9 +1562,14 @@
     case EVT_APP_TO_HOST_CHRE:
         onEvtAppToHostChre(evtData);
         break;
+#ifdef LEGACY_HAL_ENABLED
     case EVT_APP_FROM_HOST:
         onEvtAppFromHost(evtData);
         break;
+#endif
+    case EVT_APP_FROM_HOST_CHRE:
+        onEvtAppFromHostChre(evtData);
+        break;
 #ifdef DEBUG_LOG_EVT
     case EVT_DEBUG_LOG:
         onEvtDebugLog(evtData);
@@ -1530,7 +1585,7 @@
         onEvtAppToSensorHalData(evtData);
         break;
     default:
-        onEvtSensorData(evtType, evtData);
+        onEvtSensorData(EVENT_GET_EVENT(evtType), evtData);
         break;
     }
 }
@@ -1633,4 +1688,4 @@
     cpuIntsRestore(state);
 }
 
-INTERNAL_APP_INIT(APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0), 0, hostIntfRequest, hostIntfRelease, hostIntfHandleEvent);
+INTERNAL_CHRE_APP_INIT(APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0), 0, hostIntfRequest, hostIntfRelease, hostIntfHandleEvent);
diff --git a/firmware/os/core/nanohubCommand.c b/firmware/os/core/nanohubCommand.c
index 1c31bb3..d679e73 100644
--- a/firmware/os/core/nanohubCommand.c
+++ b/firmware/os/core/nanohubCommand.c
@@ -39,6 +39,7 @@
 #include <nanohubPacket.h>
 #include <eeData.h>
 #include <seos.h>
+#include <seos_priv.h>
 #include <util.h>
 #include <mpu.h>
 #include <heap.h>
@@ -49,6 +50,7 @@
 #include <cpu.h>
 #include <cpu/cpuMath.h>
 #include <algos/ap_hub_sync.h>
+#include <sensors_priv.h>
 
 #include <chre.h>
 
@@ -56,9 +58,13 @@
         { .reason = _reason, .fastHandler = _fastHandler, .handler = _handler, \
           .minDataLen = sizeof(_minReqType), .maxDataLen = sizeof(_maxReqType) }
 
-#define NANOHUB_HAL_COMMAND(_msg, _handler) \
+#define NANOHUB_HAL_LEGACY_COMMAND(_msg, _handler) \
         { .msg = _msg, .handler = _handler }
 
+#define NANOHUB_HAL_COMMAND(_msg, _handler, _minReqType, _maxReqType) \
+        { .msg = _msg, .handler = _handler, \
+          .minDataLen = sizeof(_minReqType), .maxDataLen = sizeof(_maxReqType) }
+
 // 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
@@ -91,6 +97,7 @@
 
 static struct DownloadState *mDownloadState;
 static AppSecErr mAppSecStatus;
+static struct AppHdr *mApp;
 static struct SlabAllocator *mEventSlab;
 static struct HostIntfDataBuffer mTxCurr, mTxNext;
 static uint8_t mTxCurrLength, mTxNextLength;
@@ -263,7 +270,7 @@
     mDownloadState = NULL;
 }
 
-static void resetDownloadState(bool initial)
+static bool resetDownloadState(bool initial, bool erase)
 {
     bool doCreate = true;
 
@@ -280,14 +287,19 @@
         else
             doCreate = false;
     }
+    mDownloadState->dstOffset = 0;
     if (doCreate)
         mDownloadState->start = osAppSegmentCreate(mDownloadState->size);
-    if (!mDownloadState->start)
-        mDownloadState->erase = true;
-    mDownloadState->dstOffset = 0;
+    if (!mDownloadState->start) {
+        if (erase)
+            mDownloadState->erase = true;
+        else
+            return false;
+    }
+    return true;
 }
 
-static bool doStartFirmwareUpload(struct NanohubStartFirmwareUploadRequest *req)
+static bool doStartFirmwareUpload(struct NanohubStartFirmwareUploadRequest *req, bool erase)
 {
     if (!mDownloadState) {
         mDownloadState = heapAlloc(sizeof(struct DownloadState));
@@ -301,9 +313,7 @@
     mDownloadState->size = le32toh(req->size);
     mDownloadState->crc = le32toh(req->crc);
     mDownloadState->chunkReply = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
-    resetDownloadState(true);
-
-    return true;
+    return resetDownloadState(true, erase);
 }
 
 static uint32_t startFirmwareUpload(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
@@ -311,7 +321,7 @@
     struct NanohubStartFirmwareUploadRequest *req = rx;
     struct NanohubStartFirmwareUploadResponse *resp = tx;
 
-    resp->accepted = doStartFirmwareUpload(req);
+    resp->accepted = doStartFirmwareUpload(req, true);
 
     return sizeof(*resp);
 }
@@ -444,15 +454,18 @@
     if (!osAppSegmentClose(app, mDownloadState->dstOffset, segState)) {
         osLog(LOG_INFO, "%s: Failed to close segment\n", __func__);
         valid = false;
+        mApp = NULL;
     } else {
         segState = osAppSegmentGetState(app);
+        mApp = app;
         valid = (segState == SEG_ST_VALID);
     }
     osLog(LOG_INFO, "Loaded %s image type %" PRIu8 ": %" PRIu32
-                    " bytes @ %p; state=%02" PRIX32 "\n",
+                    " bytes @ %p; state=%02" PRIX32 "; crc=%08" PRIX32 "\n",
                     valid ? "valid" : "invalid",
                     app->hdr.payInfoType, mDownloadState->size,
-                    mDownloadState->start, segState);
+                    mDownloadState->start, segState,
+                    mApp ? osAppSegmentGetCrc(mApp) : 0xFFFFFFFF);
 
     freeDownloadState(); // no more access to mDownloadState
 
@@ -502,11 +515,30 @@
     mDownloadState->eraseScheduled = false;
 }
 
+SET_PACKED_STRUCT_MODE_ON
+struct FirmwareWriteCookie
+{
+    uint32_t evtType;
+    union {
+#ifdef LEGACY_HAL_ENABLED
+        struct NanohubHalLegacyContUploadTx respLegacy;
+#endif
+        struct NanohubHalContUploadTx resp;
+    };
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+static void writeCookieFree(void *ptr)
+{
+    struct FirmwareWriteCookie *buf = container_of(ptr, struct FirmwareWriteCookie, resp);
+    heapFree(buf);
+}
+
 static void firmwareWrite(void *cookie)
 {
     bool valid;
     bool finished = false;
-    struct NanohubHalContUploadTx *resp = cookie;
+    struct FirmwareWriteCookie *resp = cookie;
     // only check crc when cookie is NULL (write came from kernel, not HAL)
     bool checkCrc = !cookie;
 
@@ -545,8 +577,15 @@
             valid = false;
     }
     if (resp) {
-        resp->success = valid;
-        osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
+        if (resp->evtType == EVT_APP_TO_HOST) {
+#ifdef LEGACY_HAL_ENABLED
+            resp->respLegacy.success = valid;
+            osEnqueueEvtOrFree(EVT_APP_TO_HOST, &resp->respLegacy, writeCookieFree);
+#endif
+        } else {
+            resp->resp.ret.status = !valid;
+            osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, &resp->resp, writeCookieFree);
+        }
     }
 }
 
@@ -575,10 +614,10 @@
             firmwareFinish(false);
         } else if (offset != mDownloadState->srcOffset) {
             reply = NANOHUB_FIRMWARE_CHUNK_REPLY_RESTART;
-            resetDownloadState(false);
+            resetDownloadState(false, true);
         } else {
             if (!cookie)
-                mDownloadState->srcCrc = crc32(data, len, mDownloadState->srcCrc);
+                mDownloadState->srcCrc = soft_crc32(data, len, mDownloadState->srcCrc);
             mDownloadState->srcOffset += len;
             memcpy(mDownloadState->data, data, len);
             mDownloadState->lenLeft = mDownloadState->len = len;
@@ -602,12 +641,24 @@
     return sizeof(*resp);
 }
 
-static uint32_t doFinishFirmwareUpload()
+static uint32_t doFinishFirmwareUpload(uint32_t *addr, uint32_t *crc)
 {
     uint32_t reply;
 
     if (!mDownloadState) {
         reply = appSecErrToNanohubReply(mAppSecStatus);
+        if (addr) {
+            if (mApp)
+                *addr = (uint32_t)mApp;
+            else
+                *addr = 0xFFFFFFFF;
+        }
+        if (crc) {
+            if (mApp)
+                *crc = osAppSegmentGetCrc(mApp);
+            else
+                *crc = 0xFFFFFFFF;
+        }
     } else if (mDownloadState->srcOffset == mDownloadState->size) {
         reply = NANOHUB_FIRMWARE_UPLOAD_PROCESSING;
     } else {
@@ -620,7 +671,7 @@
 static uint32_t finishFirmwareUpload(void *rx, uint8_t rx_len, void *tx, uint64_t timestamp)
 {
     struct NanohubFinishFirmwareUploadResponse *resp = tx;
-    resp->uploadReply = doFinishFirmwareUpload();
+    resp->uploadReply = doFinishFirmwareUpload(NULL, NULL);
     if (resp->uploadReply != NANOHUB_FIRMWARE_UPLOAD_PROCESSING)
         osLog(LOG_INFO, "%s: reply=%" PRIu8 "\n", __func__, resp->uploadReply);
     return sizeof(*resp);
@@ -666,26 +717,12 @@
     return sizeof(*resp);
 }
 
-static void nanohubDelayStartApps(void *cookie)
-{
-    uint32_t status = 0;
-    status = osExtAppStartAppsDelayed();
-    osLog(LOG_DEBUG, "Started delayed apps; EXT status: %08" PRIX32 "\n", status);
-}
-
 static void addDelta(struct ApHubSync *sync, uint64_t apTime, uint64_t hubTime)
 {
-    static bool delayStart = false;
-
 #if DEBUG_APHUB_TIME_SYNC
     syncDebugAdd(apTime, hubTime);
 #endif
     apHubSyncAddDelta(sync, apTime, hubTime);
-
-    if (!delayStart) {
-        delayStart = true;
-        osDefer(nanohubDelayStartApps, NULL, false);
-    }
 }
 
 static int64_t getAvgDelta(struct ApHubSync *sync)
@@ -1044,7 +1081,7 @@
     NANOHUB_COMMAND(NANOHUB_REASON_GET_INTERRUPT,
                     getInterrupt,
                     getInterrupt,
-                    0,
+                    struct { },
                     struct NanohubGetInterruptRequest),
     NANOHUB_COMMAND(NANOHUB_REASON_MASK_INTERRUPT,
                     maskInterrupt,
@@ -1080,13 +1117,15 @@
     return NULL;
 }
 
-static void halSendMgmtResponse(uint32_t cmd, uint32_t status)
+#ifdef LEGACY_HAL_ENABLED
+
+static void halSendLegacyMgmtResponse(uint32_t cmd, uint32_t status)
 {
-    struct NanohubHalMgmtTx *resp;
+    struct NanohubHalLegacyMgmtTx *resp;
 
     resp = heapAlloc(sizeof(*resp));
     if (resp) {
-        resp->hdr = (struct NanohubHalHdr) {
+        resp->hdr = (struct NanohubHalLegacyHdr) {
             .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
             .len = sizeof(*resp) - sizeof(resp->hdr) + sizeof(resp->hdr.msg),
             .msg = cmd,
@@ -1096,36 +1135,36 @@
     }
 }
 
-static void halExtAppsOn(void *rx, uint8_t rx_len)
+static void halLegacyExtAppsOn(void *rx, uint8_t rx_len)
 {
-    struct NanohubHalMgmtRx *req = rx;
+    struct NanohubHalLegacyMgmtRx *req = rx;
 
-    halSendMgmtResponse(NANOHUB_HAL_EXT_APPS_ON, osExtAppStartAppsByAppId(le64toh(unaligned_u64(&req->appId))));
+    halSendLegacyMgmtResponse(NANOHUB_HAL_LEGACY_EXT_APPS_ON, osExtAppStartAppsByAppId(le64toh(unaligned_u64(&req->appId))));
 }
 
-static void halExtAppsOff(void *rx, uint8_t rx_len)
+static void halLegacyExtAppsOff(void *rx, uint8_t rx_len)
 {
-    struct NanohubHalMgmtRx *req = rx;
+    struct NanohubHalLegacyMgmtRx *req = rx;
 
-    halSendMgmtResponse(NANOHUB_HAL_EXT_APPS_OFF, osExtAppStopAppsByAppId(le64toh(unaligned_u64(&req->appId))));
+    halSendLegacyMgmtResponse(NANOHUB_HAL_LEGACY_EXT_APPS_OFF, osExtAppStopAppsByAppId(le64toh(unaligned_u64(&req->appId))));
 }
 
-static void halExtAppDelete(void *rx, uint8_t rx_len)
+static void halLegacyExtAppDelete(void *rx, uint8_t rx_len)
 {
-    struct NanohubHalMgmtRx *req = rx;
+    struct NanohubHalLegacyMgmtRx *req = rx;
 
-    halSendMgmtResponse(NANOHUB_HAL_EXT_APP_DELETE, osExtAppEraseAppsByAppId(le64toh(unaligned_u64(&req->appId))));
+    halSendLegacyMgmtResponse(NANOHUB_HAL_LEGACY_EXT_APP_DELETE, osExtAppEraseAppsByAppId(le64toh(unaligned_u64(&req->appId))));
 }
 
-static void halQueryMemInfo(void *rx, uint8_t rx_len)
+static void halLegacyQueryMemInfo(void *rx, uint8_t rx_len)
 {
 }
 
-static void halQueryApps(void *rx, uint8_t rx_len)
+static void halLegacyQueryApps(void *rx, uint8_t rx_len)
 {
-    struct NanohubHalQueryAppsRx *req = rx;
-    struct NanohubHalQueryAppsTx *resp;
-    struct NanohubHalHdr *hdr;
+    struct NanohubHalLegacyQueryAppsRx *req = rx;
+    struct NanohubHalLegacyQueryAppsTx *resp;
+    struct NanohubHalLegacyHdr *hdr;
     uint64_t appId;
     uint32_t appVer, appSize;
 
@@ -1133,8 +1172,8 @@
         resp = heapAlloc(sizeof(*resp));
         if (resp) {
             resp->hdr.appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0);
-            resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1;
-            resp->hdr.msg = NANOHUB_HAL_QUERY_APPS;
+            resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalLegacyHdr) + 1;
+            resp->hdr.msg = NANOHUB_HAL_LEGACY_QUERY_APPS;
             resp->appId = appId;
             resp->version = appVer;
             resp->flashUse = appSize;
@@ -1146,16 +1185,16 @@
         if (hdr) {
             hdr->appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0);
             hdr->len = 1;
-            hdr->msg = NANOHUB_HAL_QUERY_APPS;
+            hdr->msg = NANOHUB_HAL_LEGACY_QUERY_APPS;
             osEnqueueEvtOrFree(EVT_APP_TO_HOST, hdr, heapFree);
         }
     }
 }
 
-static void halQueryRsaKeys(void *rx, uint8_t rx_len)
+static void halLegacyQueryRsaKeys(void *rx, uint8_t rx_len)
 {
-    struct NanohubHalQueryRsaKeysRx *req = rx;
-    struct NanohubHalQueryRsaKeysTx *resp;
+    struct NanohubHalLegacyQueryRsaKeysRx *req = rx;
+    struct NanohubHalLegacyQueryRsaKeysTx *resp;
     int len = 0;
     const uint32_t *ptr;
     uint32_t numKeys;
@@ -1172,75 +1211,77 @@
     }
 
     resp->hdr.appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0);
-    resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1 + len;
-    resp->hdr.msg = NANOHUB_HAL_QUERY_RSA_KEYS;
+    resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalLegacyHdr) + 1 + len;
+    resp->hdr.msg = NANOHUB_HAL_LEGACY_QUERY_RSA_KEYS;
 
     osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
 }
 
-static void halStartUpload(void *rx, uint8_t rx_len)
+static void halLegacyStartUpload(void *rx, uint8_t rx_len)
 {
-    struct NanohubHalStartUploadRx *req = rx;
+    struct NanohubHalLegacyStartUploadRx *req = rx;
     struct NanohubStartFirmwareUploadRequest hwReq = {
-        .size= req->length
+        .size = req->length
     };
-    struct NanohubHalStartUploadTx *resp;
+    struct NanohubHalLegacyStartUploadTx *resp;
 
     if (!(resp = heapAlloc(sizeof(*resp))))
         return;
 
     resp->hdr.appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0);
-    resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1;
-    resp->hdr.msg = NANOHUB_HAL_START_UPLOAD;
-    resp->success = doStartFirmwareUpload(&hwReq);
+    resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalLegacyHdr) + 1;
+    resp->hdr.msg = NANOHUB_HAL_LEGACY_START_UPLOAD;
+    resp->success = doStartFirmwareUpload(&hwReq, true);
 
     osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
 }
 
-static void halContUpload(void *rx, uint8_t rx_len)
+static void halLegacyContUpload(void *rx, uint8_t rx_len)
 {
     uint32_t offset;
     uint32_t reply;
     uint8_t len;
-    struct NanohubHalContUploadRx *req = rx;
-    struct NanohubHalContUploadTx *resp;
+    struct NanohubHalLegacyContUploadRx *req = rx;
+    struct FirmwareWriteCookie *cookie;
 
-    if (!(resp = heapAlloc(sizeof(*resp))))
+    if (!(cookie = heapAlloc(sizeof(*cookie))))
         return;
 
-    resp->hdr.appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0);
-    resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1;
-    resp->hdr.msg = NANOHUB_HAL_CONT_UPLOAD;
+    cookie->evtType = EVT_APP_TO_HOST;
+    cookie->respLegacy.hdr = (struct NanohubHalLegacyHdr) {
+        .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+        .len = sizeof(cookie->respLegacy) - sizeof(struct NanohubHalLegacyHdr) + 1,
+        .msg = NANOHUB_HAL_LEGACY_CONT_UPLOAD,
+    };
+    cookie->respLegacy.success = false;
 
     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);
+        reply = doFirmwareChunk(req->data, offset, len, cookie);
     }
     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);
+        osEnqueueEvtOrFree(EVT_APP_TO_HOST, &cookie->respLegacy, writeCookieFree);
     }
 }
 
-static void halFinishUpload(void *rx, uint8_t rx_len)
+static void halLegacyFinishUpload(void *rx, uint8_t rx_len)
 {
-    struct NanohubHalFinishUploadTx *resp;
+    struct NanohubHalLegacyFinishUploadTx *resp;
     uint32_t reply;
 
     if (!(resp = heapAlloc(sizeof(*resp))))
         return;
 
     resp->hdr.appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0);
-    resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalHdr) + 1;
-    resp->hdr.msg = NANOHUB_HAL_FINISH_UPLOAD;
+    resp->hdr.len = sizeof(*resp) - sizeof(struct NanohubHalLegacyHdr) + 1;
+    resp->hdr.msg = NANOHUB_HAL_LEGACY_FINISH_UPLOAD;
 
-    reply = doFinishFirmwareUpload();
+    reply = doFinishFirmwareUpload(NULL, NULL);
 
     osLog(LOG_INFO, "%s: reply=%" PRIu32 "\n", __func__, reply);
 
@@ -1249,32 +1290,598 @@
     osEnqueueEvtOrFree(EVT_APP_TO_HOST, resp, heapFree);
 }
 
-static void halReboot(void *rx, uint8_t rx_len)
+static void halLegacyReboot(void *rx, uint8_t rx_len)
 {
     BL.blReboot();
 }
 
+const static struct NanohubHalLegacyCommand mBuiltinHalLegacyCommands[] = {
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_EXT_APPS_ON,
+                            halLegacyExtAppsOn),
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_EXT_APPS_OFF,
+                            halLegacyExtAppsOff),
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_EXT_APP_DELETE,
+                            halLegacyExtAppDelete),
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_QUERY_MEMINFO,
+                            halLegacyQueryMemInfo),
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_QUERY_APPS,
+                            halLegacyQueryApps),
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_QUERY_RSA_KEYS,
+                            halLegacyQueryRsaKeys),
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_START_UPLOAD,
+                            halLegacyStartUpload),
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_CONT_UPLOAD,
+                            halLegacyContUpload),
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_FINISH_UPLOAD,
+                            halLegacyFinishUpload),
+    NANOHUB_HAL_LEGACY_COMMAND(NANOHUB_HAL_LEGACY_REBOOT,
+                            halLegacyReboot),
+};
+
+const struct NanohubHalLegacyCommand *nanohubHalLegacyFindCommand(uint8_t msg)
+{
+    uint32_t i;
+
+    for (i = 0; i < ARRAY_SIZE(mBuiltinHalLegacyCommands); i++) {
+        const struct NanohubHalLegacyCommand *cmd = &mBuiltinHalLegacyCommands[i];
+        if (cmd->msg == msg)
+            return cmd;
+    }
+    return NULL;
+}
+
+#endif /* LEGACY_HAL_ENABLED */
+
+static void halSendAppMgmtResponse(struct NanohubHalAppMgmtRx *req, uint32_t status, struct MgmtStatus stat, uint32_t transactionId)
+{
+    struct NanohubHalAppMgmtTx *resp;
+
+    resp = heapAlloc(sizeof(*resp));
+    if (resp) {
+        resp->hdr = (struct NanohubHalHdr) {
+            .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+            .len = sizeof(*resp) - sizeof(resp->hdr),
+            .transactionId = transactionId,
+        };
+        resp->ret = (struct NanohubHalRet) {
+            .msg = NANOHUB_HAL_APP_MGMT,
+            .status = htole32(status),
+        };
+        resp->cmd = req->cmd;
+        resp->stat = stat;
+        osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+    }
+}
+
+static void halAppMgmt(void *rx, uint8_t rx_len, uint32_t transactionId)
+{
+    struct NanohubHalAppMgmtRx *req = rx;
+    struct MgmtStatus stat;
+    uint32_t ret;
+
+    switch (req->cmd) {
+    case NANOHUB_HAL_APP_MGMT_START:
+        stat.value= osExtAppStartAppsByAppId(le64toh(unaligned_u64(&req->appId)));
+        ret = stat.op > 0 ? 0 : -1;
+        break;
+    case NANOHUB_HAL_APP_MGMT_STOP:
+        stat.value = osExtAppStopAppsByAppId(le64toh(unaligned_u64(&req->appId)));
+        ret = stat.op > 0 ? 0 : -1;
+        break;
+    case NANOHUB_HAL_APP_MGMT_UNLOAD:
+        stat.value = osExtAppStopAppsByAppId(le64toh(unaligned_u64(&req->appId)));
+        ret = stat.op > 0 ? 0 : -1;
+        break;
+    case NANOHUB_HAL_APP_MGMT_DELETE:
+        stat.value = osExtAppEraseAppsByAppId(le64toh(unaligned_u64(&req->appId)));
+        ret = stat.erase > 0 ? 0 : -1;
+        break;
+    default:
+        return;
+    }
+
+    halSendAppMgmtResponse(req, ret, stat, transactionId);
+}
+
+static void deferHalSysMgmtErase(void *cookie)
+{
+    struct NanohubHalSysMgmtTx *resp = cookie;
+
+    bool success = osEraseShared();
+
+    if (success)
+        resp->ret.status = htole32(0);
+    else
+        resp->ret.status = htole32(-1);
+
+    osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+}
+
+static void halSysMgmt(void *rx, uint8_t rx_len, uint32_t transactionId)
+{
+    struct NanohubHalSysMgmtRx *req = rx;
+    struct NanohubHalSysMgmtTx *resp;
+    uint32_t ret = 0;
+
+    if (!(resp = heapAlloc(sizeof(*resp))))
+        return;
+
+    resp->hdr = (struct NanohubHalHdr) {
+        .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+        .len = sizeof(*resp) - sizeof(resp->hdr),
+        .transactionId = transactionId,
+    };
+    resp->ret = (struct NanohubHalRet) {
+        .msg = NANOHUB_HAL_SYS_MGMT,
+    };
+    resp->cmd = req->cmd;
+
+    switch (req->cmd) {
+    case NANOHUB_HAL_SYS_MGMT_ERASE:
+        ret = osExtAppStopAppsByAppId(APP_ID_ANY);
+        osLog(LOG_INFO, "%s: unloaded apps, ret=%08lx\n", __func__, ret);
+        // delay to make sure all apps are unloaded before erasing
+        if (osDefer(deferHalSysMgmtErase, resp, false) == false) {
+            resp->ret.status = htole32(-1);
+            osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+        }
+        break;
+    case NANOHUB_HAL_SYS_MGMT_REBOOT:
+        BL.blReboot();
+        break;
+    default:
+        resp->ret.status = htole32(-1);
+        osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+    }
+}
+
+static bool copyTLV64(uint8_t *buf, size_t *offset, size_t max_len, uint8_t tag, uint64_t val)
+{
+    if (*offset + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint64_t) > max_len)
+        return false;
+    buf[(*offset)++] = tag;
+    buf[(*offset)++] = sizeof(uint64_t);
+    memcpy(&buf[*offset], &val, sizeof(uint64_t));
+    *offset += sizeof(uint64_t);
+    return true;
+}
+
+static bool copyTLV32(uint8_t *buf, size_t *offset, size_t max_len, uint8_t tag, uint32_t val)
+{
+    if (*offset + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t) > max_len)
+        return false;
+    buf[(*offset)++] = tag;
+    buf[(*offset)++] = sizeof(uint32_t);
+    memcpy(&buf[*offset], &val, sizeof(uint32_t));
+    *offset += sizeof(uint32_t);
+    return true;
+}
+
+static bool copyTLV8(uint8_t *buf, size_t *offset, size_t max_len, uint8_t tag, uint8_t val)
+{
+    if (*offset + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) > max_len)
+        return false;
+    buf[(*offset)++] = tag;
+    buf[(*offset)++] = sizeof(uint8_t);
+    memcpy(&buf[*offset], &val, sizeof(uint8_t));
+    *offset += sizeof(uint8_t);
+    return true;
+}
+
+static bool copyTLVEmpty(uint8_t *buf, size_t *offset, size_t max_len, uint8_t tag)
+{
+    if (*offset + sizeof(uint8_t) + sizeof(uint8_t) > max_len)
+        return false;
+    buf[(*offset)++] = tag;
+    buf[(*offset)++] = 0;
+    return true;
+}
+
+static int processAppTags(const struct AppHdr *app, uint32_t crc, uint32_t size, uint8_t *data, uint8_t *tags, int cnt, bool req_tid)
+{
+    int i;
+    size_t offset = 0;
+    const size_t max_len = HOST_HUB_CHRE_PACKET_MAX_LEN - sizeof(struct NanohubHalRet);
+    bool success = true;
+    uint32_t tid;
+    bool tid_valid = false;
+    struct Task *task;
+
+    if (app->hdr.magic != APP_HDR_MAGIC ||
+        app->hdr.fwVer != APP_HDR_VER_CUR ||
+        (app->hdr.fwFlags & FL_APP_HDR_APPLICATION) == 0 ||
+        app->hdr.payInfoType != LAYOUT_APP) {
+        return 0;
+    }
+
+    if (osTidById(&app->hdr.appId, &tid)) {
+        tid_valid = true;
+        task = osTaskFindByTid(tid);
+        if (task) {
+            if (task->app != app)
+                tid_valid = false;
+        } else
+            tid_valid = false;
+    }
+
+    if (!tid_valid && req_tid)
+        return 0;
+
+    for (i=0; i<cnt && success; i++) {
+        switch(tags[i]) {
+        case NANOHUB_HAL_APP_INFO_APPID:
+            success = copyTLV64(data, &offset, max_len, tags[i], app->hdr.appId);
+            break;
+        case NANOHUB_HAL_APP_INFO_CRC:
+            if (size)
+                success = copyTLV32(data, &offset, max_len, tags[i], crc);
+            else
+                success = copyTLVEmpty(data, &offset, max_len, tags[i]);
+            break;
+        case NANOHUB_HAL_APP_INFO_TID:
+            if (tid_valid)
+                success = copyTLV32(data, &offset, max_len, tags[i], tid);
+            else
+                success = copyTLVEmpty(data, &offset, max_len, tags[i]);
+            break;
+        case NANOHUB_HAL_APP_INFO_VERSION:
+            success = copyTLV32(data, &offset, max_len, tags[i], app->hdr.appVer);
+            break;
+        case NANOHUB_HAL_APP_INFO_ADDR:
+            success = copyTLV32(data, &offset, max_len, tags[i], (uint32_t)app);
+            break;
+        case NANOHUB_HAL_APP_INFO_SIZE:
+            if (size)
+                success = copyTLV32(data, &offset, max_len, tags[i], size);
+            else
+                success = copyTLVEmpty(data, &offset, max_len, tags[i]);
+            break;
+        case NANOHUB_HAL_APP_INFO_HEAP:
+            if (tid_valid)
+                success = copyTLV32(data, &offset, max_len, tags[i], heapGetTaskSize(tid));
+            else
+                success = copyTLVEmpty(data, &offset, max_len, tags[i]);
+            break;
+        case NANOHUB_HAL_APP_INFO_DATA:
+            success = copyTLV32(data, &offset, max_len, tags[i], app->sect.got_end - app->sect.data_start);
+            break;
+        case NANOHUB_HAL_APP_INFO_BSS:
+            success = copyTLV32(data, &offset, max_len, tags[i], app->sect.bss_end - app->sect.bss_start);
+            break;
+        case NANOHUB_HAL_APP_INFO_CHRE_MAJOR:
+            if (app->hdr.fwFlags & FL_APP_HDR_CHRE)
+                success = copyTLV8(data, &offset, max_len, tags[i],
+                    (app->hdr.chreApiMajor == 0xFF && app->hdr.chreApiMinor == 0xFF) ? 0x01 :
+                        app->hdr.chreApiMajor);
+            else
+                success = copyTLVEmpty(data, &offset, max_len, tags[i]);
+            break;
+        case NANOHUB_HAL_APP_INFO_CHRE_MINOR:
+            if (app->hdr.fwFlags & FL_APP_HDR_CHRE)
+                success = copyTLV8(data, &offset, max_len, tags[i],
+                    (app->hdr.chreApiMajor == 0xFF && app->hdr.chreApiMinor == 0xFF) ? 0x00 :
+                        app->hdr.chreApiMinor);
+            else
+                success = copyTLVEmpty(data, &offset, max_len, tags[i]);
+            break;
+        case NANOHUB_HAL_APP_INFO_END:
+        default:
+            success = false;
+            copyTLVEmpty(data, &offset, max_len, NANOHUB_HAL_APP_INFO_END);
+            break;
+        }
+    }
+
+    return offset;
+}
+
+static void halAppInfo(void *rx, uint8_t rx_len, uint32_t transactionId)
+{
+    struct NanohubHalAppInfoRx *req = rx;
+    struct NanohubHalAppInfoTx *resp;
+    struct SegmentIterator it;
+    uint32_t state;
+    int ret, i;
+    uint32_t sharedSize, numApps;
+    const struct AppHdr *internal;
+    const uint8_t *shared;
+
+    if (!(resp = heapAlloc(sizeof(*resp))))
+        return;
+
+    resp->hdr = (struct NanohubHalHdr) {
+        .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+        .len = sizeof(*resp) - sizeof(resp->hdr) - sizeof(resp->data),
+        .transactionId = transactionId,
+    };
+    resp->ret = (struct NanohubHalRet) {
+        .msg = NANOHUB_HAL_APP_INFO,
+    };
+
+    shared = platGetSharedAreaInfo(&sharedSize);
+    internal = platGetInternalAppList(&numApps);
+
+    if ((le32toh(req->addr) >= (uint32_t)shared && le32toh(req->addr) < (uint32_t)shared + sharedSize) ||
+        (le32toh(req->addr) < (uint32_t)shared &&
+            ((uint32_t)shared < (uint32_t)internal ||
+                (numApps > 0 && le32toh(req->addr) > (uint32_t)(internal+numApps-1))))) {
+        osSegmentIteratorInit(&it);
+        while (osSegmentIteratorNext(&it)) {
+            state = osSegmentGetState(it.seg);
+            switch (state) {
+            case SEG_ST_EMPTY:
+            case SEG_ST_RESERVED:
+                 osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+                 return;
+            case SEG_ST_ERASED:
+            case SEG_ST_VALID:
+                if (le32toh(req->addr) <= (uint32_t)osSegmentGetData(it.seg)) {
+                    ret = processAppTags(osSegmentGetData(it.seg), osSegmentGetCrc(it.seg), osSegmentGetSize(it.seg), resp->data, req->tags, rx_len - 4, state == SEG_ST_ERASED);
+                    if (ret > 0) {
+                        resp->hdr.len += ret;
+                        osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+                        return;
+                    }
+                }
+                break;
+            }
+        }
+    } else {
+        for (i = 0; i < numApps; i++, internal++) {
+            if (le32toh(req->addr) <= (uint32_t)internal) {
+                ret = processAppTags(internal, 0, 0, resp->data, req->tags, rx_len - 4, false);
+                if (ret > 0) {
+                    resp->hdr.len += ret;
+                    osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+                    return;
+                }
+            }
+        }
+    }
+
+    osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+}
+
+static void halSysInfo(void *rx, uint8_t rx_len, uint32_t transactionId)
+{
+    extern uint8_t __code_start[];
+    extern uint8_t __code_end[];
+    extern uint8_t __text_end[];
+    extern uint8_t __ram_start[];
+    extern uint8_t __ram_end[];
+
+    struct NanohubHalSysInfoRx *req = rx;
+    struct NanohubHalSysInfoTx *resp;
+    int i;
+    size_t offset = 0;
+    const size_t max_len = HOST_HUB_CHRE_PACKET_MAX_LEN - sizeof(struct NanohubHalRet);
+    bool success = true;
+    int free, chunks, largest;
+    uint32_t shared_size;
+
+    free = heapGetFreeSize(&chunks, &largest);
+
+    if (!(resp = heapAlloc(sizeof(*resp))))
+        return;
+
+    resp->hdr = (struct NanohubHalHdr) {
+        .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+        .len = sizeof(*resp) - sizeof(resp->hdr) - sizeof(resp->data),
+        .transactionId = transactionId,
+    };
+    resp->ret = (struct NanohubHalRet) {
+        .msg = NANOHUB_HAL_SYS_INFO,
+    };
+
+    for (i=0; i<rx_len && success; i++) {
+        switch(req->tags[i]) {
+        case NANOHUB_HAL_SYS_INFO_HEAP_FREE:
+            if (free >= 0)
+                success = copyTLV32(resp->data, &offset, max_len, req->tags[i], free);
+            else
+                success = copyTLVEmpty(resp->data, &offset, max_len, req->tags[i]);
+            break;
+        case NANOHUB_HAL_SYS_INFO_RAM_SIZE:
+            success = copyTLV32(resp->data, &offset, max_len, req->tags[i], __ram_end - __ram_start);
+            break;
+        case NANOHUB_HAL_SYS_INFO_EEDATA_SIZE:
+            success = copyTLV32(resp->data, &offset, max_len, req->tags[i], eeDataGetSize());
+            break;
+        case NANOHUB_HAL_SYS_INFO_EEDATA_FREE:
+            success = copyTLV32(resp->data, &offset, max_len, req->tags[i], eeDataGetFree());
+            break;
+        case NANOHUB_HAL_SYS_INFO_CODE_SIZE:
+            success = copyTLV32(resp->data, &offset, max_len, req->tags[i], __code_end - __code_start);
+            break;
+        case NANOHUB_HAL_SYS_INFO_CODE_FREE:
+            success = copyTLV32(resp->data, &offset, max_len, req->tags[i], __code_end - __text_end);
+            break;
+        case NANOHUB_HAL_SYS_INFO_SHARED_SIZE:
+            platGetSharedAreaInfo(&shared_size);
+            success = copyTLV32(resp->data, &offset, max_len, req->tags[i], shared_size);
+            break;
+        case NANOHUB_HAL_SYS_INFO_SHARED_FREE:
+            success = copyTLV32(resp->data, &offset, max_len, req->tags[i], osSegmentGetFree());
+            break;
+        case NANOHUB_HAL_SYS_INFO_END:
+        default:
+            success = false;
+            copyTLVEmpty(resp->data, &offset, max_len, NANOHUB_HAL_APP_INFO_END);
+            break;
+        }
+    }
+
+    resp->hdr.len += offset;
+
+    osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+}
+
+static void halKeyInfo(void *rx, uint8_t rx_len, uint32_t transactionId)
+{
+    struct NanohubHalKeyInfoRx *req = rx;
+    struct NanohubHalKeyInfoTx *resp;
+    const uint32_t *ptr;
+    uint32_t numKeys;
+    uint32_t dataLength;
+
+    if (!(resp = heapAlloc(sizeof(*resp))))
+        return;
+
+    ptr = BL.blGetPubKeysInfo(&numKeys);
+
+    resp->hdr = (struct NanohubHalHdr) {
+        .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+        .len = sizeof(*resp) - sizeof(resp->hdr) - sizeof(resp->data),
+        .transactionId = transactionId,
+    };
+    resp->ret = (struct NanohubHalRet) {
+        .msg = NANOHUB_HAL_KEY_INFO,
+    };
+
+    resp->keyLength = 0;
+
+    if (ptr && req->keyNum < numKeys) {
+        if (req->dataOffset < RSA_BYTES) {
+            resp->keyLength = RSA_BYTES;
+            if (RSA_BYTES - req->dataOffset > NANOHUB_RSA_KEY_CHUNK_LEN)
+                dataLength = NANOHUB_RSA_KEY_CHUNK_LEN;
+            else
+                dataLength = RSA_BYTES - req->dataOffset;
+            memcpy(resp->data, (const uint8_t *)ptr + (req->keyNum * RSA_BYTES) + req->dataOffset, dataLength);
+            resp->hdr.len += dataLength;
+        }
+    }
+
+    osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+}
+
+static void halStartUpload(void *rx, uint8_t rx_len, uint32_t transactionId)
+{
+    struct NanohubHalStartUploadRx *req = rx;
+    struct NanohubStartFirmwareUploadRequest hwReq = {
+        .size = req->length
+    };
+    struct NanohubHalStartUploadTx *resp;
+
+    if (!(resp = heapAlloc(sizeof(*resp))))
+        return;
+
+    resp->hdr = (struct NanohubHalHdr) {
+        .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+        .len = sizeof(*resp) - sizeof(resp->hdr),
+        .transactionId = transactionId,
+    };
+
+    resp->ret.msg = NANOHUB_HAL_START_UPLOAD;
+    if (doStartFirmwareUpload(&hwReq, false))
+        resp->ret.status = NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED;
+    else
+        resp->ret.status = NANOHUB_FIRMWARE_CHUNK_REPLY_NO_SPACE;
+
+    osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+}
+
+static void halContUpload(void *rx, uint8_t rx_len, uint32_t transactionId)
+{
+    uint32_t offset;
+    uint32_t reply;
+    uint8_t len;
+    struct NanohubHalContUploadRx *req = rx;
+    struct FirmwareWriteCookie *cookie;
+
+    if (!(cookie = heapAlloc(sizeof(*cookie))))
+        return;
+
+    cookie->evtType = EVT_APP_TO_HOST_CHRE;
+    cookie->resp.hdr = (struct NanohubHalHdr) {
+        .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+        .len = sizeof(cookie->resp) - sizeof(cookie->resp.hdr),
+        .transactionId = transactionId,
+    };
+    cookie->resp.ret = (struct NanohubHalRet) {
+        .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, cookie);
+    }
+    if (reply != NANOHUB_FIRMWARE_CHUNK_REPLY_ACCEPTED) {
+        osLog(LOG_ERROR, "%s: reply=%" PRIu32 "\n", __func__, reply);
+
+        cookie->resp.ret.status = reply;
+
+        osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, &cookie->resp, writeCookieFree);
+    }
+}
+
+static void halFinishUpload(void *rx, uint8_t rx_len, uint32_t transactionId)
+{
+    struct NanohubHalFinishUploadTx *resp;
+    uint32_t reply;
+    uint32_t addr = 0xFFFFFFFF;
+    uint32_t crc = 0xFFFFFFFF;
+
+    if (!(resp = heapAlloc(sizeof(*resp))))
+        return;
+
+    resp->hdr = (struct NanohubHalHdr) {
+        .appId = APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 0),
+        .len = sizeof(*resp) - sizeof(resp->hdr),
+        .transactionId = transactionId,
+    };
+
+    reply = doFinishFirmwareUpload(&addr, &crc);
+
+    osLog(LOG_INFO, "%s: reply=%" PRIu32 "\n", __func__, reply);
+
+    resp->ret = (struct NanohubHalRet) {
+        .msg = NANOHUB_HAL_FINISH_UPLOAD,
+        .status = reply,
+    };
+
+    resp->addr = addr;
+    resp->crc = crc;
+
+    osEnqueueEvtOrFree(EVT_APP_TO_HOST_CHRE, resp, heapFree);
+}
+
 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_APP_MGMT,
+                            halAppMgmt,
+                            struct NanohubHalAppMgmtRx,
+                            struct NanohubHalAppMgmtRx),
+    NANOHUB_HAL_COMMAND(NANOHUB_HAL_SYS_MGMT,
+                            halSysMgmt,
+                            struct NanohubHalSysMgmtRx,
+                            struct NanohubHalSysMgmtRx),
+    NANOHUB_HAL_COMMAND(NANOHUB_HAL_APP_INFO,
+                            halAppInfo,
+                            __le32,
+                            struct NanohubHalAppInfoRx),
+    NANOHUB_HAL_COMMAND(NANOHUB_HAL_SYS_INFO,
+                            halSysInfo,
+                            struct { },
+                            struct NanohubHalSysInfoRx),
+    NANOHUB_HAL_COMMAND(NANOHUB_HAL_KEY_INFO,
+                            halKeyInfo,
+                            struct NanohubHalKeyInfoRx,
+                            struct NanohubHalKeyInfoRx),
     NANOHUB_HAL_COMMAND(NANOHUB_HAL_START_UPLOAD,
-                        halStartUpload),
+                            halStartUpload,
+                            struct NanohubHalStartUploadRx,
+                            struct NanohubHalStartUploadRx),
     NANOHUB_HAL_COMMAND(NANOHUB_HAL_CONT_UPLOAD,
-                        halContUpload),
+                            halContUpload,
+                            __le32,
+                            struct NanohubHalContUploadRx),
     NANOHUB_HAL_COMMAND(NANOHUB_HAL_FINISH_UPLOAD,
-                        halFinishUpload),
-    NANOHUB_HAL_COMMAND(NANOHUB_HAL_REBOOT,
-                        halReboot),
+                            halFinishUpload,
+                            struct { },
+                            struct { }),
 };
 
 const struct NanohubHalCommand *nanohubHalFindCommand(uint8_t msg)
@@ -1289,6 +1896,7 @@
     return NULL;
 }
 
+
 int64_t hostGetTimeDelta(void)
 {
     int64_t delta = getAvgDelta(&mTimeSync);
diff --git a/firmware/os/core/seos.c b/firmware/os/core/seos.c
index 418fe76..9756964 100644
--- a/firmware/os/core/seos.c
+++ b/firmware/os/core/seos.c
@@ -513,6 +513,24 @@
     return (struct Segment *)(start + size);
 }
 
+uint32_t osSegmentGetFree()
+{
+    struct SegmentIterator it;
+    const struct Segment *storageSeg = NULL;
+
+    osSegmentIteratorInit(&it);
+    while (osSegmentIteratorNext(&it)) {
+        if (osSegmentGetState(it.seg) == SEG_ST_EMPTY) {
+            storageSeg = it.seg;
+            break;
+        }
+    }
+    if (!storageSeg || storageSeg > it.sharedEnd)
+        return 0;
+
+    return (uint8_t *)it.sharedEnd - (uint8_t *)storageSeg;
+}
+
 struct Segment *osGetSegment(const struct AppHdr *app)
 {
     uint32_t size;
@@ -614,13 +632,13 @@
     footerLen = (-fullSize) & 3;
     memset(footer, 0x00, footerLen);
 
-#ifdef SEGMENT_CRC_SUPPORT
-    struct SegmentFooter segFooter {
-        .crc = ~crc32(storageSeg, fullSize, ~0),
+    wdtDisableClk();
+    struct SegmentFooter segFooter = {
+        .crc = ~soft_crc32(storageSeg, fullSize, ~0),
     };
+    wdtEnableClk();
     memcpy(&footer[footerLen], &segFooter, sizeof(segFooter));
     footerLen += sizeof(segFooter);
-#endif
 
     if (ret && footerLen)
         ret = osWriteShared((uint8_t*)storageSeg + fullSize, footer, footerLen);
@@ -673,10 +691,10 @@
 
 static bool osExtAppIsValid(const struct AppHdr *app, uint32_t len)
 {
-    //TODO: when CRC support is ready, add CRC check here
     return  osAppIsValid(app) &&
             len >= sizeof(*app) &&
             osAppSegmentGetState(app) == SEG_ST_VALID &&
+            osAppSegmentCalcCrcResidue(app) == CRC_RESIDUE &&
             !(app->hdr.fwFlags & FL_APP_HDR_INTERNAL);
 }
 
@@ -781,20 +799,20 @@
     osStopTask(task, true);
 }
 
-static bool matchDelayStart(const void *cookie, const struct AppHdr *app)
+static bool matchAutoStart(const void *cookie, const struct AppHdr *app)
 {
     bool match = (bool)cookie;
 
     if (app->hdr.fwFlags & FL_APP_HDR_CHRE) {
         if (app->hdr.chreApiMajor == 0xFF && app->hdr.chreApiMinor == 0xFF)
-            return !match;
+            return match;
         else if ((app->hdr.chreApiMajor < 0x01) ||
                  (app->hdr.chreApiMajor == 0x01 && app->hdr.chreApiMinor < 0x01))
-            return !match;
-        else
             return match;
+        else
+            return !match;
     } else {
-        return !match;
+        return match;
     }
 }
 
@@ -964,11 +982,6 @@
     return osExtAppStartApps(matchAppId, &appId);
 }
 
-uint32_t osExtAppStartAppsDelayed()
-{
-    return osExtAppStartApps(matchDelayStart, (void *)true);
-}
-
 static void osStartTasks(void)
 {
     const struct AppHdr *app;
@@ -1019,7 +1032,7 @@
     }
 
     osLog(LOG_DEBUG, "Starting external apps...\n");
-    status = osExtAppStartApps(matchDelayStart, (void *)false);
+    status = osExtAppStartApps(matchAutoStart, (void *)true);
     osLog(LOG_DEBUG, "Started %" PRIu32 " internal apps; EXT status: %08" PRIX32 "\n", taskCnt, status);
 }
 
@@ -1395,7 +1408,7 @@
     return osEnqueuePrivateEvtEx(evtType & EVT_MASK, evtData, taggedPtrMakeFromUint(osGetCurrentTid()), toTid);
 }
 
-bool osTidById(uint64_t *appId, uint32_t *tid)
+bool osTidById(const uint64_t *appId, uint32_t *tid)
 {
     struct Task *task;
 
diff --git a/firmware/os/inc/eeData.h b/firmware/os/inc/eeData.h
index 9b313ee..0a6eca6 100644
--- a/firmware/os/inc/eeData.h
+++ b/firmware/os/inc/eeData.h
@@ -45,6 +45,9 @@
 bool eeDataGet(uint32_t name, void *buf, uint32_t *szP);
 bool eeDataSet(uint32_t name, const void *buf, uint32_t len);
 
+uint32_t eeDataGetSize();
+uint32_t eeDataGetFree();
+
 //allow getting old "versions". Set state to NULL initially, call till you get NULL as return value
 void *eeDataGetAllVersions(uint32_t name, void *buf, uint32_t *szP, void **stateP);
 bool eeDataEraseOldVersion(uint32_t name, void *addr); // addr is non-NULL address returned by call to eeDataGetAllVersions
diff --git a/firmware/os/inc/heap.h b/firmware/os/inc/heap.h
index c5bea59..cb1ccb2 100644
--- a/firmware/os/inc/heap.h
+++ b/firmware/os/inc/heap.h
@@ -24,19 +24,16 @@
 #include <stdint.h>
 #include <stdbool.h>
 
-
-
-
 bool heapInit(void);
 void* heapAlloc(uint32_t sz);
 void heapFree(void* ptr);
 int heapFreeAll(uint32_t tid);
-
+int heapGetFreeSize(int *numChunks, int *largestChunk);
+int heapGetTaskSize(uint32_t tid);
 
 #ifdef __cplusplus
 }
 #endif
 
-
 #endif
 
diff --git a/firmware/os/inc/nanohubCommand.h b/firmware/os/inc/nanohubCommand.h
index 408cffd..e100980 100644
--- a/firmware/os/inc/nanohubCommand.h
+++ b/firmware/os/inc/nanohubCommand.h
@@ -34,11 +34,20 @@
 void nanohubPrefetchTx(uint32_t interrupt, uint32_t wakeup, uint32_t nonwakeup);
 const struct NanohubCommand *nanohubFindCommand(uint32_t packetReason);
 
-struct NanohubHalCommand {
+struct NanohubHalLegacyCommand {
     uint8_t msg;
     void (*handler)(void *, uint8_t);
 };
 
+const struct NanohubHalLegacyCommand *nanohubHalLegacyFindCommand(uint8_t msg);
+
+struct NanohubHalCommand {
+    uint8_t msg;
+    void (*handler)(void *, uint8_t, uint32_t);
+    uint8_t minDataLen;
+    uint8_t maxDataLen;
+};
+
 const struct NanohubHalCommand *nanohubHalFindCommand(uint8_t msg);
 uint64_t hostGetTime(void);
 int64_t hostGetTimeDelta(void);
diff --git a/firmware/os/inc/nanohubPacket.h b/firmware/os/inc/nanohubPacket.h
index ad962ea..df4af66 100644
--- a/firmware/os/inc/nanohubPacket.h
+++ b/firmware/os/inc/nanohubPacket.h
@@ -166,6 +166,7 @@
     NANOHUB_FIRMWARE_CHUNK_REPLY_RESTART,
     NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL,
     NANOHUB_FIRMWARE_CHUNK_REPLY_CANCEL_NO_RETRY,
+    NANOHUB_FIRMWARE_CHUNK_REPLY_NO_SPACE,
 };
 
 SET_PACKED_STRUCT_MODE_ON
@@ -278,18 +279,6 @@
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
-SET_PACKED_STRUCT_MODE_ON
-struct NanohubHalHdr {
-    uint64_t appId;
-    uint8_t len;
-    uint8_t msg;
-} ATTRIBUTE_PACKED;
-SET_PACKED_STRUCT_MODE_OFF
-
-#define NANOHUB_HAL_EXT_APPS_ON     0
-#define NANOHUB_HAL_EXT_APPS_OFF    1
-#define NANOHUB_HAL_EXT_APP_DELETE  2
-
 // this behaves more stable w.r.t. endianness than bit field
 // this is setting byte fields in MgmtStatus response
 // the high-order bit, if set, is indication of counter overflow
@@ -310,32 +299,46 @@
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
+#ifdef LEGACY_HAL_ENABLED
+
 SET_PACKED_STRUCT_MODE_ON
-struct NanohubHalMgmtRx {
+struct NanohubHalLegacyHdr {
+    uint64_t appId;
+    uint8_t len;
+    uint8_t msg;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_LEGACY_EXT_APPS_ON      0
+#define NANOHUB_HAL_LEGACY_EXT_APPS_OFF     1
+#define NANOHUB_HAL_LEGACY_EXT_APP_DELETE   2
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalLegacyMgmtRx {
     __le64 appId;
     struct MgmtStatus stat;
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
 SET_PACKED_STRUCT_MODE_ON
-struct NanohubHalMgmtTx {
-    struct NanohubHalHdr hdr;
+struct NanohubHalLegacyMgmtTx {
+    struct NanohubHalLegacyHdr hdr;
     __le32 status;
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
-#define NANOHUB_HAL_QUERY_MEMINFO   3
-#define NANOHUB_HAL_QUERY_APPS      4
+#define NANOHUB_HAL_LEGACY_QUERY_MEMINFO    3
+#define NANOHUB_HAL_LEGACY_QUERY_APPS       4
 
 SET_PACKED_STRUCT_MODE_ON
-struct NanohubHalQueryAppsRx {
+struct NanohubHalLegacyQueryAppsRx {
     __le32 idx;
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
 SET_PACKED_STRUCT_MODE_ON
-struct NanohubHalQueryAppsTx {
-    struct NanohubHalHdr hdr;
+struct NanohubHalLegacyQueryAppsTx {
+    struct NanohubHalLegacyHdr hdr;
     __le64 appId;
     __le32 version;
     __le32 flashUse;
@@ -343,22 +346,205 @@
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
-#define NANOHUB_HAL_QUERY_RSA_KEYS  5
+#define NANOHUB_HAL_LEGACY_QUERY_RSA_KEYS   5
 
 SET_PACKED_STRUCT_MODE_ON
-struct NanohubHalQueryRsaKeysRx {
+struct NanohubHalLegacyQueryRsaKeysRx {
     __le32 offset;
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
 SET_PACKED_STRUCT_MODE_ON
-struct NanohubHalQueryRsaKeysTx {
-    struct NanohubHalHdr hdr;
+struct NanohubHalLegacyQueryRsaKeysTx {
+    struct NanohubHalLegacyHdr hdr;
     uint8_t data[];
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
-#define NANOHUB_HAL_START_UPLOAD    6
+#define NANOHUB_HAL_LEGACY_START_UPLOAD     6
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalLegacyStartUploadRx {
+    uint8_t isOs;
+    __le32 length;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalLegacyStartUploadTx {
+    struct NanohubHalLegacyHdr hdr;
+    uint8_t success;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_LEGACY_CONT_UPLOAD      7
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalLegacyContUploadRx {
+    __le32 offset;
+    uint8_t data[];
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalLegacyContUploadTx {
+    struct NanohubHalLegacyHdr hdr;
+    uint8_t success;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_LEGACY_FINISH_UPLOAD    8
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalLegacyFinishUploadTx {
+    struct NanohubHalLegacyHdr hdr;
+    uint8_t success;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_LEGACY_REBOOT           9
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalLegacyRebootTx {
+    struct NanohubHalLegacyHdr hdr;
+    __le32 reason;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#endif /* LEGACY_HAL_ENABLED */
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalHdr {
+    __le64 appId;
+    uint8_t len;
+    __le32 transactionId;
+    __le16 unused;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalRet {
+    uint8_t msg;
+    __le32 status;
+} ATTRIBUTE_PACKED;
+
+#define NANOHUB_HAL_APP_MGMT            0x10
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalAppMgmtRx {
+    __le64 appId;
+    uint8_t cmd;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_APP_MGMT_START      0
+#define NANOHUB_HAL_APP_MGMT_STOP       1
+#define NANOHUB_HAL_APP_MGMT_UNLOAD     2
+#define NANOHUB_HAL_APP_MGMT_DELETE     3
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalAppMgmtTx {
+    struct NanohubHalHdr hdr;
+    struct NanohubHalRet ret;
+    uint8_t cmd;
+    struct MgmtStatus stat;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_SYS_MGMT            0x11
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalSysMgmtRx {
+    uint8_t cmd;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_SYS_MGMT_ERASE      0
+#define NANOHUB_HAL_SYS_MGMT_REBOOT     1
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalSysMgmtTx {
+    struct NanohubHalHdr hdr;
+    struct NanohubHalRet ret;
+    uint8_t cmd;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_APP_INFO            0x12
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalAppInfoRx {
+    __le32 addr;
+    uint8_t tags[HOST_HUB_CHRE_PACKET_MAX_LEN - sizeof(__le32)];
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_APP_INFO_APPID          0x00
+#define NANOHUB_HAL_APP_INFO_CRC            0x01
+#define NANOHUB_HAL_APP_INFO_TID            0x02
+#define NANOHUB_HAL_APP_INFO_VERSION        0x03
+#define NANOHUB_HAL_APP_INFO_ADDR           0x04
+#define NANOHUB_HAL_APP_INFO_SIZE           0x05
+#define NANOHUB_HAL_APP_INFO_HEAP           0x06
+#define NANOHUB_HAL_APP_INFO_DATA           0x07
+#define NANOHUB_HAL_APP_INFO_BSS            0x08
+#define NANOHUB_HAL_APP_INFO_CHRE_MAJOR     0x09
+#define NANOHUB_HAL_APP_INFO_CHRE_MINOR     0x0A
+#define NANOHUB_HAL_APP_INFO_END            0xFF
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalAppInfoTx {
+    struct NanohubHalHdr hdr;
+    struct NanohubHalRet ret;
+    uint8_t data[HOST_HUB_CHRE_PACKET_MAX_LEN - sizeof(struct NanohubHalRet)];
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_SYS_INFO            0x13
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalSysInfoRx {
+    uint8_t tags[HOST_HUB_CHRE_PACKET_MAX_LEN];
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_SYS_INFO_HEAP_FREE      0x0F
+#define NANOHUB_HAL_SYS_INFO_RAM_SIZE       0x12
+#define NANOHUB_HAL_SYS_INFO_EEDATA_SIZE    0x13
+#define NANOHUB_HAL_SYS_INFO_EEDATA_FREE    0x14
+#define NANOHUB_HAL_SYS_INFO_CODE_SIZE      0x15
+#define NANOHUB_HAL_SYS_INFO_CODE_FREE      0x16
+#define NANOHUB_HAL_SYS_INFO_SHARED_SIZE    0x17
+#define NANOHUB_HAL_SYS_INFO_SHARED_FREE    0x18
+#define NANOHUB_HAL_SYS_INFO_END            0xFF
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalSysInfoTx {
+    struct NanohubHalHdr hdr;
+    struct NanohubHalRet ret;
+    uint8_t data[HOST_HUB_CHRE_PACKET_MAX_LEN - sizeof(struct NanohubHalRet)];
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_KEY_INFO            0x14
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalKeyInfoRx {
+    uint32_t keyNum;
+    uint32_t dataOffset;
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+SET_PACKED_STRUCT_MODE_ON
+struct NanohubHalKeyInfoTx {
+    struct NanohubHalHdr hdr;
+    struct NanohubHalRet ret;
+    uint32_t keyLength;
+    uint8_t data[NANOHUB_RSA_KEY_CHUNK_LEN];
+} ATTRIBUTE_PACKED;
+SET_PACKED_STRUCT_MODE_OFF
+
+#define NANOHUB_HAL_START_UPLOAD        0x16
 
 SET_PACKED_STRUCT_MODE_ON
 struct NanohubHalStartUploadRx {
@@ -370,41 +556,34 @@
 SET_PACKED_STRUCT_MODE_ON
 struct NanohubHalStartUploadTx {
     struct NanohubHalHdr hdr;
-    uint8_t success;
+    struct NanohubHalRet ret;
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
-#define NANOHUB_HAL_CONT_UPLOAD     7
+#define NANOHUB_HAL_CONT_UPLOAD         0x17
 
 SET_PACKED_STRUCT_MODE_ON
 struct NanohubHalContUploadRx {
     __le32 offset;
-    uint8_t data[];
+    uint8_t data[HOST_HUB_CHRE_PACKET_MAX_LEN-sizeof(__le32)];
 } ATTRIBUTE_PACKED;
-SET_PACKED_STRUCT_MODE_OFF
+SET_PACKED_STRUCT_MODE_ON
 
 SET_PACKED_STRUCT_MODE_ON
 struct NanohubHalContUploadTx {
     struct NanohubHalHdr hdr;
-    uint8_t success;
+    struct NanohubHalRet ret;
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
-#define NANOHUB_HAL_FINISH_UPLOAD   8
+#define NANOHUB_HAL_FINISH_UPLOAD       0x18
 
 SET_PACKED_STRUCT_MODE_ON
 struct NanohubHalFinishUploadTx {
     struct NanohubHalHdr hdr;
-    uint8_t success;
-} ATTRIBUTE_PACKED;
-SET_PACKED_STRUCT_MODE_OFF
-
-#define NANOHUB_HAL_REBOOT          9
-
-SET_PACKED_STRUCT_MODE_ON
-struct NanohubHalRebootTx {
-    struct NanohubHalHdr hdr;
-    __le32 reason;
+    struct NanohubHalRet ret;
+    __le32 addr;
+    __le32 crc;
 } ATTRIBUTE_PACKED;
 SET_PACKED_STRUCT_MODE_OFF
 
diff --git a/firmware/os/inc/seos.h b/firmware/os/inc/seos.h
index c66d897..c8a3d48 100644
--- a/firmware/os/inc/seos.h
+++ b/firmware/os/inc/seos.h
@@ -22,6 +22,7 @@
 #endif
 
 #include <plat/taggedPtr.h>
+#include <plat/wdt.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdarg.h>
@@ -30,12 +31,11 @@
 #include <plat/app.h>
 #include <eventnums.h>
 #include <variant/variant.h>
+#include <crc.h>
 #include "toolchain.h"
 
 #include <nanohub/nanohub.h>
 
-//#define SEGMENT_CRC_SUPPORT
-
 #ifndef MAX_TASKS
 /* Default to 16 tasks, override may come from variant.h */
 #define MAX_TASKS                        16
@@ -177,7 +177,7 @@
 
 bool osDefer(OsDeferCbkF callback, void *cookie, bool urgent);
 
-bool osTidById(uint64_t *appId, uint32_t *tid);
+bool osTidById(const uint64_t *appId, uint32_t *tid);
 bool osAppInfoById(uint64_t appId, uint32_t *appIdx, uint32_t *appVer, uint32_t *appSize);
 bool osAppInfoByIndex(uint32_t appIdx, uint64_t *appId, uint32_t *appVer, uint32_t *appSize);
 bool osExtAppInfoByIndex(uint32_t appIdx, uint64_t *appId, uint32_t *appVer, uint32_t *appSize);
@@ -189,8 +189,9 @@
 bool osAppSegmentSetState(const struct AppHdr *app, uint32_t segState);
 bool osSegmentSetSize(struct Segment *seg, uint32_t size);
 bool osAppWipeData(struct AppHdr *app);
-struct Segment *osGetSegment(const struct AppHdr *app);
 struct Segment *osSegmentGetEnd();
+uint32_t osSegmentGetFree();
+struct Segment *osGetSegment(const struct AppHdr *app);
 
 static inline int32_t osSegmentGetSize(const struct Segment *seg)
 {
@@ -207,17 +208,19 @@
     return (struct AppHdr*)(&seg[1]);
 }
 
-#ifdef SEGMENT_CRC_SUPPORT
-
 struct SegmentFooter
 {
     uint32_t crc;
 };
 
 #define FOOTER_SIZE sizeof(struct SegmentFooter)
-#else
-#define FOOTER_SIZE 0
-#endif
+
+static inline uint32_t osSegmentGetCrc(const struct Segment *seg)
+{
+    struct SegmentFooter *footer = (struct SegmentFooter *)(((uint8_t*)seg) +
+        ((osSegmentGetSize(seg) + 3) & ~3) + sizeof(*seg));
+    return footer ? footer->crc : 0xFFFFFFFF;
+}
 
 static inline uint32_t osSegmentSizeAlignedWithFooter(uint32_t size)
 {
@@ -243,6 +246,24 @@
     return osSegmentGetState(osGetSegment(app));
 }
 
+static inline uint32_t osAppSegmentGetCrc(const struct AppHdr *app)
+{
+    return osSegmentGetCrc(osGetSegment(app));
+}
+
+static inline uint32_t osAppSegmentCalcCrcResidue(const struct AppHdr *app)
+{
+    struct Segment *seg = osGetSegment(app);
+    uint32_t size = osSegmentSizeAlignedWithFooter(osSegmentGetSize(seg));
+    uint32_t crc;
+
+    wdtDisableClk();
+    crc = soft_crc32((uint8_t*)seg, size + sizeof(*seg), ~0);
+    wdtEnableClk();
+
+    return crc;
+}
+
 struct SegmentIterator {
     const struct Segment *shared;
     const struct Segment *sharedEnd;
@@ -272,7 +293,6 @@
 uint32_t osExtAppStopAppsByAppId(uint64_t appId);
 uint32_t osExtAppEraseAppsByAppId(uint64_t appId);
 uint32_t osExtAppStartAppsByAppId(uint64_t appId);
-uint32_t osExtAppStartAppsDelayed();
 
 bool osAppIsChre(uint16_t tid);
 uint32_t osAppChreVersion(uint16_t tid);
@@ -293,15 +313,33 @@
 #define INTERNAL_APP_INIT(_id, _ver, _init, _end, _event)                               \
 SET_INTERNAL_LOCATION(location, ".internal_app_init")static const struct AppHdr         \
 SET_INTERNAL_LOCATION_ATTRIBUTES(used, section (".internal_app_init")) mAppHdr = {      \
-    .hdr.magic   = APP_HDR_MAGIC,                                                       \
-    .hdr.fwVer   = APP_HDR_VER_CUR,                                                     \
-    .hdr.fwFlags = FL_APP_HDR_INTERNAL | FL_APP_HDR_APPLICATION,                        \
-    .hdr.appId   = (_id),                                                               \
-    .hdr.appVer  = (_ver),                                                              \
+    .hdr.magic       = APP_HDR_MAGIC,                                                   \
+    .hdr.fwVer       = APP_HDR_VER_CUR,                                                 \
+    .hdr.fwFlags     = FL_APP_HDR_INTERNAL | FL_APP_HDR_APPLICATION,                    \
+    .hdr.appId       = (_id),                                                           \
+    .hdr.appVer      = (_ver),                                                          \
     .hdr.payInfoType = LAYOUT_APP,                                                      \
-    .vec.init    = (uint32_t)(_init),                                                   \
-    .vec.end     = (uint32_t)(_end),                                                    \
-    .vec.handle  = (uint32_t)(_event)                                                   \
+    .vec.init        = (uint32_t)(_init),                                               \
+    .vec.end         = (uint32_t)(_end),                                                \
+    .vec.handle      = (uint32_t)(_event)                                               \
+}
+#endif
+
+#ifndef INTERNAL_CHRE_APP_INIT
+#define INTERNAL_CHRE_APP_INIT(_id, _ver, _init, _end, _event)                          \
+SET_INTERNAL_LOCATION(location, ".internal_app_init")static const struct AppHdr         \
+SET_INTERNAL_LOCATION_ATTRIBUTES(used, section (".internal_app_init")) mAppHdr = {      \
+    .hdr.magic        = APP_HDR_MAGIC,                                                  \
+    .hdr.fwVer        = APP_HDR_VER_CUR,                                                \
+    .hdr.fwFlags      = FL_APP_HDR_INTERNAL | FL_APP_HDR_APPLICATION | FL_APP_HDR_CHRE, \
+    .hdr.chreApiMajor = 0x01,                                                           \
+    .hdr.chreApiMinor = 0x02,                                                           \
+    .hdr.appId        = (_id),                                                          \
+    .hdr.appVer       = (_ver),                                                         \
+    .hdr.payInfoType  = LAYOUT_APP,                                                     \
+    .vec.init         = (uint32_t)(_init),                                              \
+    .vec.end          = (uint32_t)(_end),                                               \
+    .vec.handle       = (uint32_t)(_event)                                              \
 }
 #endif
 
diff --git a/firmware/os/platform/stm32/eeData.c b/firmware/os/platform/stm32/eeData.c
index 20e859e..2710ba0 100644
--- a/firmware/os/platform/stm32/eeData.c
+++ b/firmware/os/platform/stm32/eeData.c
@@ -90,6 +90,34 @@
     return (uint32_t*)data - 1;
 }
 
+uint32_t eeDataGetSize()
+{
+    return __eedata_end - __eedata_start;
+}
+
+uint32_t eeDataGetFree()
+{
+    uint32_t *p = __eedata_start;
+
+    //find the last incarnation of "name" in flash area
+    while (p < __eedata_end) {
+        uint32_t info = *p;
+        uint32_t name = info & EE_DATA_NAME_MAX;
+        uint32_t sz = info / (EE_DATA_NAME_MAX + 1);
+
+        //check for ending condition (name == max)
+        if (name == EE_DATA_NAME_MAX)
+            break;
+
+        p++;
+
+        //skip over to next data chunk header
+        p += (sz + 3) / 4;
+    }
+
+    return __eedata_end - p;
+}
+
 bool eeDataGet(uint32_t name, void *buf, uint32_t *szP)
 {
     uint32_t offset = 0;
diff --git a/lib/Android.mk b/lib/Android.mk
index 0caa123..88707e1 100644
--- a/lib/Android.mk
+++ b/lib/Android.mk
@@ -20,7 +20,6 @@
     nanohub/aes.c \
     nanohub/rsa.c \
     nanohub/sha2.c \
-    nanohub/softcrc.c \
 
 src_includes := \
     $(LOCAL_PATH)/include \
@@ -36,12 +35,24 @@
 
 include $(BUILD_NANOHUB_BL_STATIC_LIBRARY)
 
+include $(CLEAR_NANO_VARS)
+
+LOCAL_MODULE := libnanohub_common_os
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := nanohub/softcrc.c
+LOCAL_C_INCLUDES := $(src_includes)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(src_includes)
+
+include $(BUILD_NANOHUB_OS_STATIC_LIBRARY)
+
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := libnanohub_common
 LOCAL_MODULE_TAGS := optional
 LOCAL_SRC_FILES := \
     $(src_files) \
+    nanohub/softcrc.c \
     nanohub/nanoapp.c \
 
 LOCAL_CFLAGS := \