Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/virtio/random/virtio_random.c
Show First 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
#include <machine/resource.h> | #include <machine/resource.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <dev/random/randomdev.h> | #include <dev/random/randomdev.h> | ||||
#include <dev/random/random_harvestq.h> | #include <dev/random/random_harvestq.h> | ||||
#include <dev/virtio/virtio.h> | #include <dev/virtio/virtio.h> | ||||
#include <dev/virtio/virtqueue.h> | #include <dev/virtio/virtqueue.h> | ||||
struct vtrnd_harvest_buf { | |||||
uint32_t value[HARVESTSIZE] __aligned(sizeof(uint32_t) * HARVESTSIZE); | |||||
struct sglist_seg segs[1]; | |||||
struct sglist sg; | |||||
}; | |||||
struct vtrnd_softc { | struct vtrnd_softc { | ||||
device_t vtrnd_dev; | device_t vtrnd_dev; | ||||
struct vtrnd_harvest_buf vtrnd_harvest_buf; | |||||
uint64_t vtrnd_features; | uint64_t vtrnd_features; | ||||
struct virtqueue *vtrnd_vq; | struct virtqueue *vtrnd_vq; | ||||
}; | }; | ||||
static int vtrnd_modevent(module_t, int, void *); | static int vtrnd_modevent(module_t, int, void *); | ||||
static int vtrnd_probe(device_t); | static int vtrnd_probe(device_t); | ||||
static int vtrnd_attach(device_t); | static int vtrnd_attach(device_t); | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
return (VIRTIO_SIMPLE_PROBE(dev, virtio_random)); | return (VIRTIO_SIMPLE_PROBE(dev, virtio_random)); | ||||
} | } | ||||
static int | static int | ||||
vtrnd_attach(device_t dev) | vtrnd_attach(device_t dev) | ||||
{ | { | ||||
struct vtrnd_softc *sc, *exp; | struct vtrnd_softc *sc, *exp; | ||||
struct vtrnd_harvest_buf *buf; | |||||
int error; | int error; | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
sc->vtrnd_dev = dev; | sc->vtrnd_dev = dev; | ||||
buf = &sc->vtrnd_harvest_buf; | |||||
virtio_set_feature_desc(dev, vtrnd_feature_desc); | virtio_set_feature_desc(dev, vtrnd_feature_desc); | ||||
error = vtrnd_setup_features(sc); | error = vtrnd_setup_features(sc); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "cannot setup features\n"); | device_printf(dev, "cannot setup features\n"); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
error = vtrnd_alloc_virtqueue(sc); | error = vtrnd_alloc_virtqueue(sc); | ||||
if (error) { | if (error) { | ||||
device_printf(dev, "cannot allocate virtqueue\n"); | device_printf(dev, "cannot allocate virtqueue\n"); | ||||
goto fail; | goto fail; | ||||
} | } | ||||
exp = NULL; | exp = NULL; | ||||
if (!atomic_compare_exchange_strong_explicit(&g_vtrnd_softc, &exp, sc, | if (!atomic_compare_exchange_strong_explicit(&g_vtrnd_softc, &exp, sc, | ||||
memory_order_release, memory_order_acquire)) { | memory_order_release, memory_order_acquire)) { | ||||
error = EEXIST; | error = EEXIST; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
_Static_assert(sizeof(buf->value) < PAGE_SIZE, "sglist assumption"); | |||||
sglist_init(&buf->sg, 1, buf->segs); | |||||
error = sglist_append(&buf->sg, buf->value, sizeof(buf->value)); | |||||
if (error != 0) | |||||
panic("%s: sglist_append error=%d", __func__, error); | |||||
random_source_register(&random_vtrnd); | random_source_register(&random_vtrnd); | ||||
fail: | fail: | ||||
if (error) | if (error) | ||||
vtrnd_detach(dev); | vtrnd_detach(dev); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | vtrnd_alloc_virtqueue(struct vtrnd_softc *sc) | ||||
dev = sc->vtrnd_dev; | dev = sc->vtrnd_dev; | ||||
VQ_ALLOC_INFO_INIT(&vq_info, 0, NULL, sc, &sc->vtrnd_vq, | VQ_ALLOC_INFO_INIT(&vq_info, 0, NULL, sc, &sc->vtrnd_vq, | ||||
"%s request", device_get_nameunit(dev)); | "%s request", device_get_nameunit(dev)); | ||||
return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info)); | return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info)); | ||||
} | } | ||||
#define VTRND_HARVEST_MAXWAIT (SBT_1MS * 2) | |||||
static int | static int | ||||
vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz) | vtrnd_harvest(struct vtrnd_softc *sc, void *buf, size_t *sz) | ||||
{ | { | ||||
struct sglist_seg segs[1]; | struct vtrnd_harvest_buf *hbuf; | ||||
struct sglist sg; | |||||
struct virtqueue *vq; | struct virtqueue *vq; | ||||
void *cookie; | void *cookie; | ||||
uint32_t value[HARVESTSIZE] __aligned(sizeof(uint32_t) * HARVESTSIZE); | |||||
uint32_t rdlen; | uint32_t rdlen; | ||||
int error; | int error; | ||||
_Static_assert(sizeof(value) < PAGE_SIZE, "sglist assumption"); | |||||
sglist_init(&sg, 1, segs); | |||||
error = sglist_append(&sg, value, *sz); | |||||
if (error != 0) | |||||
panic("%s: sglist_append error=%d", __func__, error); | |||||
vq = sc->vtrnd_vq; | vq = sc->vtrnd_vq; | ||||
KASSERT(virtqueue_empty(vq), ("%s: non-empty queue", __func__)); | hbuf = &sc->vtrnd_harvest_buf; | ||||
error = virtqueue_enqueue(vq, buf, &sg, 0, 1); | /* | ||||
* The host provider may have ratelimited us, causing the last poll | |||||
* attempt to fail. Discard the result and try again. Perhaps we could | |||||
* have used it, but it's safer to discard it after it's been sitting in | |||||
* memory for so long in the same allocation as the softc. The odds of | |||||
* it having been observed seem too high to risk it. | |||||
cem: This doesn’t make sense starting at “after it’s” - ie just end the sentence at “safer to… | |||||
*/ | |||||
if (!virtqueue_empty(vq)) { | |||||
cookie = virtqueue_poll_timeout(vq, NULL, | |||||
VTRND_HARVEST_MAXWAIT); | |||||
/* We could still be blocked. */ | |||||
if (cookie == NULL) | |||||
return (EAGAIN); | |||||
} | |||||
error = virtqueue_enqueue(vq, hbuf, &hbuf->sg, 0, 1); | |||||
if (error != 0) | if (error != 0) | ||||
return (error); | return (error); | ||||
/* | /* | ||||
* Poll for the response, but the command is likely already | * Poll for the response, but the command is likely already | ||||
* done when we return from the notify. | * done when we return from the notify. | ||||
*/ | */ | ||||
virtqueue_notify(vq); | virtqueue_notify(vq); | ||||
cookie = virtqueue_poll(vq, &rdlen); | cookie = virtqueue_poll_timeout(vq, &rdlen, VTRND_HARVEST_MAXWAIT); | ||||
/* Almost trivially true, but could indicate something bad. */ | /* Almost trivially true, but could indicate something bad. */ | ||||
KASSERT(cookie == buf, ("inconsistent virtqueue state")); | KASSERT(cookie == NULL || cookie == hbuf, | ||||
("inconsistent virtqueue state")); | |||||
if (cookie == NULL) | |||||
return (EAGAIN); | |||||
if (rdlen > *sz) | if (rdlen > *sz) | ||||
panic("%s: random device wrote %zu bytes beyond end of provided" | panic("%s: random device wrote %zu bytes beyond end of provided" | ||||
" buffer %p:%zu", __func__, (size_t)rdlen - *sz, | " buffer %p:%zu", __func__, (size_t)rdlen - *sz, | ||||
(void *)value, *sz); | (void *)hbuf->value, *sz); | ||||
else if (rdlen == 0) | else if (rdlen == 0) | ||||
return (EAGAIN); | return (EAGAIN); | ||||
*sz = MIN(rdlen, *sz); | *sz = MIN(rdlen, *sz); | ||||
memcpy(buf, value, *sz); | memcpy(buf, hbuf->value, *sz); | ||||
explicit_bzero(value, *sz); | explicit_bzero(hbuf->value, *sz); | ||||
return (0); | return (0); | ||||
} | } | ||||
static unsigned | static unsigned | ||||
vtrnd_read(void *buf, unsigned usz) | vtrnd_read(void *buf, unsigned usz) | ||||
{ | { | ||||
struct vtrnd_softc *sc; | struct vtrnd_softc *sc; | ||||
size_t sz; | size_t sz; | ||||
Show All 13 Lines |
This doesn’t make sense starting at “after it’s” - ie just end the sentence at “safer to discard it.”