1264055Sgrehan/*- 2264055Sgrehan * Copyright (c) 2014 Nahanni Systems Inc. 3264055Sgrehan * All rights reserved. 4264055Sgrehan * 5264055Sgrehan * Redistribution and use in source and binary forms, with or without 6264055Sgrehan * modification, are permitted provided that the following conditions 7264055Sgrehan * are met: 8264055Sgrehan * 1. Redistributions of source code must retain the above copyright 9264055Sgrehan * notice, this list of conditions and the following disclaimer 10264055Sgrehan * in this position and unchanged. 11264055Sgrehan * 2. Redistributions in binary form must reproduce the above copyright 12264055Sgrehan * notice, this list of conditions and the following disclaimer in the 13264055Sgrehan * documentation and/or other materials provided with the distribution. 14264055Sgrehan * 15264055Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16264055Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17264055Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18264055Sgrehan * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19264055Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20264055Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21264055Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22264055Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23264055Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24264055Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25264055Sgrehan * SUCH DAMAGE. 26264055Sgrehan */ 27264055Sgrehan 28264055Sgrehan/* 29264055Sgrehan * virtio entropy device emulation. 30264055Sgrehan * Randomness is sourced from /dev/random which does not block 31264055Sgrehan * once it has been seeded at bootup. 32264055Sgrehan */ 33264055Sgrehan 34264055Sgrehan#include <sys/cdefs.h> 35264055Sgrehan__FBSDID("$FreeBSD$"); 36264055Sgrehan 37264055Sgrehan#include <sys/param.h> 38264055Sgrehan#include <sys/linker_set.h> 39264055Sgrehan#include <sys/uio.h> 40264055Sgrehan 41264055Sgrehan#include <errno.h> 42264055Sgrehan#include <fcntl.h> 43264055Sgrehan#include <stdio.h> 44264055Sgrehan#include <stdlib.h> 45264055Sgrehan#include <string.h> 46264055Sgrehan#include <unistd.h> 47264055Sgrehan#include <assert.h> 48264055Sgrehan#include <pthread.h> 49264055Sgrehan 50264055Sgrehan#include "bhyverun.h" 51264055Sgrehan#include "pci_emul.h" 52264055Sgrehan#include "virtio.h" 53264055Sgrehan 54264055Sgrehan#define VTRND_RINGSZ 64 55264055Sgrehan 56264055Sgrehan 57264055Sgrehanstatic int pci_vtrnd_debug; 58264055Sgrehan#define DPRINTF(params) if (pci_vtrnd_debug) printf params 59264055Sgrehan#define WPRINTF(params) printf params 60264055Sgrehan 61264055Sgrehan/* 62264055Sgrehan * Per-device softc 63264055Sgrehan */ 64264055Sgrehanstruct pci_vtrnd_softc { 65264055Sgrehan struct virtio_softc vrsc_vs; 66264055Sgrehan struct vqueue_info vrsc_vq; 67264055Sgrehan pthread_mutex_t vrsc_mtx; 68264055Sgrehan uint64_t vrsc_cfg; 69264055Sgrehan int vrsc_fd; 70264055Sgrehan}; 71264055Sgrehan 72264055Sgrehanstatic void pci_vtrnd_reset(void *); 73264055Sgrehanstatic void pci_vtrnd_notify(void *, struct vqueue_info *); 74264055Sgrehan 75264055Sgrehanstatic struct virtio_consts vtrnd_vi_consts = { 76264055Sgrehan "vtrnd", /* our name */ 77264055Sgrehan 1, /* we support 1 virtqueue */ 78264055Sgrehan 0, /* config reg size */ 79264055Sgrehan pci_vtrnd_reset, /* reset */ 80264055Sgrehan pci_vtrnd_notify, /* device-wide qnotify */ 81264055Sgrehan NULL, /* read virtio config */ 82264055Sgrehan NULL, /* write virtio config */ 83271685Sgrehan NULL, /* apply negotiated features */ 84264055Sgrehan 0, /* our capabilities */ 85264055Sgrehan}; 86264055Sgrehan 87264055Sgrehan 88264055Sgrehanstatic void 89264055Sgrehanpci_vtrnd_reset(void *vsc) 90264055Sgrehan{ 91264055Sgrehan struct pci_vtrnd_softc *sc; 92264055Sgrehan 93264055Sgrehan sc = vsc; 94264055Sgrehan 95264055Sgrehan DPRINTF(("vtrnd: device reset requested !\n")); 96264055Sgrehan vi_reset_dev(&sc->vrsc_vs); 97264055Sgrehan} 98264055Sgrehan 99264055Sgrehan 100264055Sgrehanstatic void 101264055Sgrehanpci_vtrnd_notify(void *vsc, struct vqueue_info *vq) 102264055Sgrehan{ 103264055Sgrehan struct iovec iov; 104264055Sgrehan struct pci_vtrnd_softc *sc; 105264055Sgrehan int len; 106264055Sgrehan 107264055Sgrehan sc = vsc; 108264055Sgrehan 109264055Sgrehan vq_startchains(vq); 110264055Sgrehan 111264055Sgrehan if (sc->vrsc_fd < 0) { 112264055Sgrehan vq_endchains(vq, 0); 113264055Sgrehan return; 114264055Sgrehan } 115264055Sgrehan 116264055Sgrehan while (vq_has_descs(vq)) { 117264055Sgrehan vq_getchain(vq, &iov, 1, NULL); 118264055Sgrehan 119264055Sgrehan len = read(sc->vrsc_fd, iov.iov_base, iov.iov_len); 120264055Sgrehan 121264055Sgrehan DPRINTF(("vtrnd: vtrnd_notify(): %d\r\n", len)); 122264055Sgrehan 123264055Sgrehan /* Catastrophe if unable to read from /dev/random */ 124264055Sgrehan assert(len > 0); 125264055Sgrehan 126264055Sgrehan /* 127264055Sgrehan * Release this chain and handle more 128264055Sgrehan */ 129264055Sgrehan vq_relchain(vq, len); 130264055Sgrehan } 131264055Sgrehan vq_endchains(vq, 1); /* Generate interrupt if appropriate. */ 132264055Sgrehan} 133264055Sgrehan 134264055Sgrehan 135264055Sgrehanstatic int 136264055Sgrehanpci_vtrnd_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) 137264055Sgrehan{ 138264055Sgrehan struct pci_vtrnd_softc *sc; 139264055Sgrehan int fd; 140264055Sgrehan int len; 141264055Sgrehan uint8_t v; 142264055Sgrehan 143264055Sgrehan /* 144264055Sgrehan * Should always be able to open /dev/random. 145264055Sgrehan */ 146264055Sgrehan fd = open("/dev/random", O_RDONLY | O_NONBLOCK); 147264055Sgrehan 148264055Sgrehan assert(fd >= 0); 149264055Sgrehan 150264055Sgrehan /* 151264055Sgrehan * Check that device is seeded and non-blocking. 152264055Sgrehan */ 153264055Sgrehan len = read(fd, &v, sizeof(v)); 154264055Sgrehan if (len <= 0) { 155264055Sgrehan WPRINTF(("vtrnd: /dev/random not ready, read(): %d", len)); 156264055Sgrehan return (1); 157264055Sgrehan } 158264055Sgrehan 159268953Sjhb sc = calloc(1, sizeof(struct pci_vtrnd_softc)); 160264055Sgrehan 161264055Sgrehan vi_softc_linkup(&sc->vrsc_vs, &vtrnd_vi_consts, sc, pi, &sc->vrsc_vq); 162264055Sgrehan sc->vrsc_vs.vs_mtx = &sc->vrsc_mtx; 163264055Sgrehan 164264055Sgrehan sc->vrsc_vq.vq_qsize = VTRND_RINGSZ; 165264055Sgrehan 166264055Sgrehan /* keep /dev/random opened while emulating */ 167264055Sgrehan sc->vrsc_fd = fd; 168264055Sgrehan 169264055Sgrehan /* initialize config space */ 170264055Sgrehan pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_RANDOM); 171264055Sgrehan pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR); 172264055Sgrehan pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_CRYPTO); 173264055Sgrehan pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_TYPE_ENTROPY); 174264055Sgrehan 175264055Sgrehan if (vi_intr_init(&sc->vrsc_vs, 1, fbsdrun_virtio_msix())) 176264055Sgrehan return (1); 177264055Sgrehan vi_set_io_bar(&sc->vrsc_vs, 0); 178264055Sgrehan 179264055Sgrehan return (0); 180264055Sgrehan} 181264055Sgrehan 182264055Sgrehan 183264055Sgrehanstruct pci_devemu pci_de_vrnd = { 184264055Sgrehan .pe_emu = "virtio-rnd", 185264055Sgrehan .pe_init = pci_vtrnd_init, 186264055Sgrehan .pe_barwrite = vi_pci_write, 187264055Sgrehan .pe_barread = vi_pci_read 188264055Sgrehan}; 189264055SgrehanPCI_EMUL_SET(pci_de_vrnd); 190