drivers/char/hw_random/virtio-rng.c

Source file repositories/reference/linux-study-clean/drivers/char/hw_random/virtio-rng.c

File Facts

System
Linux kernel
Corpus path
drivers/char/hw_random/virtio-rng.c
Extension
.c
Size
6227 bytes
Lines
282
Domain
Driver Families
Bucket
drivers/char
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct virtrng_info {
	struct hwrng hwrng;
	struct virtqueue *vq;
	char name[25];
	int index;
	bool hwrng_register_done;
	bool hwrng_removed;
	/* data transfer */
	struct completion have_data;
	unsigned int data_avail;
	unsigned int data_idx;
	/* minimal size returned by rng_buffer_size() */
	__dma_from_device_group_begin();
#if SMP_CACHE_BYTES < 32
	u8 data[32];
#else
	u8 data[SMP_CACHE_BYTES];
#endif
	__dma_from_device_group_end();
};

static void random_recv_done(struct virtqueue *vq)
{
	struct virtrng_info *vi = vq->vdev->priv;
	unsigned int len;

	/* We can get spurious callbacks, e.g. shared IRQs + virtio_pci. */
	if (!virtqueue_get_buf(vi->vq, &len))
		return;

	smp_store_release(&vi->data_avail, len);
	complete(&vi->have_data);
}

static void request_entropy(struct virtrng_info *vi)
{
	struct scatterlist sg;

	reinit_completion(&vi->have_data);
	vi->data_idx = 0;

	sg_init_one(&sg, vi->data, sizeof(vi->data));

	/* There should always be room for one buffer. */
	virtqueue_add_inbuf(vi->vq, &sg, 1, vi->data, GFP_KERNEL);

	virtqueue_kick(vi->vq);
}

static unsigned int copy_data(struct virtrng_info *vi, void *buf,
			      unsigned int size)
{
	unsigned int idx, avail;

	/*
	 * vi->data_avail was set from the device-reported used.len and
	 * vi->data_idx was advanced by previous copy_data() calls.  A
	 * malicious or buggy virtio-rng backend can drive either past
	 * sizeof(vi->data).  Clamp at point of use and harden the index
	 * with array_index_nospec() so the memcpy() below cannot be
	 * steered into adjacent slab memory, including under
	 * speculation.
	 */
	avail = min_t(unsigned int, vi->data_avail, sizeof(vi->data));
	if (vi->data_idx >= avail) {
		vi->data_avail = 0;
		request_entropy(vi);
		return 0;
	}
	size = min_t(unsigned int, size, avail - vi->data_idx);
	idx = array_index_nospec(vi->data_idx, sizeof(vi->data));
	memcpy(buf, vi->data + idx, size);
	vi->data_idx += size;
	vi->data_avail -= size;
	if (vi->data_avail == 0)
		request_entropy(vi);
	return size;
}

static int virtio_read(struct hwrng *rng, void *buf, size_t size, bool wait)
{
	int ret;
	struct virtrng_info *vi = (struct virtrng_info *)rng->priv;
	unsigned int chunk;
	size_t read;

	if (vi->hwrng_removed)
		return -ENODEV;

	read = 0;

Annotation

Implementation Notes