blob: 0986d764a143c72d5e8a65734c6f013705e78b87 [file] [log] [blame]
/*
* Copyright (C) 2016-2017 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.
*/
#include <assert.h>
#include <lk/list.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <trusty_ipc.h>
#include <uapi/err.h>
#include <interface/hwrng/hwrng.h>
#include "common.h"
#include "hwrng_srv_priv.h"
#define TLOG_LVL TLOG_LVL_DEFAULT
#define TLOG_TAG "hwrng_srv"
#include "tlog.h"
#define HWRNG_SRV_NAME HWRNG_PORT
#define MAX_HWRNG_MSG_SIZE 4096
struct hwrng_chan_ctx {
tipc_event_handler_t evt_handler;
struct list_node node;
handle_t chan;
size_t req_size;
bool send_blocked;
};
static void hwrng_port_handler(const uevent_t *ev, void *priv);
static void hwrng_chan_handler(const uevent_t *ev, void *priv);
static handle_t hwrng_port = INVALID_IPC_HANDLE;
static tipc_event_handler_t hwrng_port_evt_handler = {
.proc = hwrng_port_handler,
};
static uint8_t rng_data[MAX_HWRNG_MSG_SIZE];
static struct list_node hwrng_req_list = LIST_INITIAL_VALUE(hwrng_req_list);
/****************************************************************************/
/*
* Hexdump content of memory region
*/
static void _hexdump8(const void *ptr, size_t len)
{
addr_t address = (addr_t)ptr;
size_t count;
size_t i;
for (count = 0 ; count < len; count += 16) {
fprintf(stderr, "0x%08lx: ", address);
for (i=0; i < MIN(len - count, 16); i++) {
fprintf(stderr, "0x%02hhx ", *(const uint8_t *)(address + i));
}
fprintf(stderr, "\n");
address += 16;
}
}
/*
* Close specified HWRNG service channel
*/
static void hwrng_close_chan(struct hwrng_chan_ctx *ctx)
{
close(ctx->chan);
if (list_in_list(&ctx->node))
list_delete(&ctx->node);
free(ctx);
}
/*
* Handle HWRNG request queue
*/
static bool hwrng_handle_req_queue(void)
{
struct hwrng_chan_ctx *ctx;
struct hwrng_chan_ctx *temp;
/* service channels */
bool need_more = false;
/* for all pending requests */
list_for_every_entry_safe(&hwrng_req_list, ctx, temp,
struct hwrng_chan_ctx, node) {
if (ctx->send_blocked)
continue; /* cant service it rignt now */
size_t len = ctx->req_size;
if (len > MAX_HWRNG_MSG_SIZE)
len = MAX_HWRNG_MSG_SIZE;
/* get rng data */
hwrng_dev_get_rng_data(rng_data, len);
/* send reply */
int rc = tipc_send_single_buf(ctx->chan, rng_data, len);
if (rc < 0) {
if (rc == ERR_NOT_ENOUGH_BUFFER) {
/* mark it as send_blocked */
ctx->send_blocked = true;
} else {
/* just close HWRNG request channel */
TLOGE("failed (%d) to send_reply\n", rc);
hwrng_close_chan(ctx);
}
continue;
}
ctx->req_size -= len;
if (ctx->req_size == 0) {
/* remove it from pending list */
list_delete(&ctx->node);
} else {
need_more = true;
}
}
return need_more;
}
/*
* Check if we can handle request queue
*/
static void hwrng_kick_req_queue(void)
{
hwrng_handle_req_queue();
}
/*
* Read and queue HWRNG request message
*/
static int hwrng_chan_handle_msg(struct hwrng_chan_ctx *ctx)
{
int rc;
struct hwrng_req req;
assert(ctx);
/* read request */
rc = tipc_recv_single_buf(ctx->chan, &req, sizeof(req));
if (rc != sizeof(req)) {
TLOGE("failed (%d) to receive msg for chan %d\n", rc, ctx->chan);
return rc;
}
/* check if we already have request in progress */
if (list_in_list(&ctx->node)) {
/* extend it */
ctx->req_size += req.len;
} else {
/* queue it */
ctx->req_size = req.len;
list_add_tail(&hwrng_req_list, &ctx->node);
}
return 0;
}
/*
* Channel handler where HWRNG requests are coming from
*/
static void hwrng_chan_handler(const uevent_t *ev, void *priv)
{
struct hwrng_chan_ctx *ctx = priv;
assert(ctx);
assert(ev->handle == ctx->chan);
tipc_handle_chan_errors(ev);
if (ev->event & IPC_HANDLE_POLL_HUP) {
hwrng_close_chan(ctx);
} else {
if (ev->event & IPC_HANDLE_POLL_SEND_UNBLOCKED) {
ctx->send_blocked = false;
}
if (ev->event & IPC_HANDLE_POLL_MSG) {
int rc = hwrng_chan_handle_msg(ctx);
if (rc) {
hwrng_close_chan(ctx);
}
}
}
/* kick state machine */
hwrng_kick_req_queue();
}
/*
* Port were HWRNG requests are coming from
*/
static void hwrng_port_handler(const uevent_t *ev, void *priv)
{
uuid_t peer_uuid;
tipc_handle_port_errors(ev);
if (ev->event & IPC_HANDLE_POLL_READY) {
handle_t chan;
/* incoming connection: accept it */
int rc = accept(ev->handle, &peer_uuid);
if (rc < 0) {
TLOGE("failed (%d) to accept on port %d\n", rc, ev->handle);
return;
}
chan = (handle_t) rc;
/* allocate state */
struct hwrng_chan_ctx *ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
TLOGE("failed to alloc state for chan %d\n", chan);
close(chan);
return;
}
/* init channel state */
ctx->evt_handler.priv = ctx;
ctx->evt_handler.proc = hwrng_chan_handler;
ctx->chan = chan;
/* attach channel handler */
rc = set_cookie(chan, &ctx->evt_handler);
if (rc) {
TLOGE("failed (%d) to set_cookie on chan %d\n", rc, chan);
free(ctx);
close(chan);
return;
}
}
}
/*
* Initialize HWRNG services
*/
int hwrng_start_service(void)
{
int rc;
TLOGI("Start HWRNG service\n");
/* create HWRNG port */
rc = port_create(HWRNG_SRV_NAME, 1, MAX_HWRNG_MSG_SIZE,
IPC_PORT_ALLOW_TA_CONNECT);
if (rc < 0) {
TLOGE("Failed (%d) to create port '%s'\n", rc, HWRNG_SRV_NAME);
return rc;
} else {
hwrng_port = (handle_t)rc;
set_cookie(hwrng_port, &hwrng_port_evt_handler);
}
return NO_ERROR;
}