blob: 1757839592b81038a08c389c9cd1e14e007c32d5 [file] [log] [blame]
/*
* Copyright (C) 2016 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 "hwrng_srv"
#include <assert.h>
#include <inttypes.h>
#include <lk/list.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uapi/err.h>
#include <hwcrypto/hwrng_dev.h>
#include <interface/hwrng/hwrng.h>
#include <lib/tipc/tipc.h>
#include <lib/tipc/tipc_srv.h>
#include <trusty_log.h>
#define HWRNG_SRV_NAME HWRNG_PORT
#define MAX_HWRNG_MSG_SIZE 4096
/* 0 means unlimited number of connections */
#define HWRNG_MAX_NUM_CHANNELS 0
struct hwrng_chan_ctx {
struct tipc_event_handler evt_handler;
struct list_node node;
handle_t chan;
size_t req_size;
int error;
bool send_blocked;
};
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) {
uintptr_t address = (uintptr_t)ptr;
size_t count;
size_t i;
for (count = 0; count < len; count += 16) {
fprintf(stderr, "0x%08" PRIxPTR ": ", address);
for (i = 0; i < MIN(len - count, 16); i++) {
fprintf(stderr, "0x%02hhx ", *(const uint8_t*)(address + i));
}
fprintf(stderr, "\n");
address += 16;
}
}
/*
* Handle HWRNG request queue
*/
static void hwrng_handle_req_queue(void) {
int rc;
struct hwrng_chan_ctx* ctx;
struct hwrng_chan_ctx* temp;
/* for all pending requests */
bool more_requests;
do {
more_requests = false;
list_for_every_entry_safe(&hwrng_req_list, ctx, temp,
struct hwrng_chan_ctx, node) {
if (ctx->error || ctx->send_blocked) {
continue; /* can't service it right now */
}
size_t len = ctx->req_size;
if (len > MAX_HWRNG_MSG_SIZE)
len = MAX_HWRNG_MSG_SIZE;
/* get hwrng data */
rc = trusty_rng_hw_rand(rng_data, len);
if (rc != NO_ERROR) {
TLOGE("failed (%d) to get hwrng data\n", rc);
ctx->error = rc;
continue;
}
/* send reply */
rc = tipc_send1(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);
ctx->error = rc;
}
continue;
}
ctx->req_size -= len;
if (ctx->req_size == 0) {
/* remove it from pending list */
list_delete(&ctx->node);
} else {
more_requests = true;
}
}
} while (more_requests);
}
/*
* Read and queue HWRNG request message
*/
static int hwrng_chan_handle_msg(const struct tipc_port* port,
handle_t chan,
void* received_ctx) {
int rc;
struct hwrng_req req;
struct hwrng_chan_ctx* ctx = (struct hwrng_chan_ctx*)received_ctx;
assert(ctx);
/* check for an error from a previous send attempt */
if (ctx->error) {
return ctx->error;
}
/* read request */
rc = tipc_recv1(chan, sizeof(req), &req, sizeof(req));
if (rc < 0) {
TLOGE("failed (%d) to receive msg for chan %d\n", rc, 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);
}
hwrng_handle_req_queue();
return ctx->error;
}
/*
* Create hwrng channel context
*/
static int hwrng_chan_ctx_create(const struct tipc_port* port,
handle_t chan,
const struct uuid* peer,
void** ctx) {
struct hwrng_chan_ctx* chan_ctx = calloc(1, sizeof(*chan_ctx));
if (!chan_ctx) {
return ERR_NO_MEMORY;
}
/* init channel state */
chan_ctx->chan = chan;
*ctx = chan_ctx;
return NO_ERROR;
}
/*
* Close specified hwrng channel context
*/
static void hwrng_chan_ctx_close(void* ctx_rcv) {
struct hwrng_chan_ctx* ctx = (struct hwrng_chan_ctx*)ctx_rcv;
if (list_in_list(&ctx->node))
list_delete(&ctx->node);
close(ctx->chan);
free(ctx);
}
static int hwrng_handle_send_unblocked(const struct tipc_port* port,
handle_t chan,
void* ctx_v) {
struct hwrng_chan_ctx* ctx = ctx_v;
if (ctx->error) {
return ctx->error;
}
ctx->send_blocked = false;
hwrng_handle_req_queue();
return ctx->error;
}
/*
* Initialize HWRNG services
*/
int hwrng_start_service(struct tipc_hset* hset) {
int rc;
TLOGD("Start HWRNG service\n");
static struct tipc_port_acl acl = {
.flags = IPC_PORT_ALLOW_TA_CONNECT,
.uuid_num = 0,
.uuids = NULL,
};
static struct tipc_port port = {
.name = HWRNG_SRV_NAME,
.msg_max_size = MAX_HWRNG_MSG_SIZE,
.msg_queue_len = 1,
.acl = &acl,
};
static struct tipc_srv_ops ops = {
.on_message = hwrng_chan_handle_msg,
.on_connect = hwrng_chan_ctx_create,
.on_channel_cleanup = hwrng_chan_ctx_close,
.on_send_unblocked = hwrng_handle_send_unblocked,
};
rc = hwrng_dev_init();
if (rc != NO_ERROR) {
TLOGE("Failed (%d) to initialize HWRNG device\n", rc);
return rc;
}
return tipc_add_service(hset, &port, 1, HWRNG_MAX_NUM_CHANNELS, &ops);
}