[confirmationui] NS IPC using shared memory

This simplifies the interace and removes packetization logic.

Also, refactor event loop to use lib/tipc.

Bug: 148421469
Test: VtsHalConfirmationUIV1_0TargetTest
Change-Id: I33463acc7f280bf46b76e78fcbfeb8947a57c811
diff --git a/src/ipc.h b/src/ipc.h
new file mode 100644
index 0000000..eb764bc
--- /dev/null
+++ b/src/ipc.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+/*
+ * This interface is shared between Android and Trusty. There is a copy in each
+ * repository. They must be kept in sync.
+ */
+
+#define CONFIRMATIONUI_PORT "com.android.trusty.confirmationui"
+
+/**
+ * enum confirmationui_cmd - command identifiers for ConfirmationUI interface
+ * @CONFIRMATIONUI_RESP_BIT:  response bit set as part of response
+ * @CONFIRMATIONUI_REQ_SHIFT: number of bits used by response bit
+ * @CONFIRMATIONUI_CMD_INIT:  command to initialize session
+ * @CONFIRMATIONUI_CMD_MSG:   command to send ConfirmationUI messages
+ */
+enum confirmationui_cmd : uint32_t {
+    CONFIRMATIONUI_RESP_BIT = 1,
+    CONFIRMATIONUI_REQ_SHIFT = 1,
+
+    CONFIRMATIONUI_CMD_INIT = (1 << CONFIRMATIONUI_REQ_SHIFT),
+    CONFIRMATIONUI_CMD_MSG = (2 << CONFIRMATIONUI_REQ_SHIFT),
+};
+
+/**
+ * struct confirmationui_hdr - header for ConfirmationUI messages
+ * @cmd: command identifier
+ *
+ * Note that no messages return a status code. Any error on the server side
+ * results in the connection being closed. So, operations can be assumed to be
+ * successful if they return a response.
+ */
+struct confirmationui_hdr {
+    uint32_t cmd;
+};
+
+/**
+ * struct confirmationui_init_req - arguments for request to initialize a
+ *                                  session
+ * @shm_len: length of memory region being shared
+ *
+ * A handle to a memory region must be sent along with this message. This memory
+ * is send to ConfirmationUI messages.
+ */
+struct confirmationui_init_req {
+    uint32_t shm_len;
+};
+
+/**
+ * struct confirmationui_msg_args - arguments for sending a message
+ * @msg_len: length of message being sent
+ *
+ * Contents of the message are located in the shared memory region that is
+ * established using %CONFIRMATIONUI_CMD_INIT.
+ *
+ * ConfirmationUI messages can travel both ways.
+ */
+struct confirmationui_msg_args {
+    uint32_t msg_len;
+};
+
+#define CONFIRMATIONUI_MAX_MSG_SIZE 0x2000
diff --git a/src/main.cpp b/src/main.cpp
index 5079509..f9e8653 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -16,76 +16,28 @@
 
 #define TLOG_TAG "confirmationui"
 
+#include <lib/keymaster/keymaster.h>
 #include <lib/tipc/tipc.h>
+#include <lib/tipc/tipc_srv.h>
 #include <lk/err_ptr.h>
 #include <lk/macros.h>
-#include <stdlib.h>
-#include <trusty_ipc.h>
+#include <sys/mman.h>
 #include <trusty_log.h>
 #include <uapi/err.h>
 
-#include <algorithm>
+#include <memory>
 
+#include "ipc.h"
 #include "trusty_operation.h"
 
