blob: 091cbb75e98d60be22b813131d374f05c12f67f8 [file]
/*
* Copyright (c) 2022, Google Inc. All rights reserved
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
* (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge,
* publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#define LOCAL_TRACE 0
#include <err.h>
#include <inttypes.h>
#include <lib/ktipc/ktipc.h>
#include <lib/rand/rand.h>
#include <lib/trusty/ipc.h>
#include <lib/trusty/ipc_msg.h>
#include <lib/trusty/uuid.h>
#include <lk/init.h>
#include <lk/list.h>
#include <lk/trace.h>
#include <interface/hwrng/hwrng.h>
#define HWRNG_SRV_NAME HWRNG_PORT
#define MAX_HWRNG_MSG_SIZE 128
struct hwrng_chan_ctx {
struct list_node node;
struct handle* chan;
size_t req_size;
int error;
bool send_blocked;
};
static uint8_t rng_data[MAX_HWRNG_MSG_SIZE];
/* If we have data left over from the last request, keep track of it */
static size_t rng_data_avail_count;
static size_t rng_data_avail_pos;
static struct list_node hwrng_req_list = LIST_INITIAL_VALUE(hwrng_req_list);
/*
* 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 */
/*
* send up to MAX_HWRNG_MSG_SIZE per client at a time,
* to prevent a single client from starving all the others
*/
size_t len = MIN(ctx->req_size, MAX_HWRNG_MSG_SIZE);
if (rng_data_avail_count) {
/* use leftover data if there is any */
len = MIN(len, rng_data_avail_count);
} else {
/* get hwrng data */
rand_get_bytes(rng_data, len);
rng_data_avail_pos = 0;
rng_data_avail_count = len;
}
/* send reply */
rc = ktipc_send(ctx->chan, rng_data + rng_data_avail_pos, len);
if (rc < 0) {
if (rc == ERR_NOT_ENOUGH_BUFFER) {
/* mark it as send_blocked */
ctx->send_blocked = true;
} else {
TRACEF("%s: failed (%d) to send_reply\n", __func__, rc);
ctx->error = rc;
}
continue;
}
rng_data_avail_pos += len;
rng_data_avail_count -= len;
ctx->req_size -= len;
if (!ctx->req_size) {
/* remove it from pending list */
list_delete(&ctx->node);
} else {
more_requests = true;
}
}
} while (more_requests);
}
static int hwrng_handle_msg(const struct ktipc_port* port,
struct handle* chan,
void* ctx_v) {
int rc;
struct hwrng_chan_ctx* ctx = ctx_v;
struct hwrng_req req;
/* check for an error from a previous send attempt */
if (ctx->error) {
return ctx->error;
}
/* read request */
rc = ktipc_recv(chan, sizeof(req), &req, sizeof(req));
if (rc < 0) {
TRACEF("%s: failed (%d) to receive msg for chan\n", __func__, rc);
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;
}
static int hwrng_handle_connect(const struct ktipc_port* port,
struct handle* chan,
const struct uuid* peer,
void** ctx_p) {
struct hwrng_chan_ctx* ctx = calloc(1, sizeof(*ctx));
if (!ctx) {
TRACEF("%s: failed to allocate context\n", __func__);
return ERR_NO_MEMORY;
}
ctx->chan = chan;
*ctx_p = ctx;
return NO_ERROR;
}
static void hwrng_handle_channel_cleanup(void* ctx_v) {
struct hwrng_chan_ctx* ctx = ctx_v;
if (list_in_list(&ctx->node)) {
list_delete(&ctx->node);
}
free(ctx);
}
static int hwrng_handle_send_unblocked(const struct ktipc_port* port,
struct handle* 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;
}
const static struct ktipc_srv_ops hwrng_srv_ops = {
.on_connect = hwrng_handle_connect,
.on_message = hwrng_handle_msg,
.on_channel_cleanup = hwrng_handle_channel_cleanup,
.on_send_unblocked = hwrng_handle_send_unblocked,
};
const static struct ktipc_port_acl hwrng_srv_port_acl = {
.flags = IPC_PORT_ALLOW_TA_CONNECT,
.uuids = NULL,
.uuid_num = 0,
.extra_data = NULL,
};
const static struct ktipc_port hwrng_srv_port = {
.name = HWRNG_SRV_NAME,
.uuid = &kernel_uuid,
.msg_max_size = MAX_HWRNG_MSG_SIZE,
.msg_queue_len = 1,
.acl = &hwrng_srv_port_acl,
.priv = NULL,
};
static struct ktipc_server hwrng_ktipc_server =
KTIPC_SERVER_INITIAL_VALUE(hwrng_ktipc_server, "hwrng_ktipc_server");
static void hwrng_ktipc_server_init(uint lvl) {
int rc;
rc = ktipc_server_start(&hwrng_ktipc_server);
if (rc < 0) {
panic("Failed (%d) to start hwrng server\n", rc);
}
rc = ktipc_server_add_port(&hwrng_ktipc_server, &hwrng_srv_port,
&hwrng_srv_ops);
if (rc < 0) {
panic("Failed (%d) to create hwrng service port\n", rc);
}
}
LK_INIT_HOOK(hwrng_ktipc_server_init,
hwrng_ktipc_server_init,
LK_INIT_LEVEL_APPS - 1)