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