| /* |
| * 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 <inttypes.h> |
| #include <lk/list.h> |
| #include <stdint.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_TAG "hwrng_srv" |
| #include <trusty_log.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) { |
| 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; |
| } |
| } |
| |
| /* |
| * 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; |
| |
| TLOGD("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; |
| } |