diff --git a/Cargo.lock b/Cargo.lock index a3f72df075..bc21a5ae49 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,12 +55,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cee078b3ed87e2621e0389729e2f032cc0408a4548768ced01678c9451c62237" -[[package]] -name = "allocator-api2" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c583acf993cf4245c4acb0a2cc2ab1f9cc097de73411bb6d3647ff6af2b1013d" - [[package]] name = "allocator-api2" version = "0.4.0" @@ -225,9 +219,9 @@ checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" [[package]] name = "bitfield-struct" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3ca019570363e800b05ad4fd890734f28ac7b72f563ad8a35079efb793616f8" +checksum = "8769c4854c5ada2852ddf6fd09d15cf43d4c2aaeccb4de6432f5402f08a6003b" dependencies = [ "proc-macro2", "quote", @@ -1844,7 +1838,7 @@ version = "5.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a5888b3189a89207eea4190e74387b5db3150d213f84186cac637dcd55259d" dependencies = [ - "allocator-api2 0.4.0", + "allocator-api2", "lock_api", ] @@ -2015,10 +2009,9 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "virtio-spec" version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd2f32d50a7e480738d6e43ea2048cd1c34cc33362e55d87d1eebd02bd2f563f" +source = "git+https://github.com/sbutz/virtio-spec-rs?branch=sb%2Fentropy#f1f9a52153ccfa185181a4ec63287d2de4662504" dependencies = [ - "allocator-api2 0.3.1", + "allocator-api2", "bitfield-struct", "bitflags 2.13.0", "endian-num", diff --git a/Cargo.toml b/Cargo.toml index 21a2ead30b..ccb22ebe70 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -206,6 +206,9 @@ virtio-console = ["virtio"] ## Enables the virtio-fs driver. virtio-fs = ["virtio", "dep:fuse-abi", "fuse-abi/num_enum"] +## Enables the virtio-entropy driver. +virtio-entropy = ["virtio"] + ## Enables the virtio-vsock driver. virtio-vsock = ["virtio"] @@ -310,7 +313,7 @@ workspace = true [dependencies] hermit-macro = { version = "=0.1.0", path = "hermit-macro" } -virtio = { package = "virtio-spec", version = "0.3", optional = true, features = ["alloc", "mmio", "nightly", "zerocopy"] } +virtio = { package = "virtio-spec", git = "https://github.com/sbutz/virtio-spec-rs", branch = "sb/entropy", optional = true, features = ["alloc", "mmio", "nightly", "zerocopy"] } ahash = { version = "0.8", default-features = false } align-address = "0.4" anstyle = { version = "1", default-features = false } diff --git a/src/arch/aarch64/kernel/mmio.rs b/src/arch/aarch64/kernel/mmio.rs index 4c6730ffe8..97aecf4271 100644 --- a/src/arch/aarch64/kernel/mmio.rs +++ b/src/arch/aarch64/kernel/mmio.rs @@ -5,6 +5,7 @@ use align_address::Align; use arm_gic::{IntId, Trigger}; #[cfg(any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-vsock", ))] @@ -22,6 +23,8 @@ use crate::drivers::InterruptHandlerMap; use crate::drivers::console::VirtioConsoleDriver; #[cfg(feature = "virtio-console")] use crate::drivers::console::VirtioUART; +#[cfg(feature = "virtio-entropy")] +use crate::drivers::entropy::VirtioEntropyDriver; #[cfg(feature = "virtio-fs")] use crate::drivers::fs::VirtioFsDriver; #[cfg(feature = "virtio-net")] @@ -29,6 +32,7 @@ use crate::drivers::net::virtio::VirtioNetDriver; use crate::drivers::virtio::transport::mmio as mmio_virtio; #[cfg(any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-net", feature = "virtio-vsock", @@ -47,6 +51,8 @@ pub(crate) static MMIO_DRIVERS: InitCell> = InitCell::new(Vec::n pub(crate) enum MmioDriver { #[cfg(feature = "virtio-console")] VirtioConsole(InterruptTicketMutex), + #[cfg(feature = "virtio-entropy")] + VirtioEntropy(InterruptTicketMutex), #[cfg(feature = "virtio-fs")] VirtioFs(InterruptTicketMutex), #[cfg(feature = "virtio-vsock")] @@ -63,6 +69,15 @@ impl MmioDriver { } } + #[cfg(feature = "virtio-entropy")] + fn get_entropy_driver(&self) -> Option<&InterruptTicketMutex> { + #[allow(unreachable_patterns)] + match self { + Self::VirtioEntropy(drv) => Some(drv), + _ => None, + } + } + #[cfg(feature = "virtio-fs")] fn get_filesystem_driver(&self) -> Option<&InterruptTicketMutex> { #[allow(unreachable_patterns)] @@ -84,6 +99,7 @@ impl MmioDriver { #[cfg(any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-vsock" ))] @@ -102,6 +118,14 @@ pub(crate) fn get_console_driver() -> Option<&'static InterruptTicketMutex Option<&'static InterruptTicketMutex> { + MMIO_DRIVERS + .get()? + .iter() + .find_map(|drv| drv.get_entropy_driver()) +} + #[cfg(feature = "virtio-fs")] pub(crate) fn get_filesystem_driver() -> Option<&'static InterruptTicketMutex> { MMIO_DRIVERS @@ -234,6 +258,10 @@ pub fn init_drivers(handlers: &mut InterruptHandlerMap) { VirtioDriver::Console(drv) => register_driver(MmioDriver::VirtioConsole( InterruptTicketMutex::new(*drv), )), + #[cfg(feature = "virtio-entropy")] + VirtioDriver::Entropy(drv) => register_driver(MmioDriver::VirtioEntropy( + InterruptTicketMutex::new(*drv), + )), #[cfg(feature = "virtio-fs")] VirtioDriver::Fs(drv) => { register_driver(MmioDriver::VirtioFs(InterruptTicketMutex::new(*drv))); diff --git a/src/arch/aarch64/kernel/processor.rs b/src/arch/aarch64/kernel/processor.rs index 3a62f128a7..53bf7b899f 100644 --- a/src/arch/aarch64/kernel/processor.rs +++ b/src/arch/aarch64/kernel/processor.rs @@ -196,6 +196,7 @@ impl fmt::Display for CpuFrequency { } } +#[cfg_attr(feature = "virtio-entropy", allow(dead_code))] pub fn seed_entropy() -> Option<[u8; 32]> { None } diff --git a/src/arch/riscv64/kernel/devicetree.rs b/src/arch/riscv64/kernel/devicetree.rs index 3b795c996e..98c3f7c169 100644 --- a/src/arch/riscv64/kernel/devicetree.rs +++ b/src/arch/riscv64/kernel/devicetree.rs @@ -13,6 +13,7 @@ use crate::arch::riscv64::kernel::interrupts::init_plic; #[cfg(all( any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-vsock", ), @@ -36,6 +37,7 @@ use crate::drivers::virtio::transport::mmio as mmio_virtio; #[cfg(all( any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-net", feature = "virtio-vsock", @@ -49,6 +51,7 @@ use crate::executor::device::NETWORK_DEVICE; #[cfg(all( any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-vsock", ), @@ -264,6 +267,12 @@ pub fn init_drivers(handlers: &mut InterruptHandlerMap) { hermit_sync::InterruptSpinMutex::new(*drv), )); } + #[cfg(feature = "virtio-entropy")] + Ok(VirtioDriver::Entropy(drv)) => { + register_driver(MmioDriver::VirtioEntropy( + hermit_sync::InterruptSpinMutex::new(*drv), + )); + } #[cfg(feature = "virtio-fs")] Ok(VirtioDriver::Fs(drv)) => { register_driver(MmioDriver::VirtioFs( diff --git a/src/arch/riscv64/kernel/mmio.rs b/src/arch/riscv64/kernel/mmio.rs index 67928a388e..8dc6edd540 100644 --- a/src/arch/riscv64/kernel/mmio.rs +++ b/src/arch/riscv64/kernel/mmio.rs @@ -2,6 +2,7 @@ use alloc::vec::Vec; #[cfg(any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-vsock", ))] @@ -9,6 +10,8 @@ use hermit_sync::InterruptSpinMutex; #[cfg(feature = "virtio-console")] use crate::drivers::console::VirtioConsoleDriver; +#[cfg(feature = "virtio-entropy")] +use crate::drivers::entropy::VirtioEntropyDriver; #[cfg(feature = "virtio-fs")] use crate::drivers::fs::VirtioFsDriver; #[cfg(feature = "gem-net")] @@ -25,6 +28,8 @@ pub(crate) static MMIO_DRIVERS: InitCell> = InitCell::new(Vec::n pub(crate) enum MmioDriver { #[cfg(feature = "virtio-console")] VirtioConsole(InterruptSpinMutex), + #[cfg(feature = "virtio-entropy")] + VirtioEntropy(InterruptSpinMutex), #[cfg(feature = "virtio-fs")] VirtioFs(InterruptSpinMutex), #[cfg(feature = "virtio-vsock")] @@ -41,6 +46,15 @@ impl MmioDriver { } } + #[cfg(feature = "virtio-entropy")] + fn get_entropy_driver(&self) -> Option<&InterruptSpinMutex> { + #[allow(unreachable_patterns)] + match self { + Self::VirtioEntropy(drv) => Some(drv), + _ => None, + } + } + #[cfg(feature = "virtio-fs")] fn get_filesystem_driver(&self) -> Option<&InterruptSpinMutex> { #[allow(unreachable_patterns)] @@ -62,6 +76,7 @@ impl MmioDriver { #[cfg(any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-vsock", ))] @@ -83,6 +98,14 @@ pub(crate) fn get_console_driver() -> Option<&'static InterruptSpinMutex Option<&'static InterruptSpinMutex> { + MMIO_DRIVERS + .get()? + .iter() + .find_map(|drv| drv.get_entropy_driver()) +} + #[cfg(feature = "virtio-fs")] pub(crate) fn get_filesystem_driver() -> Option<&'static InterruptSpinMutex> { MMIO_DRIVERS diff --git a/src/arch/riscv64/kernel/processor.rs b/src/arch/riscv64/kernel/processor.rs index d6cfd90aef..62cc743138 100644 --- a/src/arch/riscv64/kernel/processor.rs +++ b/src/arch/riscv64/kernel/processor.rs @@ -208,6 +208,7 @@ impl FPUState { } } +#[cfg_attr(feature = "virtio-entropy", allow(dead_code))] pub fn seed_entropy() -> Option<[u8; 32]> { None } diff --git a/src/arch/x86_64/kernel/mmio.rs b/src/arch/x86_64/kernel/mmio.rs index 50bc6871fb..1946827938 100644 --- a/src/arch/x86_64/kernel/mmio.rs +++ b/src/arch/x86_64/kernel/mmio.rs @@ -7,6 +7,7 @@ use align_address::Align; use free_list::{PageLayout, PageRange}; #[cfg(any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-vsock", ))] @@ -23,6 +24,8 @@ use crate::arch::x86_64::mm::paging::{ use crate::drivers::InterruptHandlerMap; #[cfg(feature = "virtio-console")] use crate::drivers::console::VirtioConsoleDriver; +#[cfg(feature = "virtio-entropy")] +use crate::drivers::entropy::VirtioEntropyDriver; #[cfg(feature = "virtio-fs")] use crate::drivers::fs::VirtioFsDriver; #[cfg(feature = "virtio-net")] @@ -30,6 +33,7 @@ use crate::drivers::net::virtio::VirtioNetDriver; use crate::drivers::virtio::transport::mmio as mmio_virtio; #[cfg(any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-net", feature = "virtio-vsock", @@ -51,6 +55,8 @@ static MMIO_DRIVERS: InitCell> = InitCell::new(Vec::new()); pub(crate) enum MmioDriver { #[cfg(feature = "virtio-console")] VirtioConsole(InterruptTicketMutex), + #[cfg(feature = "virtio-entropy")] + VirtioEntropy(InterruptTicketMutex), #[cfg(feature = "virtio-fs")] VirtioFs(InterruptTicketMutex), #[cfg(feature = "virtio-vsock")] @@ -67,6 +73,15 @@ impl MmioDriver { } } + #[cfg(feature = "virtio-entropy")] + fn get_entropy_driver(&self) -> Option<&InterruptTicketMutex> { + #[allow(unreachable_patterns)] + match self { + Self::VirtioEntropy(drv) => Some(drv), + _ => None, + } + } + #[cfg(feature = "virtio-fs")] fn get_filesystem_driver(&self) -> Option<&InterruptTicketMutex> { #[allow(unreachable_patterns)] @@ -184,6 +199,7 @@ fn guess_device( #[cfg(any( feature = "virtio-console", + feature = "virtio-entropy", feature = "virtio-fs", feature = "virtio-vsock", ))] @@ -202,6 +218,14 @@ pub(crate) fn get_console_driver() -> Option<&'static InterruptTicketMutex Option<&'static InterruptTicketMutex> { + MMIO_DRIVERS + .get()? + .iter() + .find_map(|drv| drv.get_entropy_driver()) +} + #[cfg(feature = "virtio-fs")] pub(crate) fn get_filesystem_driver() -> Option<&'static InterruptTicketMutex> { MMIO_DRIVERS @@ -228,6 +252,10 @@ fn register_mmio( Ok(VirtioDriver::Console(drv)) => { register_driver(MmioDriver::VirtioConsole(InterruptTicketMutex::new(*drv))); } + #[cfg(feature = "virtio-entropy")] + Ok(VirtioDriver::Entropy(drv)) => { + register_driver(MmioDriver::VirtioEntropy(InterruptTicketMutex::new(*drv))); + } #[cfg(feature = "virtio-fs")] Ok(VirtioDriver::Fs(drv)) => { register_driver(MmioDriver::VirtioFs(InterruptTicketMutex::new(*drv))); diff --git a/src/arch/x86_64/kernel/processor.rs b/src/arch/x86_64/kernel/processor.rs index 0915d6365c..b02c00ba4e 100644 --- a/src/arch/x86_64/kernel/processor.rs +++ b/src/arch/x86_64/kernel/processor.rs @@ -962,6 +962,7 @@ pub fn print_information() { infofooter!(); } +#[cfg_attr(feature = "virtio-entropy", allow(dead_code))] pub fn seed_entropy() -> Option<[u8; 32]> { let mut buf = [0; 32]; diff --git a/src/config.rs b/src/config.rs index 5f5cb52326..f20ff54240 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,3 +17,6 @@ pub(crate) const VSOCK_PACKET_SIZE: u32 = 8192; #[cfg(feature = "virtio-console")] pub(crate) const CONSOLE_PACKET_SIZE: u32 = 8192; + +#[cfg(feature = "virtio-entropy")] +pub(crate) const ENTROPY_PACKET_SIZE: u32 = 32; diff --git a/src/drivers/entropy/mmio.rs b/src/drivers/entropy/mmio.rs new file mode 100644 index 0000000000..2847d559e5 --- /dev/null +++ b/src/drivers/entropy/mmio.rs @@ -0,0 +1,36 @@ +use virtio::mmio::DeviceRegisters; +use volatile::VolatileRef; + +use crate::drivers::entropy::{EntropyDevCfg, RxQueue, VirtioEntropyDriver}; +use crate::drivers::virtio::error::VirtioError; +use crate::drivers::virtio::transport::mmio::{ComCfg, NotifCfg}; + +impl VirtioEntropyDriver { + pub fn new( + dev_id: u16, + mut registers: VolatileRef<'static, DeviceRegisters>, + ) -> Result { + let dev_cfg = EntropyDevCfg { + dev_id, + features: virtio::entropy::F::empty(), + }; + let notif_cfg = NotifCfg::new(registers.borrow_mut()); + + Ok(VirtioEntropyDriver { + dev_cfg, + com_cfg: ComCfg::new(registers), + notif_cfg, + recv_vq: RxQueue::new(), + }) + } + + pub fn init( + dev_id: u16, + registers: VolatileRef<'static, DeviceRegisters>, + ) -> Result { + let mut drv = VirtioEntropyDriver::new(dev_id, registers)?; + drv.init_dev().map_err(VirtioError::EntropyDriver)?; + drv.com_cfg.print_information(); + Ok(drv) + } +} diff --git a/src/drivers/entropy/mod.rs b/src/drivers/entropy/mod.rs new file mode 100644 index 0000000000..f983fa99cb --- /dev/null +++ b/src/drivers/entropy/mod.rs @@ -0,0 +1,237 @@ +//! A virtio-entropy driver. +//! +//! For details on the device, see [Entropy Device]. +//! For details on the Rust definitions, see [`virtio::entropy`]. +//! +//! [Entropy Device]: https://docs.oasis-open.org/virtio/virtio/v1.4/virtio-v1.4.pdf#page=180.53 +//! + +#[cfg(not(feature = "pci"))] +mod mmio; +#[cfg(feature = "pci")] +mod pci; + +use alloc::vec::Vec; + +use embedded_io::{ErrorType, Read}; +use smallvec::SmallVec; + +use crate::config::{ENTROPY_PACKET_SIZE, VIRTIO_MAX_QUEUE_SIZE}; +use crate::drivers::error::DriverError; +#[cfg(not(feature = "pci"))] +use crate::drivers::mmio::get_entropy_driver; +#[cfg(feature = "pci")] +use crate::drivers::pci::get_entropy_driver; +use crate::drivers::virtio::ControlRegisters; +use crate::drivers::virtio::error::VirtioEntropyError; +#[cfg(not(feature = "pci"))] +use crate::drivers::virtio::transport::mmio::{ComCfg, NotifCfg}; +#[cfg(feature = "pci")] +use crate::drivers::virtio::transport::pci::{ComCfg, NotifCfg}; +use crate::drivers::virtio::virtqueue::split::SplitVq; +use crate::drivers::virtio::virtqueue::{ + AvailBufferToken, BufferElem, BufferType, UsedBufferToken, VirtQueue, Virtq, +}; +use crate::errno::Errno; +use crate::mm::device_alloc::DeviceAlloc; + +pub fn seed_entropy() -> Option<[u8; 32]> { + get_entropy_driver().and_then(|drv| { + let mut buf = [0u8; 32]; + if drv.lock().read(&mut buf).ok()? == buf.len() { + Some(buf) + } else { + None + } + }) +} + +fn fill_queue(vq: &mut VirtQueue, num_packets: u16, packet_size: u32) { + for _ in 0..num_packets { + let buff_tkn = match AvailBufferToken::new(SmallVec::new(), { + let mut vec = SmallVec::new(); + vec.push(BufferElem::Vector(Vec::with_capacity_in( + packet_size.try_into().unwrap(), + DeviceAlloc, + ))); + vec + }) { + Ok(tkn) => tkn, + Err(_vq_err) => { + panic!("Setup of entropy queue failed, which should not happen!"); + } + }; + + // BufferTokens are directly provided to the queue + // TransferTokens are directly dispatched + // Transfers will be awaited at the queue + if let Err(err) = vq.dispatch(buff_tkn, false, BufferType::Direct) { + error!("{err:#?}"); + break; + } + } +} + +pub(crate) struct RxQueue { + vq: Option, + packet_size: u32, +} +impl RxQueue { + pub fn new() -> Self { + Self { + vq: None, + packet_size: ENTROPY_PACKET_SIZE, + } + } + + pub fn add(&mut self, mut vq: VirtQueue) { + const BUFF_PER_PACKET: u16 = 1; + let num_packets = vq.size() / BUFF_PER_PACKET; + fill_queue(&mut vq, num_packets, self.packet_size); + + self.vq = Some(vq); + } + + pub fn disable_notifs(&mut self) { + let Some(vq) = &mut self.vq else { + return; + }; + + vq.disable_notifs(); + } + + fn get_next(&mut self) -> Option { + self.vq.as_mut().unwrap().try_recv().ok() + } + + pub fn process_packet(&mut self, mut f: F) -> Result + where + F: FnMut(&[u8]) -> usize, + { + let Some(mut buffer_tkn) = self.get_next() else { + return Ok(0); + }; + + let packet = buffer_tkn.used_recv_buff.pop_front_vec().unwrap(); + let vq = self.vq.as_mut().unwrap(); + let result = f(&packet[..]); + + fill_queue(vq, 1, self.packet_size); + + Ok(result) + } +} + +/// A wrapper struct for the raw configuration structure. +/// Handling the right access to fields, as some are read-only +/// for the driver. +pub(crate) struct EntropyDevCfg { + pub dev_id: u16, + pub features: virtio::entropy::F, +} + +pub(crate) struct VirtioEntropyDriver { + pub(super) dev_cfg: EntropyDevCfg, + pub(super) com_cfg: ComCfg, + pub(super) notif_cfg: NotifCfg, + + pub(super) recv_vq: RxQueue, +} + +impl VirtioEntropyDriver { + #[cfg(feature = "pci")] + pub fn set_failed(&mut self) { + self.com_cfg.set_failed(); + } + + pub fn init_dev(&mut self) -> Result<(), VirtioEntropyError> { + // Reset + self.com_cfg.reset_dev(); + + // Indicate device, that OS noticed it + self.com_cfg.ack_dev(); + + // Indicate device, that driver is able to handle it + self.com_cfg.set_drv(); + + let minimal_features = virtio::entropy::F::VERSION_1; + let negotiated_features = self + .com_cfg + .control_registers() + .negotiate_features(minimal_features); + + if !negotiated_features.contains(minimal_features) { + error!("Device features set, does not satisfy minimal features needed. Aborting!"); + return Err(VirtioEntropyError::FailFeatureNeg(self.dev_cfg.dev_id)); + } + + // Indicates the device, that the current feature set is final for the driver + // and will not be changed. + self.com_cfg.features_ok(); + + // Checks if the device has accepted final set. This finishes feature negotiation. + if self.com_cfg.check_features() { + info!( + "Features have been negotiated between virtio entropy device {:x} and driver.", + self.dev_cfg.dev_id + ); + // Set feature set in device config fur future use. + self.dev_cfg.features = negotiated_features; + } else { + error!("The device does not support our subset of features."); + return Err(VirtioEntropyError::FailFeatureNeg(self.dev_cfg.dev_id)); + } + + // create the queues and tell device about them + self.recv_vq.add(VirtQueue::Split( + SplitVq::new( + &mut self.com_cfg, + &self.notif_cfg, + VIRTIO_MAX_QUEUE_SIZE, + 0, + self.dev_cfg.features.into(), + ) + .unwrap(), + )); + // Interrupt for receiving packets is not needed + self.recv_vq.disable_notifs(); + + // At this point the device is "live" + self.com_cfg.drv_ok(); + + Ok(()) + } +} + +impl ErrorType for VirtioEntropyDriver { + type Error = Errno; +} + +impl Read for VirtioEntropyDriver { + fn read(&mut self, buf: &mut [u8]) -> Result { + self.recv_vq + .process_packet(|src| { + buf[..src.len()].copy_from_slice(src); + src.len() + }) + .map_err(|_| Errno::Io) + } +} + +pub mod error { + use thiserror::Error; + + #[derive(Error, Debug, Copy, Clone)] + pub enum VirtioEntropyError { + #[cfg(feature = "pci")] + #[error( + "Virtio entropy device driver failed, for device {0:x}, due to a missing or malformed device config!" + )] + NoDevCfg(u16), + + #[error( + "Virtio entropy device driver failed, for device {0:x}, device did not acknowledge negotiated feature set!" + )] + FailFeatureNeg(u16), + } +} diff --git a/src/drivers/entropy/pci.rs b/src/drivers/entropy/pci.rs new file mode 100644 index 0000000000..3a8a51cd43 --- /dev/null +++ b/src/drivers/entropy/pci.rs @@ -0,0 +1,77 @@ +use pci_types::CommandRegister; + +use crate::arch::kernel::pci::PciConfigRegion; +use crate::drivers::entropy::{EntropyDevCfg, RxQueue, VirtioEntropyDriver}; +use crate::drivers::pci::PciDevice; +use crate::drivers::virtio::error::{self, VirtioError}; +use crate::drivers::virtio::transport::pci::{self, PciCap, UniCapsColl}; + +impl VirtioEntropyDriver { + fn map_cfg(cap: &PciCap) -> Option { + Some(EntropyDevCfg { + dev_id: cap.dev_id(), + features: virtio::entropy::F::empty(), + }) + } + + pub fn new( + caps_coll: UniCapsColl, + device: &PciDevice, + ) -> Result { + let device_id = device.device_id(); + + let UniCapsColl { + com_cfg, + notif_cfg, + dev_cfg_list, + .. + } = caps_coll; + + let Some(dev_cfg) = dev_cfg_list.iter().find_map(VirtioEntropyDriver::map_cfg) else { + error!("No dev config. Aborting!"); + return Err(error::VirtioEntropyError::NoDevCfg(device_id)); + }; + + Ok(VirtioEntropyDriver { + dev_cfg, + com_cfg, + notif_cfg, + recv_vq: RxQueue::new(), + }) + } + + pub(crate) fn init( + device: &PciDevice, + ) -> Result { + device.set_command(CommandRegister::BUS_MASTER_ENABLE); + + let mut drv = match pci::map_caps(device) { + Ok(caps) => match VirtioEntropyDriver::new(caps, device) { + Ok(driver) => driver, + Err(entropy_err) => { + error!("Initializing new virtio entropy device driver failed. Aborting!"); + return Err(VirtioError::EntropyDriver(entropy_err)); + } + }, + Err(err) => { + error!("Mapping capabilities failed. Aborting!"); + return Err(err); + } + }; + + match drv.init_dev() { + Ok(()) => { + info!( + "Entropy device with id {:x}, has been initialized by driver!", + drv.dev_cfg.dev_id + ); + + Ok(drv) + } + Err(entropy_err) => { + drv.set_failed(); + Err(VirtioError::EntropyDriver(entropy_err)) + } + } + } +} diff --git a/src/drivers/mmio.rs b/src/drivers/mmio.rs index fca912fb82..e63ad3c080 100644 --- a/src/drivers/mmio.rs +++ b/src/drivers/mmio.rs @@ -1,5 +1,7 @@ #[cfg(feature = "virtio-console")] pub(crate) use crate::arch::kernel::mmio::get_console_driver; +#[cfg(feature = "virtio-entropy")] +pub(crate) use crate::arch::kernel::mmio::get_entropy_driver; #[cfg(feature = "virtio-fs")] pub(crate) use crate::arch::kernel::mmio::get_filesystem_driver; #[cfg(feature = "virtio-vsock")] diff --git a/src/drivers/mod.rs b/src/drivers/mod.rs index 63277722e3..5b0edbadc6 100644 --- a/src/drivers/mod.rs +++ b/src/drivers/mod.rs @@ -2,6 +2,8 @@ #[cfg(feature = "virtio-console")] pub mod console; +#[cfg(feature = "virtio-entropy")] +pub mod entropy; #[cfg(feature = "virtio-fs")] pub mod fs; #[cfg(not(feature = "pci"))] diff --git a/src/drivers/pci.rs b/src/drivers/pci.rs index a59aaacfe7..4be2db51e3 100644 --- a/src/drivers/pci.rs +++ b/src/drivers/pci.rs @@ -6,7 +6,8 @@ use core::fmt; #[cfg(any( feature = "virtio-fs", feature = "virtio-vsock", - feature = "virtio-console" + feature = "virtio-console", + feature = "virtio-entropy", ))] use hermit_sync::InterruptTicketMutex; use hermit_sync::without_interrupts; @@ -25,6 +26,8 @@ use crate::drivers::Driver; use crate::drivers::InterruptHandlerMap; #[cfg(feature = "virtio-console")] use crate::drivers::console::{VirtioConsoleDriver, VirtioUART}; +#[cfg(feature = "virtio-entropy")] +use crate::drivers::entropy::VirtioEntropyDriver; #[cfg(feature = "virtio-fs")] use crate::drivers::fs::VirtioFsDriver; #[cfg(feature = "rtl8139")] @@ -333,6 +336,8 @@ pub(crate) enum PciDriver { VirtioFs(InterruptTicketMutex), #[cfg(feature = "virtio-console")] VirtioConsole(InterruptTicketMutex), + #[cfg(feature = "virtio-entropy")] + VirtioEntropy(InterruptTicketMutex), #[cfg(feature = "virtio-vsock")] VirtioVsock(InterruptTicketMutex), } @@ -347,6 +352,15 @@ impl PciDriver { } } + #[cfg(feature = "virtio-entropy")] + fn get_entropy_driver(&self) -> Option<&InterruptTicketMutex> { + #[allow(unreachable_patterns)] + match self { + Self::VirtioEntropy(drv) => Some(drv), + _ => None, + } + } + #[cfg(feature = "virtio-vsock")] fn get_vsock_driver(&self) -> Option<&InterruptTicketMutex> { #[allow(unreachable_patterns)] @@ -384,6 +398,14 @@ pub(crate) fn get_console_driver() -> Option<&'static InterruptTicketMutex Option<&'static InterruptTicketMutex> { + PCI_DRIVERS + .get()? + .iter() + .find_map(|drv| drv.get_entropy_driver()) +} + #[cfg(feature = "virtio-vsock")] pub(crate) fn get_vsock_driver() -> Option<&'static InterruptTicketMutex> { PCI_DRIVERS @@ -429,6 +451,10 @@ pub(crate) fn init(handlers: &mut InterruptHandlerMap) { .lock() .replace_device(IoDevice::Virtio(VirtioUART::new())); } + #[cfg(feature = "virtio-entropy")] + Ok(VirtioDriver::Entropy(drv)) => { + register_driver(PciDriver::VirtioEntropy(InterruptTicketMutex::new(*drv))); + } #[cfg(feature = "virtio-fs")] Ok(VirtioDriver::Fs(drv)) => { register_driver(PciDriver::VirtioFs(InterruptTicketMutex::new(*drv))); diff --git a/src/drivers/virtio/mod.rs b/src/drivers/virtio/mod.rs index a301ea1ec5..e9199b8a57 100644 --- a/src/drivers/virtio/mod.rs +++ b/src/drivers/virtio/mod.rs @@ -167,6 +167,8 @@ pub mod error { #[cfg(feature = "virtio-console")] pub use crate::drivers::console::error::VirtioConsoleError; + #[cfg(feature = "virtio-entropy")] + pub use crate::drivers::entropy::error::VirtioEntropyError; #[cfg(feature = "virtio-fs")] pub use crate::drivers::fs::error::VirtioFsInitError; #[cfg(all( @@ -226,5 +228,9 @@ pub mod error { #[cfg(feature = "virtio-console")] #[error(transparent)] ConsoleDriver(VirtioConsoleError), + + #[cfg(feature = "virtio-entropy")] + #[error(transparent)] + EntropyDriver(VirtioEntropyError), } } diff --git a/src/drivers/virtio/transport/mmio.rs b/src/drivers/virtio/transport/mmio.rs index a5bdd66ff7..01e7a00b5b 100644 --- a/src/drivers/virtio/transport/mmio.rs +++ b/src/drivers/virtio/transport/mmio.rs @@ -18,6 +18,8 @@ use volatile::{VolatilePtr, VolatileRef}; #[cfg(feature = "virtio-console")] use crate::drivers::console::VirtioConsoleDriver; +#[cfg(feature = "virtio-entropy")] +use crate::drivers::entropy::VirtioEntropyDriver; use crate::drivers::error::DriverError; #[cfg(feature = "virtio-fs")] use crate::drivers::fs::VirtioFsDriver; @@ -323,6 +325,8 @@ impl IsrStatus { pub(crate) enum VirtioDriver { #[cfg(feature = "virtio-console")] Console(alloc::boxed::Box), + #[cfg(feature = "virtio-entropy")] + Entropy(alloc::boxed::Box), #[cfg(feature = "virtio-fs")] Fs(alloc::boxed::Box), #[cfg(feature = "virtio-net")] @@ -361,6 +365,19 @@ pub(crate) fn init_device( Err(DriverError::InitVirtioDevFail(virtio_error)) } }, + #[cfg(feature = "virtio-entropy")] + virtio::Id::Rng => match VirtioEntropyDriver::init(dev_id, registers) { + Ok(virt_entropy_drv) => { + info!("Virtio entropy driver initialized."); + Ok(VirtioDriver::Entropy(alloc::boxed::Box::new( + virt_entropy_drv, + ))) + } + Err(virtio_error) => { + error!("Virtio entropy driver could not be initialized with device"); + Err(DriverError::InitVirtioDevFail(virtio_error)) + } + }, #[cfg(feature = "virtio-fs")] virtio::Id::Fs => { // TODO: check subclass diff --git a/src/drivers/virtio/transport/pci.rs b/src/drivers/virtio/transport/pci.rs index 88765cd77d..e06c1befdb 100644 --- a/src/drivers/virtio/transport/pci.rs +++ b/src/drivers/virtio/transport/pci.rs @@ -22,6 +22,8 @@ use crate::arch::kernel::pci::PciConfigRegion; use crate::drivers::InterruptHandlerMap; #[cfg(feature = "virtio-console")] use crate::drivers::console::VirtioConsoleDriver; +#[cfg(feature = "virtio-entropy")] +use crate::drivers::entropy::VirtioEntropyDriver; use crate::drivers::error::DriverError; #[cfg(feature = "virtio-fs")] use crate::drivers::fs::VirtioFsDriver; @@ -790,6 +792,19 @@ pub(crate) fn init_device( Err(DriverError::InitVirtioDevFail(virtio_error)) } }, + #[cfg(feature = "virtio-entropy")] + virtio::Id::Rng => match VirtioEntropyDriver::init(device) { + Ok(virt_entropy_drv) => { + info!("Virtio entropy driver initialized."); + Ok(VirtioDriver::Entropy(alloc::boxed::Box::new( + virt_entropy_drv, + ))) + } + Err(virtio_error) => { + error!("Virtio entropy driver could not be initialized with device: {device_id:x}"); + Err(DriverError::InitVirtioDevFail(virtio_error)) + } + }, #[cfg(feature = "virtio-fs")] virtio::Id::Fs => { // TODO: check subclass @@ -854,6 +869,8 @@ pub(crate) fn init_device( pub(crate) enum VirtioDriver { #[cfg(feature = "virtio-console")] Console(alloc::boxed::Box), + #[cfg(feature = "virtio-entropy")] + Entropy(alloc::boxed::Box), #[cfg(feature = "virtio-fs")] Fs(alloc::boxed::Box), #[cfg(all( diff --git a/src/entropy.rs b/src/entropy.rs index 76480bf985..de07c81345 100644 --- a/src/entropy.rs +++ b/src/entropy.rs @@ -7,7 +7,11 @@ use hermit_sync::InterruptTicketMutex; use rand_chacha::ChaCha20Rng; use rand_chacha::rand_core::{Rng, SeedableRng}; -use crate::arch::kernel::processor::{get_timer_ticks, seed_entropy}; +use crate::arch::kernel::processor::get_timer_ticks; +#[cfg(not(feature = "virtio-entropy"))] +use crate::arch::kernel::processor::seed_entropy as processor_seed_entropy; +#[cfg(feature = "virtio-entropy")] +use crate::drivers::entropy::seed_entropy as virtio_seed_entropy; use crate::errno::Errno; use crate::io; @@ -36,12 +40,20 @@ pub fn read(buf: &mut [u8], _flags: Flags) -> io::Result { let pool = match pool { Some(pool) if now.saturating_sub(pool.last_reseed) <= RESEED_INTERVAL => pool, pool => { - let Some(seed) = seed_entropy() else { - return Err(Errno::Nosys); + let seed = { + #[cfg(feature = "virtio-entropy")] + { + virtio_seed_entropy() + } + #[cfg(not(feature = "virtio-entropy"))] + { + processor_seed_entropy() + } }; + let seed_bytes = seed.ok_or(Errno::Nosys)?; pool.insert(Pool { - rng: ChaCha20Rng::from_seed(seed), + rng: ChaCha20Rng::from_seed(seed_bytes), last_reseed: now, }) } diff --git a/src/init_cell.rs b/src/init_cell.rs index f9f47691e6..08849ec2b8 100644 --- a/src/init_cell.rs +++ b/src/init_cell.rs @@ -2,6 +2,7 @@ not(any( feature = "virtio-vsock", feature = "virtio-fs", + feature = "virtio-entropy", feature = "virtio-console" )), expect(dead_code)