blob: 43cffc54656390aa7e27d112d2a18c37127d2a7c [file] [log] [blame]
/*
* 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);
}