Files
k-os/crates/drivers/rng/virtio/src/lib.rs

77 lines
2.6 KiB
Rust

//! virtio-rng (virtio spec 5.4): the device fills a buffer with entropy.
#![no_std]
#![forbid(unsafe_code)]
use hw_virtio::mmio::{MmioAccess, Registers};
use hw_virtio::queue::{Descriptor, VirtioQueue};
use libkos::device::Dma;
use port_entropy::Entropy;
use types::address::{ByteOffset, VirtualAddress};
use types::bounded::PageCount;
/// Drivers map their devices in the device window (0x01D0_0000 up, 64 KiB
/// each), clear of program images and heaps.
const MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x01D3_0000);
const VQUEUE_VA: VirtualAddress = VirtualAddress::from_raw(0x01D3_1000);
const BUFFER_VA: VirtualAddress = VirtualAddress::from_raw(0x01D3_3000);
/// Asked for at most this much at a time.
const CHUNK: usize = 256;
pub struct VirtioRng {
regs: Registers,
queue: VirtioQueue,
buffer: Dma,
}
impl VirtioRng {
/// Takes over the virtio-rng device behind `mmio`; `None` if it is not
/// one or any step fails.
pub fn new(mmio: types::bounded::CapSlot) -> Option<Self> {
let regs = Registers::new(libkos::device::map_mmio(mmio, MMIO_VA).ok()?);
if regs.read(hw_virtio::mmio::REG_MAGIC) != hw_virtio::mmio::MAGIC
|| regs.read(hw_virtio::mmio::REG_DEVICE_ID) != hw_virtio::mmio::DEVICE_ID_RNG
{
return None;
}
let rings = libkos::device::dma(PageCount::new(2)?, VQUEUE_VA).ok()?;
let buffer = libkos::device::dma(PageCount::new(1)?, BUFFER_VA).ok()?;
hw_virtio::mmio::init_legacy_device(
&regs,
rings.physical,
hw_virtio::queue::QUEUE_SIZE as u32,
);
Some(Self {
regs,
queue: VirtioQueue::new(rings.window),
buffer,
})
}
}
impl Entropy for VirtioRng {
fn fill(&mut self, buf: &mut [u8]) -> bool {
let mut done = 0;
while done < buf.len() {
let want = (buf.len() - done).min(CHUNK);
let desc = [Descriptor {
addr: self.buffer.physical.raw() as u64,
len: want as u32,
writable: true,
}];
let got = match self.queue.submit_written(&self.regs, &desc) {
Some(n) if n > 0 => (n as usize).min(want),
// The device gave nothing (or never answered): no randomness.
_ => return false,
};
self.buffer
.window
.copy_to(ByteOffset::ZERO, &mut buf[done..done + got]);
done += got;
}
// What was handed out is not left lying in the buffer.
self.buffer.window.fill_u32(ByteOffset::ZERO, CHUNK / 4, 0);
true
}
}