//! 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 { 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( ®s, 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 } }