1 // Copyright 2017 The Chromium OS Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 use super::Error as DeviceError; 6 use super::{ 7 ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, Queue, 8 VirtioCommon, VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM, 9 VIRTIO_F_VERSION_1, 10 }; 11 use crate::seccomp_filters::Thread; 12 use crate::thread_helper::spawn_virtio_thread; 13 use crate::GuestMemoryMmap; 14 use crate::{VirtioInterrupt, VirtioInterruptType}; 15 use seccompiler::SeccompAction; 16 use std::fs::File; 17 use std::io; 18 use std::os::unix::io::AsRawFd; 19 use std::result; 20 use std::sync::atomic::AtomicBool; 21 use std::sync::{Arc, Barrier}; 22 use versionize::{VersionMap, Versionize, VersionizeResult}; 23 use versionize_derive::Versionize; 24 use vm_memory::{Bytes, GuestAddressSpace, GuestMemoryAtomic}; 25 use vm_migration::VersionMapped; 26 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; 27 use vmm_sys_util::eventfd::EventFd; 28 29 const QUEUE_SIZE: u16 = 256; 30 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE]; 31 32 // New descriptors are pending on the virtio queue. 33 const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1; 34 35 struct RngEpollHandler { 36 queues: Vec<Queue>, 37 mem: GuestMemoryAtomic<GuestMemoryMmap>, 38 random_file: File, 39 interrupt_cb: Arc<dyn VirtioInterrupt>, 40 queue_evt: EventFd, 41 kill_evt: EventFd, 42 pause_evt: EventFd, 43 } 44 45 impl RngEpollHandler { 46 fn process_queue(&mut self) -> bool { 47 let queue = &mut self.queues[0]; 48 49 let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize]; 50 let mut used_count = 0; 51 let mem = self.mem.memory(); 52 for avail_desc in queue.iter(&mem) { 53 let mut len = 0; 54 55 // Drivers can only read from the random device. 56 if avail_desc.is_write_only() { 57 // Fill the read with data from the random device on the host. 58 if mem 59 .read_from( 60 avail_desc.addr, 61 &mut self.random_file, 62 avail_desc.len as usize, 63 ) 64 .is_ok() 65 { 66 len = avail_desc.len; 67 } 68 } 69 70 used_desc_heads[used_count] = (avail_desc.index, len); 71 used_count += 1; 72 } 73 74 for &(desc_index, len) in &used_desc_heads[..used_count] { 75 queue.add_used(&mem, desc_index, len); 76 } 77 used_count > 0 78 } 79 80 fn signal_used_queue(&self) -> result::Result<(), DeviceError> { 81 self.interrupt_cb 82 .trigger(&VirtioInterruptType::Queue, Some(&self.queues[0])) 83 .map_err(|e| { 84 error!("Failed to signal used queue: {:?}", e); 85 DeviceError::FailedSignalingUsedQueue(e) 86 }) 87 } 88 89 fn run( 90 &mut self, 91 paused: Arc<AtomicBool>, 92 paused_sync: Arc<Barrier>, 93 ) -> result::Result<(), EpollHelperError> { 94 let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?; 95 helper.add_event(self.queue_evt.as_raw_fd(), QUEUE_AVAIL_EVENT)?; 96 helper.run(paused, paused_sync, self)?; 97 98 Ok(()) 99 } 100 } 101 102 impl EpollHelperHandler for RngEpollHandler { 103 fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool { 104 let ev_type = event.data as u16; 105 match ev_type { 106 QUEUE_AVAIL_EVENT => { 107 if let Err(e) = self.queue_evt.read() { 108 error!("Failed to get queue event: {:?}", e); 109 return true; 110 } else if self.process_queue() { 111 if let Err(e) = self.signal_used_queue() { 112 error!("Failed to signal used queue: {:?}", e); 113 return true; 114 } 115 } 116 } 117 _ => { 118 error!("Unexpected event: {}", ev_type); 119 return true; 120 } 121 } 122 false 123 } 124 } 125 126 /// Virtio device for exposing entropy to the guest OS through virtio. 127 pub struct Rng { 128 common: VirtioCommon, 129 id: String, 130 random_file: Option<File>, 131 seccomp_action: SeccompAction, 132 exit_evt: EventFd, 133 } 134 135 #[derive(Versionize)] 136 pub struct RngState { 137 pub avail_features: u64, 138 pub acked_features: u64, 139 } 140 141 impl VersionMapped for RngState {} 142 143 impl Rng { 144 /// Create a new virtio rng device that gets random data from /dev/urandom. 145 pub fn new( 146 id: String, 147 path: &str, 148 iommu: bool, 149 seccomp_action: SeccompAction, 150 exit_evt: EventFd, 151 ) -> io::Result<Rng> { 152 let random_file = File::open(path)?; 153 let mut avail_features = 1u64 << VIRTIO_F_VERSION_1; 154 155 if iommu { 156 avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; 157 } 158 159 Ok(Rng { 160 common: VirtioCommon { 161 device_type: VirtioDeviceType::Rng as u32, 162 queue_sizes: QUEUE_SIZES.to_vec(), 163 paused_sync: Some(Arc::new(Barrier::new(2))), 164 avail_features, 165 min_queues: 1, 166 ..Default::default() 167 }, 168 id, 169 random_file: Some(random_file), 170 seccomp_action, 171 exit_evt, 172 }) 173 } 174 175 fn state(&self) -> RngState { 176 RngState { 177 avail_features: self.common.avail_features, 178 acked_features: self.common.acked_features, 179 } 180 } 181 182 fn set_state(&mut self, state: &RngState) { 183 self.common.avail_features = state.avail_features; 184 self.common.acked_features = state.acked_features; 185 } 186 } 187 188 impl Drop for Rng { 189 fn drop(&mut self) { 190 if let Some(kill_evt) = self.common.kill_evt.take() { 191 // Ignore the result because there is nothing we can do about it. 192 let _ = kill_evt.write(1); 193 } 194 } 195 } 196 197 impl VirtioDevice for Rng { 198 fn device_type(&self) -> u32 { 199 self.common.device_type 200 } 201 202 fn queue_max_sizes(&self) -> &[u16] { 203 &self.common.queue_sizes 204 } 205 206 fn features(&self) -> u64 { 207 self.common.avail_features 208 } 209 210 fn ack_features(&mut self, value: u64) { 211 self.common.ack_features(value) 212 } 213 214 fn activate( 215 &mut self, 216 mem: GuestMemoryAtomic<GuestMemoryMmap>, 217 interrupt_cb: Arc<dyn VirtioInterrupt>, 218 queues: Vec<Queue>, 219 mut queue_evts: Vec<EventFd>, 220 ) -> ActivateResult { 221 self.common.activate(&queues, &queue_evts, &interrupt_cb)?; 222 let (kill_evt, pause_evt) = self.common.dup_eventfds(); 223 224 if let Some(file) = self.random_file.as_ref() { 225 let random_file = file.try_clone().map_err(|e| { 226 error!("failed cloning rng source: {}", e); 227 ActivateError::BadActivate 228 })?; 229 let mut handler = RngEpollHandler { 230 queues, 231 mem, 232 random_file, 233 interrupt_cb, 234 queue_evt: queue_evts.remove(0), 235 kill_evt, 236 pause_evt, 237 }; 238 239 let paused = self.common.paused.clone(); 240 let paused_sync = self.common.paused_sync.clone(); 241 let mut epoll_threads = Vec::new(); 242 spawn_virtio_thread( 243 &self.id, 244 &self.seccomp_action, 245 Thread::VirtioRng, 246 &mut epoll_threads, 247 &self.exit_evt, 248 move || { 249 if let Err(e) = handler.run(paused, paused_sync.unwrap()) { 250 error!("Error running worker: {:?}", e); 251 } 252 }, 253 )?; 254 255 self.common.epoll_threads = Some(epoll_threads); 256 257 event!("virtio-device", "activated", "id", &self.id); 258 return Ok(()); 259 } 260 Err(ActivateError::BadActivate) 261 } 262 263 fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> { 264 let result = self.common.reset(); 265 event!("virtio-device", "reset", "id", &self.id); 266 result 267 } 268 } 269 270 impl Pausable for Rng { 271 fn pause(&mut self) -> result::Result<(), MigratableError> { 272 self.common.pause() 273 } 274 275 fn resume(&mut self) -> result::Result<(), MigratableError> { 276 self.common.resume() 277 } 278 } 279 280 impl Snapshottable for Rng { 281 fn id(&self) -> String { 282 self.id.clone() 283 } 284 285 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 286 Snapshot::new_from_versioned_state(&self.id, &self.state()) 287 } 288 289 fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> { 290 self.set_state(&snapshot.to_versioned_state(&self.id)?); 291 Ok(()) 292 } 293 } 294 295 impl Transportable for Rng {} 296 impl Migratable for Rng {} 297