1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2014 Nahanni Systems Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer
12 *    in this position and unchanged.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/*
31 * virtio entropy device emulation.
32 * Randomness is sourced from /dev/random which does not block
33 * once it has been seeded at bootup.
34 */
35
36#include <sys/param.h>
37#ifndef WITHOUT_CAPSICUM
38#include <sys/capsicum.h>
39#endif
40#include <sys/linker_set.h>
41#include <sys/uio.h>
42
43#ifndef WITHOUT_CAPSICUM
44#include <capsicum_helpers.h>
45#endif
46#include <err.h>
47#include <errno.h>
48#include <fcntl.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
53#include <assert.h>
54#include <pthread.h>
55#include <sysexits.h>
56
57#include "bhyverun.h"
58#include "debug.h"
59#include "pci_emul.h"
60#include "virtio.h"
61
62#define VTRND_RINGSZ	64
63
64
65static int pci_vtrnd_debug;
66#define DPRINTF(params) if (pci_vtrnd_debug) PRINTLN params
67#define WPRINTF(params) PRINTLN params
68
69/*
70 * Per-device softc
71 */
72struct pci_vtrnd_softc {
73	struct virtio_softc vrsc_vs;
74	struct vqueue_info  vrsc_vq;
75	pthread_mutex_t     vrsc_mtx;
76	uint64_t            vrsc_cfg;
77	int                 vrsc_fd;
78};
79
80static void pci_vtrnd_reset(void *);
81static void pci_vtrnd_notify(void *, struct vqueue_info *);
82
83static struct virtio_consts vtrnd_vi_consts = {
84	.vc_name =	"vtrnd",
85	.vc_nvq =	1,
86	.vc_cfgsize =	0,
87	.vc_reset =	pci_vtrnd_reset,
88	.vc_qnotify =	pci_vtrnd_notify,
89	.vc_hv_caps =	0,
90};
91
92static void
93pci_vtrnd_reset(void *vsc)
94{
95	struct pci_vtrnd_softc *sc;
96
97	sc = vsc;
98
99	DPRINTF(("vtrnd: device reset requested !"));
100	vi_reset_dev(&sc->vrsc_vs);
101}
102
103
104static void
105pci_vtrnd_notify(void *vsc, struct vqueue_info *vq)
106{
107	struct iovec iov;
108	struct pci_vtrnd_softc *sc;
109	struct vi_req req;
110	int len, n;
111
112	sc = vsc;
113
114	if (sc->vrsc_fd < 0) {
115		vq_endchains(vq, 0);
116		return;
117	}
118
119	while (vq_has_descs(vq)) {
120		n = vq_getchain(vq, &iov, 1, &req);
121		assert(n == 1);
122
123		len = read(sc->vrsc_fd, iov.iov_base, iov.iov_len);
124
125		DPRINTF(("vtrnd: vtrnd_notify(): %d", len));
126
127		/* Catastrophe if unable to read from /dev/random */
128		assert(len > 0);
129
130		/*
131		 * Release this chain and handle more
132		 */
133		vq_relchain(vq, req.idx, len);
134	}
135	vq_endchains(vq, 1);	/* Generate interrupt if appropriate. */
136}
137
138
139static int
140pci_vtrnd_init(struct pci_devinst *pi, nvlist_t *nvl __unused)
141{
142	struct pci_vtrnd_softc *sc;
143	int fd;
144	int len;
145	uint8_t v;
146#ifndef WITHOUT_CAPSICUM
147	cap_rights_t rights;
148#endif
149
150	/*
151	 * Should always be able to open /dev/random.
152	 */
153	fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
154
155	assert(fd >= 0);
156
157#ifndef WITHOUT_CAPSICUM
158	cap_rights_init(&rights, CAP_READ);
159	if (caph_rights_limit(fd, &rights) == -1)
160		errx(EX_OSERR, "Unable to apply rights for sandbox");
161#endif
162
163	/*
164	 * Check that device is seeded and non-blocking.
165	 */
166	len = read(fd, &v, sizeof(v));
167	if (len <= 0) {
168		WPRINTF(("vtrnd: /dev/random not ready, read(): %d", len));
169		close(fd);
170		return (1);
171	}
172
173	sc = calloc(1, sizeof(struct pci_vtrnd_softc));
174
175	pthread_mutex_init(&sc->vrsc_mtx, NULL);
176
177	vi_softc_linkup(&sc->vrsc_vs, &vtrnd_vi_consts, sc, pi, &sc->vrsc_vq);
178	sc->vrsc_vs.vs_mtx = &sc->vrsc_mtx;
179
180	sc->vrsc_vq.vq_qsize = VTRND_RINGSZ;
181
182	/* keep /dev/random opened while emulating */
183	sc->vrsc_fd = fd;
184
185	/* initialize config space */
186	pci_set_cfgdata16(pi, PCIR_DEVICE, VIRTIO_DEV_RANDOM);
187	pci_set_cfgdata16(pi, PCIR_VENDOR, VIRTIO_VENDOR);
188	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_CRYPTO);
189	pci_set_cfgdata16(pi, PCIR_SUBDEV_0, VIRTIO_ID_ENTROPY);
190	pci_set_cfgdata16(pi, PCIR_SUBVEND_0, VIRTIO_VENDOR);
191
192	if (vi_intr_init(&sc->vrsc_vs, 1, fbsdrun_virtio_msix()))
193		return (1);
194	vi_set_io_bar(&sc->vrsc_vs, 0);
195
196	return (0);
197}
198
199
200static const struct pci_devemu pci_de_vrnd = {
201	.pe_emu =	"virtio-rnd",
202	.pe_init =	pci_vtrnd_init,
203	.pe_barwrite =	vi_pci_write,
204	.pe_barread =	vi_pci_read,
205#ifdef BHYVE_SNAPSHOT
206	.pe_snapshot =	vi_pci_snapshot,
207#endif
208};
209PCI_EMUL_SET(pci_de_vrnd);
210