/* * 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 #include #include #include #include #include #include #include #include #include #include #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)