1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "NanohubHAL"
18
19#include <cassert>
20#include <cerrno>
21#include <cinttypes>
22
23#include <endian.h>
24
25#include <vector>
26
27#include <utils/Log.h>
28
29#include <endian.h>
30
31#include <hardware/context_hub.h>
32#include "nanohub_perdevice.h"
33#include "system_comms.h"
34#include "nanohubhal.h"
35
36namespace android {
37
38namespace nanohub {
39
40static void readAppName(MessageBuf &buf, hub_app_name_t &name)
41{
42    name.id = buf.readU64();
43}
44
45static void writeAppName(MessageBuf &buf, const hub_app_name_t &name)
46{
47    buf.writeU64(name.id);
48}
49
50static void readNanohubAppInfo(MessageBuf &buf, NanohubAppInfo &info)
51{
52    size_t pos = buf.getPos();
53    readAppName(buf, info.name);
54    info.version = buf.readU32();
55    info.flashUse = buf.readU32();
56    info.ramUse = buf.readU32();
57    if ((buf.getPos() - pos) != sizeof(info)) {
58        ALOGE("%s: failed to read object", __func__);
59    }
60}
61
62static void readNanohubMemInfo(MessageBuf &buf,  NanohubMemInfo &mi)
63{
64    size_t pos = buf.getPos();
65    mi.flashSz = buf.readU32();
66    mi.blSz = buf.readU32();
67    mi.osSz = buf.readU32();
68    mi.sharedSz = buf.readU32();
69    mi.eeSz = buf.readU32();
70    mi.ramSz = buf.readU32();
71
72    mi.blUse = buf.readU32();
73    mi.osUse = buf.readU32();
74    mi.sharedUse = buf.readU32();
75    mi.eeUse = buf.readU32();
76    mi.ramUse = buf.readU32();
77    if ((buf.getPos() - pos) != sizeof(mi)) {
78        ALOGE("%s: failed to read object", __func__);
79    }
80}
81
82NanohubRsp::NanohubRsp(MessageBuf &buf, bool no_status)
83{
84    // all responses start with command
85    // most of them have 4-byte status (result code)
86    buf.reset();
87    cmd = buf.readU8();
88    if (!buf.getSize()) {
89        status = -EINVAL;
90    } else if (no_status) {
91        status = 0;
92    } else {
93        status = buf.readU32();
94    }
95}
96
97int SystemComm::sendToSystem(const void *data, size_t len)
98{
99    if (NanoHub::messageTracingEnabled()) {
100        dumpBuffer("HAL -> SYS", getSystem()->mHostIfAppName, 0, data, len);
101    }
102    return NanoHub::sendToDevice(&getSystem()->mHostIfAppName, data, len);
103}
104
105int SystemComm::AppInfoSession::setup(const hub_message_t *)
106{
107    std::lock_guard<std::mutex> _l(mLock);
108    int suggestedSize = mAppInfo.size() ? mAppInfo.size() : 20;
109
110    mAppInfo.clear();
111    mAppInfo.reserve(suggestedSize);
112    setState(SESSION_USER);
113
114    return requestNext();
115}
116
117inline hub_app_name_t deviceAppNameToHost(const hub_app_name_t src)
118{
119    hub_app_name_t res = { .id = le64toh(src.id) };
120    return res;
121}
122
123inline hub_app_name_t hostAppNameToDevice(const hub_app_name_t src)
124{
125    hub_app_name_t res = { .id = htole64(src.id) };
126    return res;
127}
128
129int SystemComm::AppInfoSession::handleRx(MessageBuf &buf)
130{
131    std::lock_guard<std::mutex> _l(mLock);
132
133    NanohubRsp rsp(buf, true);
134    if (rsp.cmd != NANOHUB_QUERY_APPS) {
135        return 1;
136    }
137    size_t len = buf.getRoom();
138    if (len != sizeof(NanohubAppInfo) && len) {
139        ALOGE("%s: Invalid data size; have %zu, need %zu", __func__,
140              len, sizeof(NanohubAppInfo));
141        return -EINVAL;
142    }
143    if (getState() != SESSION_USER) {
144        ALOGE("%s: Invalid state; have %d, need %d", __func__, getState(), SESSION_USER);
145        return -EINVAL;
146    }
147    if (len) {
148        NanohubAppInfo info;
149        readNanohubAppInfo(buf, info);
150        hub_app_info appInfo;
151        appInfo.num_mem_ranges = 0;
152        if (info.flashUse != NANOHUB_MEM_SZ_UNKNOWN) {
153            mem_range_t &range = appInfo.mem_usage[appInfo.num_mem_ranges++];
154            range.type = HUB_MEM_TYPE_MAIN;
155            range.total_bytes = info.flashUse;
156        }
157        if (info.ramUse != NANOHUB_MEM_SZ_UNKNOWN) {
158            mem_range_t &range = appInfo.mem_usage[appInfo.num_mem_ranges++];
159            range.type = HUB_MEM_TYPE_RAM;
160            range.total_bytes = info.ramUse;
161        }
162
163        appInfo.app_name = info.name;
164        appInfo.version = info.version;
165
166        mAppInfo.push_back(appInfo);
167        return requestNext();
168    } else {
169        sendToApp(CONTEXT_HUB_QUERY_APPS,
170                        static_cast<const void *>(mAppInfo.data()),
171                        mAppInfo.size() * sizeof(mAppInfo[0]));
172        complete();
173    }
174
175    return 0;
176}
177
178int SystemComm::AppInfoSession::requestNext()
179{
180    char data[MAX_RX_PACKET];
181    MessageBuf buf(data, sizeof(data));
182    buf.writeU8(NANOHUB_QUERY_APPS);
183    buf.writeU32(mAppInfo.size());
184    return sendToSystem(buf.getData(), buf.getPos());
185}
186
187int SystemComm::MemInfoSession::setup(const hub_message_t *)
188{
189    std::lock_guard<std::mutex> _l(mLock);
190    char data[MAX_RX_PACKET];
191    MessageBuf buf(data, sizeof(data));
192    buf.writeU8(NANOHUB_QUERY_MEMINFO);
193
194    setState(SESSION_USER);
195    return sendToSystem(buf.getData(), buf.getPos());
196}
197
198int SystemComm::MemInfoSession::handleRx(MessageBuf &buf)
199{
200    std::lock_guard<std::mutex> _l(mLock);
201    NanohubRsp rsp(buf, true);
202
203    if (rsp.cmd != NANOHUB_QUERY_MEMINFO)
204        return 1;
205
206    size_t len = buf.getRoom();
207
208    if (len != sizeof(NanohubMemInfo)) {
209        ALOGE("%s: Invalid data size: %zu", __func__, len);
210        return -EINVAL;
211    }
212    if (getState() != SESSION_USER) {
213        ALOGE("%s: Invalid state; have %d, need %d", __func__, getState(), SESSION_USER);
214        return -EINVAL;
215    }
216
217    NanohubMemInfo mi;
218    readNanohubMemInfo(buf, mi);
219    std::vector<mem_range_t> ranges;
220    ranges.reserve(4);
221
222    //if each is valid, copy to output area
223    if (mi.sharedSz != NANOHUB_MEM_SZ_UNKNOWN &&
224        mi.sharedUse != NANOHUB_MEM_SZ_UNKNOWN)
225        ranges.push_back({
226            .type = HUB_MEM_TYPE_MAIN,
227            .total_bytes = mi.sharedSz,
228            .free_bytes = mi.sharedSz - mi.sharedUse,
229        });
230
231    if (mi.osSz != NANOHUB_MEM_SZ_UNKNOWN &&
232        mi.osUse != NANOHUB_MEM_SZ_UNKNOWN)
233        ranges.push_back({
234            .type = HUB_MEM_TYPE_OS,
235            .total_bytes = mi.osSz,
236            .free_bytes = mi.osSz - mi.osUse,
237        });
238
239    if (mi.eeSz != NANOHUB_MEM_SZ_UNKNOWN &&
240        mi.eeUse != NANOHUB_MEM_SZ_UNKNOWN)
241        ranges.push_back({
242            .type = HUB_MEM_TYPE_EEDATA,
243            .total_bytes = mi.eeSz,
244            .free_bytes = mi.eeSz - mi.eeUse,
245        });
246
247    if (mi.ramSz != NANOHUB_MEM_SZ_UNKNOWN &&
248        mi.ramUse != NANOHUB_MEM_SZ_UNKNOWN)
249        ranges.push_back({
250            .type = HUB_MEM_TYPE_RAM,
251            .total_bytes = mi.ramSz,
252            .free_bytes = mi.ramSz - mi.ramUse,
253        });
254
255    //send it out
256    sendToApp(CONTEXT_HUB_QUERY_MEMORY,
257              static_cast<const void *>(ranges.data()),
258              ranges.size() * sizeof(ranges[0]));
259
260    complete();
261
262    return 0;
263}
264
265int SystemComm::AppMgmtSession::setup(const hub_message_t *appMsg)
266{
267    std::lock_guard<std::mutex> _l(mLock);
268
269    char data[MAX_RX_PACKET];
270    MessageBuf buf(data, sizeof(data));
271    const uint8_t *msgData = static_cast<const uint8_t*>(appMsg->message);
272
273    mCmd = appMsg->message_type;
274    mLen = appMsg->message_len;
275    mPos = 0;
276
277    switch (mCmd) {
278    case  CONTEXT_HUB_APPS_ENABLE:
279        return setupMgmt(appMsg, NANOHUB_EXT_APPS_ON);
280    case  CONTEXT_HUB_APPS_DISABLE:
281        return setupMgmt(appMsg, NANOHUB_EXT_APPS_OFF);
282    case  CONTEXT_HUB_UNLOAD_APP:
283        return setupMgmt(appMsg, NANOHUB_EXT_APP_DELETE);
284    case  CONTEXT_HUB_LOAD_APP:
285    {
286        mData.clear();
287        mData = std::vector<uint8_t>(msgData, msgData + mLen);
288        const load_app_request_t *appReq = static_cast<const load_app_request_t*>(appMsg->message);
289        if (appReq == nullptr || mLen <= sizeof(*appReq)) {
290            ALOGE("%s: Invalid app header: too short\n", __func__);
291            return -EINVAL;
292        }
293        mAppName = appReq->app_binary.app_id;
294        setState(TRANSFER);
295
296        buf.writeU8(NANOHUB_START_UPLOAD);
297        buf.writeU8(0);
298        buf.writeU32(mLen);
299        return sendToSystem(buf.getData(), buf.getPos());
300    }
301
302    case  CONTEXT_HUB_OS_REBOOT:
303        setState(REBOOT);
304        buf.writeU8(NANOHUB_REBOOT);
305        return sendToSystem(buf.getData(), buf.getPos());
306    }
307
308    return -EINVAL;
309}
310
311int SystemComm::AppMgmtSession::setupMgmt(const hub_message_t *appMsg, uint32_t cmd)
312{
313    const hub_app_name_t &appName = *static_cast<const hub_app_name_t*>(appMsg->message);
314    if (appMsg->message_len != sizeof(appName)) {
315        return -EINVAL;
316    }
317
318    char data[MAX_RX_PACKET];
319    MessageBuf buf(data, sizeof(data));
320    buf.writeU8(cmd);
321    writeAppName(buf, appName);
322    setState(MGMT);
323
324    return sendToSystem(buf.getData(), buf.getPos());
325}
326
327int SystemComm::AppMgmtSession::handleRx(MessageBuf &buf)
328{
329    int ret = 0;
330    std::lock_guard<std::mutex> _l(mLock);
331    NanohubRsp rsp(buf);
332
333    switch (getState()) {
334    case TRANSFER:
335        ret = handleTransfer(rsp);
336        break;
337    case FINISH:
338        ret = handleFinish(rsp);
339        break;
340    case RUN:
341        ret = handleRun(rsp);
342        break;
343    case RUN_FAILED:
344        ret = handleRunFailed(rsp);
345        break;
346    case REBOOT:
347        ret = handleReboot(rsp);
348        break;
349    case MGMT:
350        ret = handleMgmt(rsp);
351        break;
352    }
353
354    return ret;
355}
356
357int SystemComm::AppMgmtSession::handleTransfer(NanohubRsp &rsp)
358{
359    if (rsp.cmd != NANOHUB_CONT_UPLOAD && rsp.cmd != NANOHUB_START_UPLOAD)
360        return 1;
361
362    char data[MAX_RX_PACKET];
363    MessageBuf buf(data, sizeof(data));
364
365    static_assert(NANOHUB_UPLOAD_CHUNK_SZ_MAX <= (MAX_RX_PACKET-5),
366                  "Invalid chunk size");
367
368    if (mPos < mLen) {
369        uint32_t chunkSize = mLen - mPos;
370
371        if (chunkSize > NANOHUB_UPLOAD_CHUNK_SZ_MAX) {
372            chunkSize = NANOHUB_UPLOAD_CHUNK_SZ_MAX;
373        }
374
375        buf.writeU8(NANOHUB_CONT_UPLOAD);
376        buf.writeU32(mPos);
377        buf.writeRaw(&mData[mPos], chunkSize);
378        mPos += chunkSize;
379    } else {
380        buf.writeU8(NANOHUB_FINISH_UPLOAD);
381        setState(FINISH);
382    }
383
384    return sendToSystem(buf.getData(), buf.getPos());
385}
386
387int SystemComm::AppMgmtSession::handleFinish(NanohubRsp &rsp)
388{
389    if (rsp.cmd != NANOHUB_FINISH_UPLOAD)
390        return 1;
391
392    int ret = 0;
393    const bool success = rsp.status != 0;
394    mData.clear();
395
396    if (success) {
397        char data[MAX_RX_PACKET];
398        MessageBuf buf(data, sizeof(data));
399        buf.writeU8(NANOHUB_EXT_APPS_ON);
400        writeAppName(buf, mAppName);
401        setState(RUN);
402        ret = sendToSystem(buf.getData(), buf.getPos());
403    } else {
404        int32_t result = NANOHUB_APP_NOT_LOADED;
405
406        sendToApp(mCmd, &result, sizeof(result));
407        complete();
408    }
409
410    return ret;
411}
412
413int SystemComm::AppMgmtSession::handleRun(NanohubRsp &rsp)
414{
415    if (rsp.cmd != NANOHUB_EXT_APPS_ON)
416        return 1;
417
418    MgmtStatus sts = { .value = (uint32_t)rsp.status };
419
420    // op counter returns number of nanoapps that were started as result of the command
421    // for successful start command it must be > 0
422    int32_t result = sts.value > 0 && sts.op > 0 && sts.op <= 0x7F ? 0 : -1;
423
424    ALOGI("Nanohub NEW APP START: %08" PRIX32 "\n", rsp.status);
425    if (result != 0) {
426        // if nanoapp failed to start we have to unload it
427        char data[MAX_RX_PACKET];
428        MessageBuf buf(data, sizeof(data));
429        buf.writeU8(NANOHUB_EXT_APP_DELETE);
430        writeAppName(buf, mAppName);
431        if (sendToSystem(buf.getData(), buf.getPos()) == 0) {
432            setState(RUN_FAILED);
433            return 0;
434        }
435        ALOGE("%s: failed to send DELETE for failed app\n", __func__);
436    }
437
438    // it is either success, and we report it, or
439    // it is a failure to load, and also failure to send erase command
440    sendToApp(mCmd, &result, sizeof(result));
441    complete();
442    return 0;
443}
444
445int SystemComm::AppMgmtSession::handleRunFailed(NanohubRsp &rsp)
446{
447    if (rsp.cmd != NANOHUB_EXT_APP_DELETE)
448        return 1;
449
450    int32_t result = -1;
451
452    ALOGI("%s: APP DELETE [because it failed]: %08" PRIX32 "\n", __func__, rsp.status);
453
454    sendToApp(mCmd, &result, sizeof(result));
455    complete();
456
457    return 0;
458}
459
460/* reboot notification, when triggered by App request */
461int SystemComm::AppMgmtSession::handleReboot(NanohubRsp &rsp)
462{
463    if (rsp.cmd != NANOHUB_REBOOT)
464        return 1;
465    ALOGI("Nanohub reboot status [USER REQ]: %08" PRIX32 "\n", rsp.status);
466
467    // reboot notification is sent by SessionManager
468    complete();
469
470    return 0;
471}
472
473int SystemComm::AppMgmtSession::handleMgmt(NanohubRsp &rsp)
474{
475    bool valid = false;
476
477    int32_t result = rsp.status;
478
479    // TODO: remove this when context hub service can handle non-zero success status
480    if (result > 0) {
481        // something happened; assume it worked
482        result = 0;
483    } else if (result == 0) {
484        // nothing happened; this is provably an error
485        result = -1;
486    }
487
488    switch (rsp.cmd) {
489    case NANOHUB_EXT_APPS_OFF:
490        valid = mCmd == CONTEXT_HUB_APPS_DISABLE;
491        break;
492    case NANOHUB_EXT_APPS_ON:
493        valid = mCmd == CONTEXT_HUB_APPS_ENABLE;
494        break;
495    case NANOHUB_EXT_APP_DELETE:
496        valid = mCmd == CONTEXT_HUB_UNLOAD_APP;
497        break;
498    default:
499        return 1;
500    }
501
502    ALOGI("Nanohub MGMT response: CMD=%02X; STATUS=%08" PRIX32, rsp.cmd, rsp.status);
503    if (!valid) {
504        ALOGE("Invalid response for this state: APP CMD=%02X", mCmd);
505        return -EINVAL;
506    }
507
508    sendToApp(mCmd, &result, sizeof(result));
509    complete();
510
511    return 0;
512}
513
514int SystemComm::KeyInfoSession::setup(const hub_message_t *) {
515    std::lock_guard<std::mutex> _l(mLock);
516    mRsaKeyData.clear();
517    setState(SESSION_USER);
518    mStatus = -EBUSY;
519    return requestRsaKeys();
520}
521
522int SystemComm::KeyInfoSession::handleRx(MessageBuf &buf)
523{
524    std::lock_guard<std::mutex> _l(mLock);
525    NanohubRsp rsp(buf, true);
526
527    if (getState() != SESSION_USER) {
528        // invalid state
529        mStatus = -EFAULT;
530        return mStatus;
531    }
532
533    if (buf.getRoom()) {
534        mRsaKeyData.insert(mRsaKeyData.end(),
535                           buf.getData() + buf.getPos(),
536                           buf.getData() + buf.getSize());
537        return requestRsaKeys();
538    } else {
539        mStatus = 0;
540        complete();
541        return 0;
542    }
543}
544
545int SystemComm::KeyInfoSession::requestRsaKeys(void)
546{
547    char data[MAX_RX_PACKET];
548    MessageBuf buf(data, sizeof(data));
549
550    buf.writeU8(NANOHUB_QUERY_APPS);
551    buf.writeU32(mRsaKeyData.size());
552
553    return sendToSystem(buf.getData(), buf.getPos());
554}
555
556int SystemComm::doHandleRx(const nano_message *msg)
557{
558    //we only care for messages from HostIF
559    if (msg->hdr.appId != mHostIfAppName.id)
560        return 1;
561
562    //they must all be at least 1 byte long
563    if (!msg->hdr.len) {
564        return -EINVAL;
565    }
566    MessageBuf buf(reinterpret_cast<const char*>(msg->data), msg->hdr.len);
567    if (NanoHub::messageTracingEnabled()) {
568        dumpBuffer("SYS -> HAL", mHostIfAppName, 0, buf.getData(), buf.getSize());
569    }
570    int status = mSessions.handleRx(buf);
571    if (status) {
572        // provide default handler for any system message, that is not properly handled
573        dumpBuffer(status > 0 ? "HAL (not handled)" : "HAL (error)",
574                   mHostIfAppName, 0, buf.getData(), buf.getSize(), status);
575        status = status > 0 ? 0 : status;
576    }
577
578    return status;
579}
580
581int SystemComm::SessionManager::handleRx(MessageBuf &buf)
582{
583    int status = 1;
584    std::unique_lock<std::mutex> lk(lock);
585
586    // pass message to all active sessions, in arbitrary order
587    // 1st session that handles the message terminates the loop
588    for (auto pos = sessions_.begin(); pos != sessions_.end() && status > 0; next(pos)) {
589        if (!isActive(pos)) {
590            continue;
591        }
592        Session *session = pos->second;
593        status = session->handleRx(buf);
594        if (status < 0) {
595            session->complete();
596        }
597    }
598
599    NanohubRsp rsp(buf);
600    if (rsp.cmd == NANOHUB_REBOOT) {
601        // if this is reboot notification, kill all sessions
602        for (auto pos = sessions_.begin(); pos != sessions_.end(); next(pos)) {
603            if (!isActive(pos)) {
604                continue;
605            }
606            Session *session = pos->second;
607            session->abort(-EINTR);
608        }
609        lk.unlock();
610        // log the reboot event, if not handled
611        if (status > 0) {
612            ALOGW("Nanohub reboot status [UNSOLICITED]: %08" PRIX32, rsp.status);
613            status = 0;
614        }
615        // report to java apps
616        sendToApp(CONTEXT_HUB_OS_REBOOT, &rsp.status, sizeof(rsp.status));
617    }
618
619    return status;
620}
621
622int SystemComm::SessionManager::setup_and_add(int id, Session *session, const hub_message_t *appMsg)
623{
624    std::lock_guard<std::mutex> _l(lock);
625
626    // scan sessions to release those that are already done
627    for (auto pos = sessions_.begin(); pos != sessions_.end(); next(pos)) {
628        continue;
629    }
630
631    if (sessions_.count(id) == 0 && !session->isRunning()) {
632        sessions_[id] = session;
633        int ret = session->setup(appMsg);
634        if (ret < 0) {
635            session->complete();
636        }
637        return ret;
638    }
639    return -EBUSY;
640}
641
642int SystemComm::doHandleTx(const hub_message_t *appMsg)
643{
644    int status = 0;
645
646    switch (appMsg->message_type) {
647    case CONTEXT_HUB_LOAD_APP:
648        if (!mKeySession.haveKeys()) {
649            status = mSessions.setup_and_add(CONTEXT_HUB_LOAD_APP, &mKeySession, appMsg);
650            if (status < 0) {
651                break;
652            }
653            mKeySession.wait();
654            status = mKeySession.getStatus();
655            if (status < 0) {
656                break;
657            }
658        }
659        status = mSessions.setup_and_add(CONTEXT_HUB_LOAD_APP, &mAppMgmtSession, appMsg);
660        break;
661    case CONTEXT_HUB_APPS_ENABLE:
662    case CONTEXT_HUB_APPS_DISABLE:
663    case CONTEXT_HUB_UNLOAD_APP:
664        // all APP-modifying commands share session key, to ensure they can't happen at the same time
665        status = mSessions.setup_and_add(CONTEXT_HUB_LOAD_APP, &mAppMgmtSession, appMsg);
666        break;
667
668    case CONTEXT_HUB_QUERY_APPS:
669        status = mSessions.setup_and_add(CONTEXT_HUB_QUERY_APPS, &mAppInfoSession, appMsg);
670        break;
671
672    case CONTEXT_HUB_QUERY_MEMORY:
673        status = mSessions.setup_and_add(CONTEXT_HUB_QUERY_MEMORY, &mMemInfoSession, appMsg);
674        break;
675
676    default:
677        ALOGW("Unknown os message type %u\n", appMsg->message_type);
678        return -EINVAL;
679    }
680
681   return status;
682}
683
684}; // namespace nanohub
685
686}; // namespace android
687