| /* |
| * 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_dpu" |
| |
| #include <assert.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 <sys/auxv.h> |
| #include <sys/mman.h> |
| #include <trusty_ipc.h> |
| #include <trusty_log.h> |
| #include <uapi/err.h> |
| |
| #include <interface/secure_dpu/secure_dpu.h> |
| #include <lib/secure_dpu/secure_dpu.h> |
| |
| struct secure_dpu_ctx { |
| /* |
| * This pointer is passed from user. |
| * Update this pointer when connecting / disconnecting. |
| */ |
| handle_t* chan; |
| }; |
| |
| static struct secure_dpu_ctx ctx; |
| |
| static struct tipc_port_acl acl = { |
| .flags = IPC_PORT_ALLOW_NS_CONNECT, |
| }; |
| static struct tipc_port port = { |
| .name = SECURE_DPU_PORT_NAME, |
| .msg_max_size = SECURE_DPU_MAX_MSG_SIZE, |
| .msg_queue_len = 1, |
| .acl = &acl, |
| .priv = &ctx, |
| }; |
| |
| static int handle_allocate_buffer_resp(handle_t chan, |
| size_t buffer_len, |
| struct secure_dpu_buf_info* buf_info) { |
| int rc = NO_ERROR; |
| struct uevent evt; |
| struct secure_dpu_resp hdr; |
| struct secure_dpu_allocate_buffer_resp resp; |
| handle_t buf_handle; |
| |
| struct iovec iov[] = { |
| { |
| .iov_base = &hdr, |
| .iov_len = sizeof(hdr), |
| }, |
| { |
| .iov_base = &resp, |
| .iov_len = sizeof(resp), |
| }, |
| }; |
| |
| struct ipc_msg msg = { |
| .iov = iov, |
| .num_iov = countof(iov), |
| .handles = &buf_handle, |
| .num_handles = 1, |
| }; |
| |
| rc = wait(chan, &evt, INFINITE_TIME); |
| if (rc != NO_ERROR) { |
| TLOGE("Error waiting for response (%d)\n", rc); |
| return rc; |
| } |
| |
| struct ipc_msg_info msg_inf; |
| rc = get_msg(chan, &msg_inf); |
| if (rc) { |
| return rc; |
| } |
| |
| if (msg_inf.num_handles != 1) { |
| TLOGE("Message had no handles\n"); |
| return ERR_INVALID_ARGS; |
| } |
| |
| rc = read_msg(chan, msg_inf.id, 0, &msg); |
| put_msg(chan, msg_inf.id); |
| if (rc != (int)(sizeof(hdr) + sizeof(resp))) { |
| TLOGE("Read length does not match\n"); |
| close(buf_handle); |
| return ERR_BAD_LEN; |
| } |
| |
| if (hdr.cmd != (SECURE_DPU_CMD_ALLOCATE_BUFFER | SECURE_DPU_CMD_RESP_BIT)) { |
| close(buf_handle); |
| return ERR_CMD_UNKNOWN; |
| } |
| |
| if (hdr.status != SECURE_DPU_ERROR_OK) { |
| TLOGE("Failed SECURE_DPU_CMD_ALLOCATE_BUFFER (%d)\n", hdr.status); |
| close(buf_handle); |
| return ERR_GENERIC; |
| } |
| |
| if ((size_t)resp.buffer_len < buffer_len) { |
| TLOGE("Not allocated enough buffer length, " |
| "requested (%zu), allocated (%zu)\n", (size_t)buffer_len, |
| (size_t)resp.buffer_len); |
| close(buf_handle); |
| return ERR_NOT_ENOUGH_BUFFER; |
| } |
| |
| void* out = mmap(0, (size_t)resp.buffer_len, PROT_READ | PROT_WRITE, 0, |
| buf_handle, 0); |
| if (!out) { |
| TLOGE("Error when calling mmap()\n"); |
| return ERR_BAD_HANDLE; |
| } |
| close(buf_handle); |
| buf_info->addr = (void*)out; |
| buf_info->len = (size_t)resp.buffer_len; |
| |
| return NO_ERROR; |
| } |
| |
| int secure_dpu_allocate_buffer(handle_t chan, |
| size_t buffer_len, |
| struct secure_dpu_buf_info* buf_info) { |
| |
| int rc; |
| struct secure_dpu_req hdr; |
| struct secure_dpu_allocate_buffer_req args; |
| if (!buf_info) { |
| TLOGE("Invalid arguments to allocate DPU buffer\n"); |
| return ERR_INVALID_ARGS; |
| } |
| if (chan == INVALID_IPC_HANDLE) { |
| TLOGE("Channel is not ready\n"); |
| return ERR_NOT_READY; |
| } |
| |
| hdr.cmd = SECURE_DPU_CMD_ALLOCATE_BUFFER; |
| args.buffer_len = (uint64_t)buffer_len; |
| |
| rc = tipc_send2(chan, &hdr, sizeof(hdr), &args, sizeof(args)); |
| if (rc != (int)(sizeof(hdr) + sizeof(args))) { |
| TLOGE("Failed to send SECURE_DPU_CMD_ALLOCATE_BUFFER request (%d)\n", rc); |
| return rc; |
| } |
| |
| rc = handle_allocate_buffer_resp(chan, buffer_len, buf_info); |
| if (rc < 0) { |
| TLOGE("Failed to handle allocate buffer\n"); |
| return rc; |
| } |
| return rc; |
| } |
| |
| int secure_dpu_release_buffer(struct secure_dpu_buf_info* buf_info) { |
| if (!buf_info) { |
| TLOGE("Invalid arguments to release DPU buffer\n"); |
| return ERR_INVALID_ARGS; |
| } |
| |
| int rc = munmap(buf_info->addr, buf_info->len); |
| if (rc < 0) { |
| TLOGE("Failed to do munmap\n"); |
| return rc; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| static int handle_start_secure_display_resp(handle_t chan) { |
| int rc; |
| struct uevent evt; |
| struct secure_dpu_resp hdr; |
| |
| rc = wait(chan, &evt, INFINITE_TIME); |
| if (rc != NO_ERROR) { |
| TLOGE("Error waiting for response (%d)\n", rc); |
| return rc; |
| } |
| |
| rc = tipc_recv1(chan, sizeof(hdr), &hdr, sizeof(hdr)); |
| if (rc != sizeof(hdr)) { |
| TLOGE("Failed to receive SECURE_DPU_CMD_START_SECURE_DISPLAY response (%d)\n", rc); |
| return rc; |
| } |
| |
| if (hdr.cmd != (SECURE_DPU_CMD_START_SECURE_DISPLAY | SECURE_DPU_CMD_RESP_BIT)) { |
| return ERR_CMD_UNKNOWN; |
| } |
| |
| if (hdr.status != SECURE_DPU_ERROR_OK) { |
| TLOGE("Failed SECURE_DPU_CMD_START_SECURE_DISPLAY (%d)\n", hdr.status); |
| return ERR_GENERIC; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| int secure_dpu_start_secure_display(handle_t chan) { |
| int rc; |
| struct secure_dpu_req hdr; |
| |
| if (chan == INVALID_IPC_HANDLE) { |
| TLOGE("Invalid arguments to start display\n"); |
| return ERR_INVALID_ARGS; |
| } |
| |
| hdr.cmd = SECURE_DPU_CMD_START_SECURE_DISPLAY; |
| |
| rc = tipc_send1(chan, &hdr, sizeof(hdr)); |
| if (rc != (int)(sizeof(hdr))) { |
| TLOGE("Failed to send SECURE_DPU_CMD_START_SECURE_DISPLAY request (%d)\n", rc); |
| return rc; |
| } |
| |
| return handle_start_secure_display_resp(chan); |
| } |
| |
| static int handle_stop_secure_display_resp(handle_t chan) { |
| int rc; |
| struct uevent evt; |
| struct secure_dpu_resp hdr; |
| |
| rc = wait(chan, &evt, INFINITE_TIME); |
| if (rc != NO_ERROR) { |
| TLOGE("Error waiting for response (%d)\n", rc); |
| return rc; |
| } |
| |
| rc = tipc_recv1(chan, sizeof(hdr), &hdr, sizeof(hdr)); |
| if (rc != sizeof(hdr)) { |
| TLOGE("Failed to receive SECURE_DPU_CMD_STOP_SECURE_DISPLAY response (%d)\n", rc); |
| return rc; |
| } |
| |
| if (hdr.cmd != (SECURE_DPU_CMD_STOP_SECURE_DISPLAY | SECURE_DPU_CMD_RESP_BIT)) { |
| return ERR_CMD_UNKNOWN; |
| } |
| |
| if (hdr.status != SECURE_DPU_ERROR_OK) { |
| TLOGE("Failed SECURE_DPU_CMD_STOP_SECURE_DISPLAY (%d)\n", hdr.status); |
| return ERR_GENERIC; |
| } |
| |
| return NO_ERROR; |
| } |
| |
| int secure_dpu_stop_secure_display(handle_t chan) { |
| int rc; |
| struct secure_dpu_req hdr; |
| |
| if (chan == INVALID_IPC_HANDLE) { |
| TLOGE("Invalid arguments to stop display\n"); |
| return ERR_INVALID_ARGS; |
| } |
| |
| hdr.cmd = SECURE_DPU_CMD_STOP_SECURE_DISPLAY; |
| |
| rc = tipc_send1(chan, &hdr, sizeof(hdr)); |
| if (rc != (int)(sizeof(hdr))) { |
| TLOGE("Failed to send SECURE_DPU_CMD_STOP_SECURE_DISPLAY request (%d)\n", rc); |
| return rc; |
| } |
| |
| return handle_stop_secure_display_resp(chan); |
| } |
| |
| /* Default message handler, not being used for normal case */ |
| static int secure_dpu_on_message(const struct tipc_port* port, |
| handle_t chan, |
| void* _ctx) { |
| /* Not expect any incoming message to this default handler */ |
| return ERR_CMD_UNKNOWN; |
| } |
| |
| static int secure_dpu_on_connect(const struct tipc_port* port, |
| handle_t chan, |
| const struct uuid* peer, |
| void** ctx_p) { |
| struct secure_dpu_ctx* priv = (struct secure_dpu_ctx*)port->priv; |
| |
| assert(priv->chan); |
| /* Update the handle to user provided pointer */ |
| *(priv->chan) = chan; |
| |
| return NO_ERROR; |
| } |
| |
| void secure_dpu_on_disconnect(const struct tipc_port* port, |
| handle_t chan, |
| void* ctx) { |
| struct secure_dpu_ctx* priv = (struct secure_dpu_ctx*)port->priv; |
| |
| assert(priv->chan); |
| *(priv->chan) = INVALID_IPC_HANDLE; |
| } |
| |
| int add_secure_dpu_service(struct tipc_hset* hset, handle_t* chan) { |
| if (!hset || !chan) { |
| return ERR_INVALID_ARGS; |
| } |
| |
| ctx.chan = chan; |
| |
| static struct tipc_srv_ops ops = { |
| .on_connect = secure_dpu_on_connect, |
| .on_disconnect = secure_dpu_on_disconnect, |
| .on_message = secure_dpu_on_message, |
| }; |
| |
| /* |
| * 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); |
| } |