Skip to content

Commit c2f267d

Browse files
committed
Implement virtio-rng device
This commit introduces the VirtIO entropy device (also know as virtio-rng in QEMU and the Linux kernel) to resolve the blocking issue of arc4random_buf() [1] caused by insufficient entropy for /dev/random. According to the man page (`man 7 random`): The kernel random-number generator relies on entropy gathered from device drivers and other sources of environmental noise to seed a cryptographically secure pseudorandom number generator (CSPRNG). Interface Pool: /dev/random Pool: Blocking pool Blocking behavior: If entropy too low, blocks until there is enough entropy Behavior when pool is not yet ready: Blocks until enough entropy gathered Quaoted from https://en.wikipedia.org/wiki//dev/random With Linux kernel 3.16 and newer, the kernel itself mixes data from hardware random number generators into /dev/random on a sliding scale based on the definable entropy estimation quality of the HWRNG. This means that no userspace daemon, such as rngd from rng-tools, is needed to do that job. With Linux kernel 3.17+, the VirtIO RNG was modified to have a default quality defined above 0, and as such, is currently the only HWRNG mixed into /dev/random by default. [1] https://elixir.bootlin.com/glibc/glibc-2.36/source/stdlib/arc4random.c Close #68.
1 parent e25b108 commit c2f267d

File tree

5 files changed

+360
-0
lines changed

5 files changed

+360
-0
lines changed

Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,13 @@ ifeq ($(call has, VIRTIOBLK), 1)
3131
endif
3232
endif
3333

34+
# virtio-rng
35+
ENABLE_VIRTIORNG ?= 1
36+
$(call set-feature, VIRTIORNG)
37+
ifeq ($(call has, VIRTIORNG), 1)
38+
OBJS_EXTRA += virtio-rng.o
39+
endif
40+
3441
NETDEV ?= tap
3542
# virtio-net
3643
ENABLE_VIRTIONET ?= 1

device.h

+49
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,52 @@ void virtio_blk_write(hart_t *vm,
172172
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
173173
#endif /* SEMU_HAS(VIRTIOBLK) */
174174

175+
/* VirtIO-RNG */
176+
177+
#if SEMU_HAS(VIRTIORNG)
178+
179+
#define IRQ_VRNG 4
180+
#define IRQ_VRNG_BIT (1 << IRQ_VRNG)
181+
182+
typedef struct {
183+
uint32_t QueueNum;
184+
uint32_t QueueDesc;
185+
uint32_t QueueAvail;
186+
uint32_t QueueUsed;
187+
uint16_t last_avail;
188+
bool ready;
189+
} virtio_rng_queue_t;
190+
191+
typedef struct {
192+
/* feature negotiation */
193+
uint32_t DeviceFeaturesSel;
194+
uint32_t DriverFeatures;
195+
uint32_t DriverFeaturesSel;
196+
/* queue config */
197+
uint32_t QueueSel;
198+
virtio_rng_queue_t queues[1];
199+
/* status */
200+
uint32_t Status;
201+
uint32_t InterruptStatus;
202+
/* supplied by environment */
203+
uint32_t *ram;
204+
} virtio_rng_state_t;
205+
206+
void virtio_rng_read(hart_t *vm,
207+
virtio_rng_state_t *rng,
208+
uint32_t addr,
209+
uint8_t width,
210+
uint32_t *value);
211+
212+
void virtio_rng_write(hart_t *vm,
213+
virtio_rng_state_t *vrng,
214+
uint32_t addr,
215+
uint8_t width,
216+
uint32_t value);
217+
218+
void virtio_rng_init(void);
219+
#endif /* SEMU_HAS(VIRTIORNG) */
220+
175221
/* ACLINT MTIMER */
176222
typedef struct {
177223
/* A MTIMER device has two separate base addresses: one for the MTIME
@@ -272,6 +318,9 @@ typedef struct {
272318
#endif
273319
#if SEMU_HAS(VIRTIOBLK)
274320
virtio_blk_state_t vblk;
321+
#endif
322+
#if SEMU_HAS(VIRTIORNG)
323+
virtio_rng_state_t vrng;
275324
#endif
276325
/* ACLINT */
277326
mtimer_state_t mtimer;

main.c

+28
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ static void emu_update_vblk_interrupts(vm_t *vm)
7272
}
7373
#endif
7474

75+
#if SEMU_HAS(VIRTIORNG)
76+
static void emu_update_vrng_interrupts(vm_t *vm)
77+
{
78+
emu_state_t *data = PRIV(vm->hart[0]);
79+
if (data->vrng.InterruptStatus)
80+
data->plic.active |= IRQ_VRNG_BIT;
81+
else
82+
data->plic.active &= ~IRQ_VRNG_BIT;
83+
plic_update_interrupts(vm, &data->plic);
84+
}
85+
#endif
86+
7587
static void emu_update_timer_interrupt(hart_t *hart)
7688
{
7789
emu_state_t *data = PRIV(hart);
@@ -137,6 +149,12 @@ static void mem_load(hart_t *hart,
137149
aclint_sswi_read(hart, &data->sswi, addr & 0xFFFFF, width, value);
138150
aclint_sswi_update_interrupts(hart, &data->sswi);
139151
return;
152+
#if SEMU_HAS(VIRTIORNG)
153+
case 0x46: /* virtio-rng */
154+
virtio_rng_read(hart, &data->vrng, addr & 0xFFFFF, width, value);
155+
emu_update_vrng_interrupts(hart->vm);
156+
return;
157+
#endif
140158
}
141159
}
142160
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);
@@ -191,6 +209,12 @@ static void mem_store(hart_t *hart,
191209
aclint_sswi_write(hart, &data->sswi, addr & 0xFFFFF, width, value);
192210
aclint_sswi_update_interrupts(hart, &data->sswi);
193211
return;
212+
#if SEMU_HAS(VIRTIORNG)
213+
case 0x46: /* virtio-rng */
214+
virtio_rng_write(hart, &data->vrng, addr & 0xFFFFF, width, value);
215+
emu_update_vrng_interrupts(hart->vm);
216+
return;
217+
#endif
194218
}
195219
}
196220
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
@@ -617,6 +641,10 @@ static int semu_start(int argc, char **argv)
617641
#if SEMU_HAS(VIRTIOBLK)
618642
emu.vblk.ram = emu.ram;
619643
emu.disk = virtio_blk_init(&(emu.vblk), disk_file);
644+
#endif
645+
#if SEMU_HAS(VIRTIORNG)
646+
emu.vrng.ram = emu.ram;
647+
virtio_rng_init();
620648
#endif
621649
/* Set up ACLINT */
622650
semu_timer_init(&emu.mtimer.mtime, CLOCK_FREQ);

minimal.dts

+8
Original file line numberDiff line numberDiff line change
@@ -63,5 +63,13 @@
6363
interrupts = <3>;
6464
};
6565
#endif
66+
67+
#if SEMU_FEATURE_VIRTIORNG
68+
rng0: virtio@4600000 {
69+
compatible = "virtio,mmio";
70+
reg = <0x4600000 0x200>;
71+
interrupts = <4>;
72+
};
73+
#endif
6674
};
6775
};

0 commit comments

Comments
 (0)