-#include <lib/keymaster/keymaster.h>
-
-#define CONFIRMATIONUI_PORT_NAME "com.android.trusty.confirmationui"
-
-/*
- * Must be kept in sync with HAL service (see TrustyApp.cpp)
- */
-static constexpr size_t kPacketSize = 0x1000 - 32;
-
-/*
- * Must be kept in sync with HAL service (see TrustyApp.cpp)
- */
-enum class PacketType : uint32_t {
-    SND,
-    RCV,
-    ACK,
+struct chan_ctx {
+    void* shm_base;
+    size_t shm_len;
+    std::unique_ptr<TrustyOperation> op;
 };
 
-static const char* toString(PacketType t) {
-    switch (t) {
-    case PacketType::SND:
-        return "SND";
-    case PacketType::RCV:
-        return "RCV";
-    case PacketType::ACK:
-        return "ACK";
-    default:
-        return "UNKNOWN";
-    }
-}
-
-/*
- * Must be kept in sync with HAL service (see TrustyApp.cpp)
- */
-struct PacketHeader {
-    PacketType type;
-    uint32_t remaining;
-};
-
-static constexpr const size_t kMessageSize = 0x2000;  // 8K
-
-enum class IpcState {
-    SENDING,
-    RECEIVING,
-    DESYNC,
-};
-
-const char* toString(IpcState s) {
-    switch (s) {
-    case IpcState::SENDING:
-        return "SENDING";
-    case IpcState::RECEIVING:
-        return "RECEIVING";
-    case IpcState::DESYNC:
-        return "DESYNC";
-    default:
-        return "Unknown";
-    }
+static inline bool is_inited(struct chan_ctx* ctx) {
+    return ctx->shm_base;
 }
 
 static bool get_auth_token_key(teeui::AuthTokenKey& authKey) {
@@ -113,188 +65,256 @@
     return true;
 }
 
-static void port_handler(const struct uevent* event, void* priv) {
+struct __attribute__((__packed__)) confirmationui_req {
+    struct confirmationui_hdr hdr;
+    union {
+        struct confirmationui_init_req init_args;
+        struct confirmationui_msg_args msg_args;
+    };
+};
+
+static int confirmationui_recv(handle_t chan,
+                               confirmationui_req* req,
+                               handle_t* h) {
     int rc;
-    struct uuid peer_uuid;
-    handle_t channel;
+    ipc_msg_info msg_info;
+    uint32_t max_num_handles = h ? 1 : 0;
+    struct iovec iov = {
+            .iov_base = req,
+            .iov_len = sizeof(*req),
+    };
+    struct ipc_msg ipc_msg = {
+            .num_iov = 1,
+            .iov = &iov,
+            .num_handles = max_num_handles,
+            .handles = h,
+    };
 
-    TLOGD("Entering port handler %u\n", event->event);
+    rc = get_msg(chan, &msg_info);
+    if (rc != NO_ERROR) {
+        TLOGE("Failed to get message (%d)\n", rc);
+        return rc;
+    }
 
-    tipc_handle_port_errors(event);
+    if (msg_info.len > sizeof(*req)) {
+        TLOGE("Message is too long (%zd)\n", msg_info.len);
+        rc = ERR_BAD_LEN;
+        goto out;
+    }
 
-    if (event->event & IPC_HANDLE_POLL_READY) {
-        rc = accept(event->handle, &peer_uuid);
-        if (rc < 0) {
-            TLOGE("%s: failed (%d) to accept on port\n", __func__, rc);
-            return;
+    if (msg_info.num_handles > max_num_handles) {
+        TLOGE("Message has too many handles (%u)\n", msg_info.num_handles);
+        rc = ERR_TOO_BIG;
+        goto out;
+    }
+
+    rc = read_msg(chan, msg_info.id, 0, &ipc_msg);
+
+out:
+    put_msg(chan, msg_info.id);
+    return rc;
+}
+
+static int handle_init(handle_t chan,
+                       handle_t shm_handle,
+                       uint32_t shm_len,
+                       struct chan_ctx* ctx) {
+    int rc;
+    struct confirmationui_hdr hdr;
+
+    if (is_inited(ctx)) {
+        TLOGE("TA is already initialized.\n");
+        return ERR_BAD_STATE;
+    }
+
+    if (shm_len > CONFIRMATIONUI_MAX_MSG_SIZE) {
+        TLOGE("Shared memory too long\n");
+        return ERR_BAD_LEN;
+    }
+
+    void* shm_base = mmap(0, shm_len, PROT_READ | PROT_WRITE, 0, shm_handle, 0);
+    if (shm_base == MAP_FAILED) {
+        TLOGE("Failed to mmap() handle\n");
+        return ERR_BAD_HANDLE;
+    }
+
+    hdr.cmd = CONFIRMATIONUI_CMD_INIT | CONFIRMATIONUI_RESP_BIT;
+    rc = tipc_send1(chan, &hdr, sizeof(hdr));
+    if (rc != (int)sizeof(hdr)) {
+        TLOGE("Failed to send response (%d)\n", rc);
+        if (rc >= 0) {
+            rc = ERR_BAD_LEN;
         }
-        TLOGD("Accepted connection\n");
-        channel = (handle_t)rc;
+        goto err;
+    }
 
-        uint8_t message_buffer[kMessageSize];
-        uint8_t* const aligned_message =
-                (uint8_t*)round_up((uintptr_t)&message_buffer[0], 8);
-        const size_t aligned_message_size =
-                kMessageSize - (aligned_message - &message_buffer[0]);
-        uint32_t mpos = 0;
-        uint32_t msize = aligned_message_size;
-        TrustyOperation op;
+    ctx->shm_base = shm_base;
+    ctx->shm_len = shm_len;
+    return NO_ERROR;
+
+err:
+    munmap(shm_base, shm_len);
+    return rc;
+}
+
+static int handle_msg(handle_t chan, uint32_t req_len, struct chan_ctx* ctx) {
+    int rc;
+    uint8_t msg[CONFIRMATIONUI_MAX_MSG_SIZE];
+    uint32_t resp_len = sizeof(msg);
+    struct confirmationui_hdr hdr;
+    struct confirmationui_msg_args args;
+
+    if (!is_inited(ctx)) {
+        TLOGE("TA is not initialized.\n");
+        return ERR_BAD_STATE;
+    }
+
+    if (req_len > ctx->shm_len) {
+        TLOGE("Message too long (%u)\n", req_len);
+        return ERR_BAD_LEN;
+    }
+
+    assert(req_len <= sizeof(msg));
+    memcpy(msg, ctx->shm_base, req_len);
+
+    ctx->op->handleMsg(msg, req_len, ctx->shm_base, &resp_len);
+
+    hdr.cmd = CONFIRMATIONUI_CMD_MSG | CONFIRMATIONUI_RESP_BIT;
+    args.msg_len = resp_len;
+    rc = tipc_send2(chan, &hdr, sizeof(hdr), &args, sizeof(args));
+    if (rc != (int)(sizeof(hdr) + sizeof(args))) {
+        TLOGE("Failed to send response (%d)\n", rc);
+        if (rc >= 0) {
+            rc = ERR_BAD_LEN;
+        }
+        return rc;
+    }
+
+    return NO_ERROR;
+}
+
+static int on_connect(const struct tipc_port* port,
+                      handle_t chan,
+                      const struct uuid* peer,
+                      void** ctx_p) {
+    auto op = std::make_unique<TrustyOperation>();
+    if (!op) {
+        TLOGE("Failed to allocate TrustyOperation\n");
+        return ERR_NO_MEMORY;
+    }
+
+    struct chan_ctx* ctx = (struct chan_ctx*)calloc(1, sizeof(*ctx));
+    if (!ctx) {
+        TLOGE("Failed to allocate channel context\n");
+        return ERR_NO_MEMORY;
+    }
 
 #if defined(PLATFORM_GENERIC_ARM64)
-        /* Use the test key for emulator */
-        constexpr const auto kTestKey = teeui::AuthTokenKey::fill(
-                static_cast<uint8_t>(teeui::TestKeyBits::BYTE));
-        op.setHmacKey(kTestKey);
+    /* Use the test key for emulator. */
+    constexpr const auto kTestKey = teeui::AuthTokenKey::fill(
+            static_cast<uint8_t>(teeui::TestKeyBits::BYTE));
+    op->setHmacKey(kTestKey);
 #else
-        teeui::AuthTokenKey authKey;
-        if (get_auth_token_key(authKey) == true) {
-            TLOGD("%s, get auth token key successfully\n", __func__);
-        } else {
-            TLOGE("%s, get auth token key failed\n", __func__);
-            /* Abort operation and free all resources */
-            op.abort();
-            close(channel);
-            return;
-        }
-        op.setHmacKey(authKey);
+    teeui::AuthTokenKey authKey;
+    if (get_auth_token_key(authKey) == true) {
+        TLOGD("%s, get auth token key successfully\n", __func__);
+    } else {
+        TLOGE("%s, get auth token key failed\n", __func__);
+        /* Abort operation and free all resources. */
+        op->abort();
+        return ERR_GENERIC;
+    }
+    op->setHmacKey(authKey);
 #endif
 
-        IpcState state = IpcState::RECEIVING;
-
-        while (true) {
-            uevent_t event = UEVENT_INITIAL_VALUE(evt);
-            TLOGD("Waiting (state: %s)\n", toString(state));
-            rc = wait(channel, &event, INFINITE_TIME);
-            if (rc < 0) {
-                TLOGI("Wait returned error %d", rc);
-                break;
-            }
-            TLOGD("Returned from wait with %u\n", event.event);
-
-            tipc_handle_chan_errors(&event);
-            if (event.event & IPC_HANDLE_POLL_HUP) {
-                TLOGI("Got HUP\n");
-                break;
-            }
-            if (!(event.event & IPC_HANDLE_POLL_MSG))
-                continue;
-
-            // Handle message
-
-            PacketHeader header{};
-            constexpr const size_t header_size = sizeof(PacketHeader);
-            constexpr const uint32_t max_payload_size =
-                    kPacketSize - header_size;
-            int rc = tipc_recv_hdr_payload(channel, &header, header_size,
-                                           &aligned_message[mpos],
-                                           aligned_message_size - mpos);
-            TLOGD("Got header msg type: %u remaining %u rc %d\n", header.type,
-                  header.remaining, rc);
-            if (rc < 0) {
-                TLOGE("Error reading command %d\n", rc);
-                break;
-            }
-
-            switch (header.type) {
-            case PacketType::SND: {
-                if (state != IpcState::RECEIVING) {
-                    state = IpcState::DESYNC;
-                    break;
-                }
-                // We are receiving SND packets
-                size_t body_size = rc - header_size;
-                mpos += body_size;
-                header.type = PacketType::ACK;
-                header.remaining -= body_size;
-                rc = tipc_send1(channel, &header, header_size);
-                if (rc != header_size) {
-                    TLOGE("Failed to send ACK %d\n", rc);
-                    state = IpcState::DESYNC;
-                    break;
-                }
-                if (header.remaining == 0) {
-                    // we got a full message
-
-                    // handleMsg reads the message from the first buffer, then
-                    // writes the reponse to the second buffer. The last
-                    // argument is the second buffer size (in) and the written
-                    // response size (out);
-                    msize = aligned_message_size;
-
-                    TLOGD("Calling event handler\n");
-                    op.handleMsg(&aligned_message[0], mpos, &aligned_message[0],
-                                 &msize);
-                    TLOGD("Returned from event handler \n");
-
-                    // the response starts at message[0]
-                    mpos = 0;
-                    state = IpcState::SENDING;
-                }
-                break;
-            }
-            case PacketType::RCV: {
-                if (state != IpcState::SENDING) {
-                    state = IpcState::DESYNC;
-                    break;
-                }
-                // We are sending RCV packets
-                header.remaining = msize - mpos;
-                header.type = PacketType::ACK;
-                size_t body_size = std::min(max_payload_size, header.remaining);
-                rc = tipc_send2(channel, &header, header_size,
-                                &aligned_message[mpos], body_size);
-                mpos += body_size;
-                if (mpos == msize) {
-                    TLOGD("complete response sent\n");
-                    // we sent the full response -> switch to expecting the next
-                    // message.
-                    state = IpcState::RECEIVING;
-                    mpos = 0;
-                    msize = 0;
-                }
-                break;
-            }
-            case PacketType::ACK:
-            default:
-                state = IpcState::DESYNC;
-            }
-            if (state == IpcState::DESYNC) {
-                TLOGE("Protocol out of sync\n");
-                break;
-            }
-        }
-
-        TLOGD("Leaving session loop\n");
-        // Abort operation and free all resources
-        op.abort();
-        close(channel);
-    }
+    ctx->op = std::move(op);
+    *ctx_p = ctx;
+    return NO_ERROR;
 }
 
+static void on_channel_cleanup(void* _ctx) {
+    struct chan_ctx* ctx = (struct chan_ctx*)_ctx;
+    /* Abort operation and free all resources. */
+    munmap(ctx->shm_base, ctx->shm_len);
+    ctx->op->abort();
+    ctx->op.reset();
+    free(ctx);
+}
+
+static int on_message(const struct tipc_port* port, handle_t chan, void* _ctx) {
+    int rc;
+    struct confirmationui_req req;
+    handle_t shm_handle = INVALID_IPC_HANDLE;
+    struct chan_ctx* ctx = (struct chan_ctx*)_ctx;
+
+    assert(ctx);
+
+    rc = confirmationui_recv(chan, &req, &shm_handle);
+    if (rc < 0) {
+        TLOGE("Failed to receive confirmationui request (%d)\n", rc);
+        return rc;
+    }
+
+    if (rc != (int)sizeof(req)) {
+        TLOGE("Receive request of unexpected size(%d)\n", rc);
+        rc = ERR_BAD_LEN;
+        goto out;
+    }
+
+    switch (req.hdr.cmd) {
+    case CONFIRMATIONUI_CMD_INIT:
+        rc = handle_init(chan, shm_handle, req.init_args.shm_len, ctx);
+        goto out;
+
+    case CONFIRMATIONUI_CMD_MSG:
+        rc = handle_msg(chan, req.msg_args.msg_len, ctx);
+        goto out;
+
+    default:
+        TLOGE("cmd 0x%x: unknown command\n", req.hdr.cmd);
+        rc = ERR_CMD_UNKNOWN;
+        goto out;
+    }
+
+out:
+    close(shm_handle);
+    return rc;
+}
+
+static struct tipc_port_acl confirmationui_port_acl = {
+        .flags = IPC_PORT_ALLOW_NS_CONNECT,
+};
+
+static struct tipc_port confirmationui_port = {
+        .name = CONFIRMATIONUI_PORT,
+        .msg_max_size = sizeof(confirmationui_req),
+        .msg_queue_len = 1,
+        .acl = &confirmationui_port_acl,
+};
+
+static struct tipc_srv_ops confirmationui_ops = {
+        .on_connect = on_connect,
+        .on_message = on_message,
+        .on_channel_cleanup = on_channel_cleanup,
+};
+
 int main(void) {
     int rc;
-    handle_t port;
+    struct tipc_hset* hset;
 
     TLOGI("Initializing ConfirmationUI app\n");
 
-    rc = port_create(CONFIRMATIONUI_PORT_NAME, 1, 4096,
-                     IPC_PORT_ALLOW_NS_CONNECT);
-    if (rc < 0) {
-        TLOGE("%s: failed (%d) create port\n", __func__, rc);
+    hset = tipc_hset_create();
+    if (IS_ERR(hset)) {
+        TLOGE("Failed to create handle set (%d)\n", PTR_ERR(hset));
+        return PTR_ERR(hset);
+    }
+
+    rc = tipc_add_service(hset, &confirmationui_port, 1, 1,
+                          &confirmationui_ops);
+    if (rc != NO_ERROR) {
         return rc;
     }
-    port = (handle_t)rc;
 
-    uevent_t event = UEVENT_INITIAL_VALUE(evt);
-
-    do {
-        rc = wait(port, &event, INFINITE_TIME);
-        TLOGD("Got a connection\n");
-        port_handler(&event, nullptr);
-    } while (rc == 0);
-
-    TLOGE("wait on port returned unexpected %d\n", rc);
-    close(port);
-
-    return rc;
+    return tipc_run_event_loop(hset);
 }