secure_fb: Add generic ipc server implementation with service side API.

Add the generic ipc server and the API that services need to implement
when they link against this service.

Bug: 175731623
Change-Id: I5173e9340d9932e1f60150c2badfcd6117f25488
diff --git a/lib/secure_fb/srv/include/lib/secure_fb/srv/dev.h b/lib/secure_fb/srv/include/lib/secure_fb/srv/dev.h
new file mode 100644
index 0000000..e4bb241
--- /dev/null
+++ b/lib/secure_fb/srv/include/lib/secure_fb/srv/dev.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2020, 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 <interface/secure_fb/secure_fb.h>
+#include <lk/compiler.h>
+#include <stdint.h>
+#include <trusty_ipc.h>
+
+__BEGIN_CDECLS
+
+/**
+ *  DOC: API notes
+ *
+ *  This header defines an API for implementing hardware-specific secure
+ *  framebuffer driver. It is expected that it will be working in conjunction
+ *  with a higher level service that will make calls described here.
+ */
+
+typedef void* secure_fb_handle_t;
+
+struct secure_fb_impl_buffers {
+    size_t num_fbs;
+    struct secure_fb_desc fbs[SECURE_FB_MAX_FBS];
+    size_t num_handles;
+    handle_t handles[SECURE_FB_MAX_FBS];
+};
+
+/**
+ * secure_fb_impl_init() - This function together with secure_fb_impl_release()
+ * frames the life cycle of a secure_fb session. The life cycle begins with this
+ * function, and the session is represented by the returned handle.
+ *
+ * Return: Session handle for the secure_fb session or NULL on failure.
+ */
+secure_fb_handle_t secure_fb_impl_init(void);
+
+/**
+ * secure_fb_impl_get_fbs() - Gets a set of up to %SECURE_FB_MAX_FBS buffers
+ * that can be used for rendering. The number of buffers is implementation
+ * defined.
+ *
+ * @session: The active session as returned by a previous call to
+ *           secure_fb_imppl_init().
+ * @buffers: Describes the buffers returned.
+ *
+ * Return: SECURE_FB_ERROR_OK on success, or an error code < 0 on failure.
+ */
+int secure_fb_impl_get_fbs(secure_fb_handle_t session,
+                           struct secure_fb_impl_buffers* buffers);
+
+/**
+ * secure_fb_impl_display_fb() - Select one of the buffers returned by
+ * secure_fb_impl_get_fbs() as active buffer. If only one buffer was
+ * returned this function doubles as render complete barrier indicating to the
+ * driver that the screen may be updated.
+ *
+ * @session:   The active session as returned by a previous call to
+ *             secure_fb_impl_init().
+ * @buffer_id: Indicates one of the buffers returned by
+ *             secure_fb_impl_get_fbs(). The @buffer_id is not an index. It
+ *             can be found at @buffers.fbs[index].buffer_id with @buffers being
+ *             the structure returned by secure_fb_impl_get_fbs().
+ *
+ * Return: SECURE_FB_ERROR_OK on success, or an error code < 0 on failure.
+ */
+int secure_fb_impl_display_fb(secure_fb_handle_t session, uint32_t buffer_id);
+
+/**
+ * secure_fb_impl_release() - Ends the life cycle of the a secure_fb session.
+ * It must relinquish all resources associated with the secure_fb session.
+ *
+ * @session: The active session as returned by a previous call to
+ *           secure_fb_impl_init().
+ *
+ * Return: SECURE_FB_ERROR_OK on success, or an error code < 0 on failure.
+ */
+int secure_fb_impl_release(secure_fb_handle_t session);
+
+__END_CDECLS
diff --git a/lib/secure_fb/srv/include/lib/secure_fb/srv/srv.h b/lib/secure_fb/srv/include/lib/secure_fb/srv/srv.h
new file mode 100644
index 0000000..444b7df
--- /dev/null
+++ b/lib/secure_fb/srv/include/lib/secure_fb/srv/srv.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2020, 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 <lib/tipc/tipc_srv.h>
+#include <lk/compiler.h>
+#include <stdint.h>
+
+__BEGIN_CDECLS
+
+/**
+ * add_secure_fb_service() - Add secure_fb service.
+ * @hset: Handle set created by tipc_hset_create()
+ *
+ * The caller should call tipc_run_event_loop() at some point after this call
+ * returns.
+ *
+ * Return: 0 on success, or an error code < 0 on failure.
+ */
+int add_secure_fb_service(struct tipc_hset* hset);
+
+__END_CDECLS
diff --git a/lib/secure_fb/srv/rules.mk b/lib/secure_fb/srv/rules.mk
new file mode 100644
index 0000000..dc4da10
--- /dev/null
+++ b/lib/secure_fb/srv/rules.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2020 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.
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+	$(LOCAL_DIR)/secure_fb_server.c \
+
+MODULE_DEPS += \
+	trusty/user/base/interface/secure_fb \
+	trusty/user/base/lib/libc-trusty \
+	trusty/user/base/lib/tipc \
+
+include make/module.mk
diff --git a/lib/secure_fb/srv/secure_fb_server.c b/lib/secure_fb/srv/secure_fb_server.c
new file mode 100644
index 0000000..036f527
--- /dev/null
+++ b/lib/secure_fb/srv/secure_fb_server.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#define TLOG_TAG "secure_fb_service"
+
+#include <assert.h>
+#include <interface/secure_fb/secure_fb.h>
+#include <lib/secure_fb/srv/dev.h>
+#include <lib/secure_fb/srv/srv.h>
+#include <lib/tipc/tipc.h>
+#include <lib/tipc/tipc_srv.h>
+#include <lk/compiler.h>
+#include <lk/macros.h>
+#include <stdlib.h>
+#include <string.h>
+#include <trusty_ipc.h>
+#include <trusty_log.h>
+#include <uapi/err.h>
+
+struct secure_fb_ctx {
+    secure_fb_handle_t session;
+};
+
+static int secure_fb_on_connect(const struct tipc_port* port,
+                                handle_t chan,
+                                const struct uuid* peer,
+                                void** ctx_p) {
+    struct secure_fb_ctx* ctx = malloc(sizeof(*ctx));
+    if (ctx == NULL) {
+        TLOGE("Memory allocation failed.\n");
+        return ERR_NO_MEMORY;
+    }
+
+    ctx->session = secure_fb_impl_init();
+    if (ctx->session == NULL) {
+        TLOGE("Driver initialization failed.\n");
+        free(ctx);
+        return ERR_GENERIC;
+    }
+
+    *ctx_p = ctx;
+    return NO_ERROR;
+}
+
+static void secure_fb_on_channel_cleanup(void* _ctx) {
+    struct secure_fb_ctx* ctx = (struct secure_fb_ctx*)_ctx;
+    if (ctx->session != NULL) {
+        secure_fb_impl_release(ctx);
+    }
+    free(ctx);
+}
+
+static int handle_get_fbs_req(handle_t chan, secure_fb_handle_t session) {
+    int rc;
+    struct secure_fb_impl_buffers buffers;
+    struct secure_fb_resp hdr;
+    struct secure_fb_get_fbs_resp args;
+    struct secure_fb_desc fbs[SECURE_FB_MAX_FBS];
+    size_t fbs_len;
+
+    rc = secure_fb_impl_get_fbs(session, &buffers);
+    if (rc != SECURE_FB_ERROR_OK) {
+        TLOGE("Failed secure_fb_impl_get_fbs() (%d)\n", rc);
+    }
+
+    hdr.cmd = SECURE_FB_CMD_GET_FBS | SECURE_FB_CMD_RESP_BIT;
+    hdr.status = rc;
+
+    args.num_fbs = buffers.num_fbs;
+
+    fbs_len = sizeof(fbs[0]) * args.num_fbs;
+    memcpy(fbs, buffers.fbs, fbs_len);
+
+    struct iovec iovs[] = {
+            {
+                    .iov_base = &hdr,
+                    .iov_len = sizeof(hdr),
+            },
+            {
+                    .iov_base = &args,
+                    .iov_len = sizeof(args),
+            },
+            {
+                    .iov_base = fbs,
+                    .iov_len = fbs_len,
+            },
+    };
+    ipc_msg_t msg = {
+            .num_iov = countof(iovs),
+            .iov = iovs,
+            .num_handles = buffers.num_handles,
+            .handles = buffers.handles,
+    };
+    rc = send_msg(chan, &msg);
+    if (rc != (int)(sizeof(hdr) + sizeof(args) + fbs_len)) {
+        TLOGE("Failed to send SECURE_FB_CMD_GET_FBS response (%d)\n", rc);
+        if (rc >= 0) {
+            return ERR_BAD_LEN;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+static int handle_display_fb(handle_t chan,
+                             struct secure_fb_display_fb_req* display_fb,
+                             secure_fb_handle_t session) {
+    int rc;
+    struct secure_fb_resp hdr;
+
+    rc = secure_fb_impl_display_fb(session, display_fb->buffer_id);
+    if (rc != SECURE_FB_ERROR_OK) {
+        TLOGE("Failed secure_fb_impl_display_fb() (%d)\n", rc);
+    }
+
+    hdr.cmd = SECURE_FB_CMD_DISPLAY_FB | SECURE_FB_CMD_RESP_BIT;
+    hdr.status = rc;
+
+    rc = tipc_send1(chan, &hdr, sizeof(hdr));
+    if (rc != (int)sizeof(hdr)) {
+        TLOGE("Failed to send SECURE_FB_CMD_DISPLAY_FB response (%d)\n", rc);
+        if (rc >= 0) {
+            return ERR_BAD_LEN;
+        }
+    }
+
+    return NO_ERROR;
+}
+
+static int secure_fb_on_message(const struct tipc_port* port,
+                                handle_t chan,
+                                void* _ctx) {
+    int rc;
+    struct {
+        struct secure_fb_req hdr;
+        union {
+            struct secure_fb_display_fb_req display_fb;
+        };
+    } req;
+    struct secure_fb_ctx* ctx = (struct secure_fb_ctx*)_ctx;
+
+    rc = tipc_recv1(chan, sizeof(req.hdr), &req, sizeof(req));
+    if (rc < 0) {
+        TLOGE("Failed to read command %d\n", rc);
+        return ERR_BAD_LEN;
+    }
+
+    switch (req.hdr.cmd) {
+    case SECURE_FB_CMD_GET_FBS:
+        if (rc != (int)sizeof(req.hdr)) {
+            TLOGE("Failed to read SECURE_FB_CMD_GET_FBS request (%d)\n", rc);
+            return ERR_BAD_LEN;
+        }
+        return handle_get_fbs_req(chan, ctx->session);
+
+    case SECURE_FB_CMD_DISPLAY_FB:
+        if (rc != (int)(sizeof(req.hdr) + sizeof(req.display_fb))) {
+            TLOGE("Failed to read SECURE_FB_CMD_DISPLAY_FB request (%d)\n", rc);
+            return ERR_BAD_LEN;
+        }
+        return handle_display_fb(chan, &req.display_fb, ctx->session);
+
+    case SECURE_FB_CMD_RELEASE:
+        if (rc != (int)sizeof(req.hdr)) {
+            TLOGE("Failed to read SECURE_FB_CMD_RELEASE request (%d)\n", rc);
+            return ERR_BAD_LEN;
+        }
+        secure_fb_impl_release(ctx->session);
+        ctx->session = NULL;
+        return NO_ERROR;
+
+    default:
+        TLOGW("Received unknown command %x\n", req.hdr.cmd);
+        return ERR_CMD_UNKNOWN;
+    }
+
+    return NO_ERROR;
+}
+
+int add_secure_fb_service(struct tipc_hset* hset) {
+    static struct tipc_port_acl acl = {
+            .flags = IPC_PORT_ALLOW_TA_CONNECT,
+    };
+    static struct tipc_port port = {
+            .name = SECURE_FB_PORT_NAME,
+            .msg_max_size = 1024,
+            .msg_queue_len = 1,
+            .acl = &acl,
+    };
+    static struct tipc_srv_ops ops = {
+            .on_connect = secure_fb_on_connect,
+            .on_message = secure_fb_on_message,
+            .on_channel_cleanup = secure_fb_on_channel_cleanup,
+    };
+
+    /*
+     * The secure display is a limited resource. This means only one client
+     * can have an open session at a time.
+     */
+    return tipc_add_service(hset, &port, 1, 1, &ops);
+}