77 lines
2.6 KiB
Rust
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(
|
|
®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
|
|
}
|
|
}
|