Skip to content

Commit fe95c9c

Browse files
committed
Implement virtio-rng device
This commit introduces the VirtIO entropy device, also known as virtio-rng in QEMU or the Linux kernel. Randomness is a precious resource in the system. Without sufficient entropy, functions like arc4random_buf() in the standard C library may not work properly since they rely on the blocking device /dev/random. Quaoted from: https://en.wikipedia.org/wiki//dev/random /dev/random typically blocked if there was less entropy available than requested; more recently (see below for the differences between operating systems) it usually blocks at startup until sufficient entropy has been gathered, then unblocks permanently. 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. Close sysprog21#68.
1 parent e25b108 commit fe95c9c

File tree

5 files changed

+360
-0
lines changed

5 files changed

+360
-0
lines changed

Makefile

Lines changed: 7 additions & 0 deletions
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

Lines changed: 49 additions & 0 deletions
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(virtio_rng_state_t *vrng);
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

Lines changed: 28 additions & 0 deletions
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(&(emu.vrng));
620648
#endif
621649
/* Set up ACLINT */
622650
semu_timer_init(&emu.mtimer.mtime, CLOCK_FREQ);

minimal.dts

Lines changed: 8 additions & 0 deletions
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)