Skip to content

Commit

Permalink
Implement virtio-rng device
Browse files Browse the repository at this point in the history
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 #68.
  • Loading branch information
shengwen-tw committed Jan 29, 2025
1 parent e25b108 commit 989bc7d
Show file tree
Hide file tree
Showing 5 changed files with 360 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ ifeq ($(call has, VIRTIOBLK), 1)
endif
endif

# virtio-rng
ENABLE_VIRTIORNG ?= 1
$(call set-feature, VIRTIORNG)
ifeq ($(call has, VIRTIORNG), 1)
OBJS_EXTRA += virtio-rng.o
endif

NETDEV ?= tap
# virtio-net
ENABLE_VIRTIONET ?= 1
Expand Down
49 changes: 49 additions & 0 deletions device.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,52 @@ void virtio_blk_write(hart_t *vm,
uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file);
#endif /* SEMU_HAS(VIRTIOBLK) */

/* VirtIO-RNG */

#if SEMU_HAS(VIRTIORNG)

#define IRQ_VRNG 4
#define IRQ_VRNG_BIT (1 << IRQ_VRNG)

typedef struct {
uint32_t QueueNum;
uint32_t QueueDesc;
uint32_t QueueAvail;
uint32_t QueueUsed;
uint16_t last_avail;
bool ready;
} virtio_rng_queue_t;

typedef struct {
/* feature negotiation */
uint32_t DeviceFeaturesSel;
uint32_t DriverFeatures;
uint32_t DriverFeaturesSel;
/* queue config */
uint32_t QueueSel;
virtio_rng_queue_t queues[1];
/* status */
uint32_t Status;
uint32_t InterruptStatus;
/* supplied by environment */
uint32_t *ram;
} virtio_rng_state_t;

void virtio_rng_read(hart_t *vm,
virtio_rng_state_t *rng,
uint32_t addr,
uint8_t width,
uint32_t *value);

void virtio_rng_write(hart_t *vm,
virtio_rng_state_t *vrng,
uint32_t addr,
uint8_t width,
uint32_t value);

void virtio_rng_init(void);
#endif /* SEMU_HAS(VIRTIORNG) */

/* ACLINT MTIMER */
typedef struct {
/* A MTIMER device has two separate base addresses: one for the MTIME
Expand Down Expand Up @@ -272,6 +318,9 @@ typedef struct {
#endif
#if SEMU_HAS(VIRTIOBLK)
virtio_blk_state_t vblk;
#endif
#if SEMU_HAS(VIRTIORNG)
virtio_rng_state_t vrng;
#endif
/* ACLINT */
mtimer_state_t mtimer;
Expand Down
28 changes: 28 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,18 @@ static void emu_update_vblk_interrupts(vm_t *vm)
}
#endif

#if SEMU_HAS(VIRTIORNG)
static void emu_update_vrng_interrupts(vm_t *vm)
{
emu_state_t *data = PRIV(vm->hart[0]);
if (data->vrng.InterruptStatus)
data->plic.active |= IRQ_VRNG_BIT;
else
data->plic.active &= ~IRQ_VRNG_BIT;
plic_update_interrupts(vm, &data->plic);
}
#endif

static void emu_update_timer_interrupt(hart_t *hart)
{
emu_state_t *data = PRIV(hart);
Expand Down Expand Up @@ -137,6 +149,12 @@ static void mem_load(hart_t *hart,
aclint_sswi_read(hart, &data->sswi, addr & 0xFFFFF, width, value);
aclint_sswi_update_interrupts(hart, &data->sswi);
return;
#if SEMU_HAS(VIRTIORNG)
case 0x46: /* virtio-rng */
virtio_rng_read(hart, &data->vrng, addr & 0xFFFFF, width, value);
emu_update_vrng_interrupts(hart->vm);
return;
#endif
}
}
vm_set_exception(hart, RV_EXC_LOAD_FAULT, hart->exc_val);
Expand Down Expand Up @@ -191,6 +209,12 @@ static void mem_store(hart_t *hart,
aclint_sswi_write(hart, &data->sswi, addr & 0xFFFFF, width, value);
aclint_sswi_update_interrupts(hart, &data->sswi);
return;
#if SEMU_HAS(VIRTIORNG)
case 0x46: /* virtio-rng */
virtio_rng_write(hart, &data->vrng, addr & 0xFFFFF, width, value);
emu_update_vrng_interrupts(hart->vm);
return;
#endif
}
}
vm_set_exception(hart, RV_EXC_STORE_FAULT, hart->exc_val);
Expand Down Expand Up @@ -617,6 +641,10 @@ static int semu_start(int argc, char **argv)
#if SEMU_HAS(VIRTIOBLK)
emu.vblk.ram = emu.ram;
emu.disk = virtio_blk_init(&(emu.vblk), disk_file);
#endif
#if SEMU_HAS(VIRTIORNG)
emu.vrng.ram = emu.ram;
virtio_rng_init();
#endif
/* Set up ACLINT */
semu_timer_init(&emu.mtimer.mtime, CLOCK_FREQ);
Expand Down
8 changes: 8 additions & 0 deletions minimal.dts
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,13 @@
interrupts = <3>;
};
#endif

#if SEMU_FEATURE_VIRTIORNG
rng0: virtio@4600000 {
compatible = "virtio,mmio";
reg = <0x4600000 0x200>;
interrupts = <4>;
};
#endif
};
};
Loading

0 comments on commit 989bc7d

Please sign in to comment.