// Copyright 2017 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. use super::Error as DeviceError; use super::{ ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue, VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1, }; use crate::seccomp_filters::{get_seccomp_filter, Thread}; use crate::{VirtioInterrupt, VirtioInterruptType}; use anyhow::anyhow; use libc::EFD_NONBLOCK; use seccomp::{SeccompAction, SeccompFilter}; use std::fs::File; use std::io; use std::os::unix::io::AsRawFd; use std::result; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread; use vm_memory::{Bytes, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap}; use vm_migration::{ Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable, Transportable, }; use vmm_sys_util::eventfd::EventFd; const QUEUE_SIZE: u16 = 256; const NUM_QUEUES: usize = 1; const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE]; // New descriptors are pending on the virtio queue. const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1; struct RngEpollHandler { queues: Vec, mem: GuestMemoryAtomic, random_file: File, interrupt_cb: Arc, queue_evt: EventFd, kill_evt: EventFd, pause_evt: EventFd, } impl RngEpollHandler { fn process_queue(&mut self) -> bool { let queue = &mut self.queues[0]; let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize]; let mut used_count = 0; let mem = self.mem.memory(); for avail_desc in queue.iter(&mem) { let mut len = 0; // Drivers can only read from the random device. if avail_desc.is_write_only() { // Fill the read with data from the random device on the host. if mem .read_from( avail_desc.addr, &mut self.random_file, avail_desc.len as usize, ) .is_ok() { len = avail_desc.len; } } used_desc_heads[used_count] = (avail_desc.index, len); used_count += 1; } for &(desc_index, len) in &used_desc_heads[..used_count] { queue.add_used(&mem, desc_index, len); } used_count > 0 } fn signal_used_queue(&self) -> result::Result<(), DeviceError> { self.interrupt_cb .trigger(&VirtioInterruptType::Queue, Some(&self.queues[0])) .map_err(|e| { error!("Failed to signal used queue: {:?}", e); DeviceError::FailedSignalingUsedQueue(e) }) } fn run(&mut self, paused: Arc) -> result::Result<(), EpollHelperError> { let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?; helper.add_event(self.queue_evt.as_raw_fd(), QUEUE_AVAIL_EVENT)?; helper.run(paused, self)?; Ok(()) } } impl EpollHelperHandler for RngEpollHandler { fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool { let ev_type = event.data as u16; match ev_type { QUEUE_AVAIL_EVENT => { if let Err(e) = self.queue_evt.read() { error!("Failed to get queue event: {:?}", e); return true; } else if self.process_queue() { if let Err(e) = self.signal_used_queue() { error!("Failed to signal used queue: {:?}", e); return true; } } } _ => { error!("Unexpected event: {}", ev_type); return true; } } false } } /// Virtio device for exposing entropy to the guest OS through virtio. pub struct Rng { id: String, kill_evt: Option, pause_evt: Option, random_file: Option, avail_features: u64, acked_features: u64, queue_evts: Option>, interrupt_cb: Option>, epoll_threads: Option>>, paused: Arc, seccomp_action: SeccompAction, } #[derive(Serialize, Deserialize)] pub struct RngState { pub avail_features: u64, pub acked_features: u64, pub paused: Arc, } impl Rng { /// Create a new virtio rng device that gets random data from /dev/urandom. pub fn new( id: String, path: &str, iommu: bool, seccomp_action: SeccompAction, ) -> io::Result { let random_file = File::open(path)?; let mut avail_features = 1u64 << VIRTIO_F_VERSION_1; if iommu { avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; } Ok(Rng { id, kill_evt: None, pause_evt: None, random_file: Some(random_file), avail_features, acked_features: 0u64, queue_evts: None, interrupt_cb: None, epoll_threads: None, paused: Arc::new(AtomicBool::new(false)), seccomp_action, }) } fn state(&self) -> RngState { RngState { avail_features: self.avail_features, acked_features: self.acked_features, paused: self.paused.clone(), } } fn set_state(&mut self, state: &RngState) -> io::Result<()> { self.avail_features = state.avail_features; self.acked_features = state.acked_features; self.paused = state.paused.clone(); Ok(()) } } impl Drop for Rng { fn drop(&mut self) { if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); } } } impl VirtioDevice for Rng { fn device_type(&self) -> u32 { VirtioDeviceType::TYPE_RNG as u32 } fn queue_max_sizes(&self) -> &[u16] { QUEUE_SIZES } fn features(&self) -> u64 { self.avail_features } fn ack_features(&mut self, value: u64) { let mut v = value; // Check if the guest is ACK'ing a feature that we didn't claim to have. let unrequested_features = v & !self.avail_features; if unrequested_features != 0 { warn!("Received acknowledge request for unknown feature."); // Don't count these features as acked. v &= !unrequested_features; } self.acked_features |= v; } fn activate( &mut self, mem: GuestMemoryAtomic, interrupt_cb: Arc, queues: Vec, mut queue_evts: Vec, ) -> ActivateResult { if queues.len() != NUM_QUEUES || queue_evts.len() != NUM_QUEUES { error!( "Cannot perform activate. Expected {} queue(s), got {}", NUM_QUEUES, queues.len() ); return Err(ActivateError::BadActivate); } let (self_kill_evt, kill_evt) = EventFd::new(EFD_NONBLOCK) .and_then(|e| Ok((e.try_clone()?, e))) .map_err(|e| { error!("failed creating kill EventFd pair: {}", e); ActivateError::BadActivate })?; self.kill_evt = Some(self_kill_evt); let (self_pause_evt, pause_evt) = EventFd::new(EFD_NONBLOCK) .and_then(|e| Ok((e.try_clone()?, e))) .map_err(|e| { error!("failed creating pause EventFd pair: {}", e); ActivateError::BadActivate })?; self.pause_evt = Some(self_pause_evt); // Save the interrupt EventFD as we need to return it on reset // but clone it to pass into the thread. self.interrupt_cb = Some(interrupt_cb.clone()); let mut tmp_queue_evts: Vec = Vec::new(); for queue_evt in queue_evts.iter() { // Save the queue EventFD as we need to return it on reset // but clone it to pass into the thread. tmp_queue_evts.push(queue_evt.try_clone().map_err(|e| { error!("failed to clone queue EventFd: {}", e); ActivateError::BadActivate })?); } self.queue_evts = Some(tmp_queue_evts); if let Some(file) = self.random_file.as_ref() { let random_file = file.try_clone().map_err(|e| { error!("failed cloning rng source: {}", e); ActivateError::BadActivate })?; let mut handler = RngEpollHandler { queues, mem, random_file, interrupt_cb, queue_evt: queue_evts.remove(0), kill_evt, pause_evt, }; let paused = self.paused.clone(); let mut epoll_threads = Vec::new(); // Retrieve seccomp filter for virtio_rng thread let virtio_rng_seccomp_filter = get_seccomp_filter(&self.seccomp_action, Thread::VirtioRng) .map_err(ActivateError::CreateSeccompFilter)?; thread::Builder::new() .name("virtio_rng".to_string()) .spawn(move || { if let Err(e) = SeccompFilter::apply(virtio_rng_seccomp_filter) { error!("Error applying seccomp filter: {:?}", e); } else if let Err(e) = handler.run(paused) { error!("Error running worker: {:?}", e); } }) .map(|thread| epoll_threads.push(thread)) .map_err(|e| { error!("failed to clone the virtio-rng epoll thread: {}", e); ActivateError::BadActivate })?; self.epoll_threads = Some(epoll_threads); return Ok(()); } Err(ActivateError::BadActivate) } fn reset(&mut self) -> Option<(Arc, Vec)> { // We first must resume the virtio thread if it was paused. if self.pause_evt.take().is_some() { self.resume().ok()?; } // Then kill it. if let Some(kill_evt) = self.kill_evt.take() { // Ignore the result because there is nothing we can do about it. let _ = kill_evt.write(1); } // Return the interrupt and queue EventFDs Some(( self.interrupt_cb.take().unwrap(), self.queue_evts.take().unwrap(), )) } } virtio_pausable!(Rng); impl Snapshottable for Rng { fn id(&self) -> String { self.id.clone() } fn snapshot(&self) -> std::result::Result { let snapshot = serde_json::to_vec(&self.state()).map_err(|e| MigratableError::Snapshot(e.into()))?; let mut rng_snapshot = Snapshot::new(self.id.as_str()); rng_snapshot.add_data_section(SnapshotDataSection { id: format!("{}-section", self.id), snapshot, }); Ok(rng_snapshot) } fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> { if let Some(rng_section) = snapshot.snapshot_data.get(&format!("{}-section", self.id)) { let rng_state = match serde_json::from_slice(&rng_section.snapshot) { Ok(state) => state, Err(error) => { return Err(MigratableError::Restore(anyhow!( "Could not deserialize RNG {}", error ))) } }; return self.set_state(&rng_state).map_err(|e| { MigratableError::Restore(anyhow!("Could not restore RNG state {:?}", e)) }); } Err(MigratableError::Restore(anyhow!( "Could not find RNG snapshot section" ))) } } impl Transportable for Rng {} impl Migratable for Rng {}