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 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause 6 7 use std::fs::File; 8 use std::io; 9 use std::os::unix::io::AsRawFd; 10 use std::result; 11 use std::sync::atomic::AtomicBool; 12 use std::sync::{Arc, Barrier}; 13 14 use anyhow::anyhow; 15 use seccompiler::SeccompAction; 16 use serde::{Deserialize, Serialize}; 17 use thiserror::Error; 18 use virtio_queue::{Queue, QueueT}; 19 use vm_memory::{GuestAddressSpace, GuestMemory, GuestMemoryAtomic}; 20 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; 21 use vm_virtio::{AccessPlatform, Translatable}; 22 use vmm_sys_util::eventfd::EventFd; 23 24 use super::Error as DeviceError; 25 use super::{ 26 ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon, 27 VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM, 28 VIRTIO_F_VERSION_1, 29 }; 30 use crate::seccomp_filters::Thread; 31 use crate::thread_helper::spawn_virtio_thread; 32 use crate::GuestMemoryMmap; 33 use crate::{VirtioInterrupt, VirtioInterruptType}; 34 35 const QUEUE_SIZE: u16 = 256; 36 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE]; 37 38 // New descriptors are pending on the virtio queue. 39 const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1; 40 41 #[derive(Error, Debug)] 42 enum Error { 43 #[error("Descriptor chain too short")] 44 DescriptorChainTooShort, 45 #[error("Invalid descriptor")] 46 InvalidDescriptor, 47 #[error("Failed to write to guest memory: {0}")] 48 GuestMemoryWrite(vm_memory::guest_memory::Error), 49 #[error("Failed adding used index: {0}")] 50 QueueAddUsed(virtio_queue::Error), 51 } 52 53 struct RngEpollHandler { 54 mem: GuestMemoryAtomic<GuestMemoryMmap>, 55 queue: Queue, 56 random_file: File, 57 interrupt_cb: Arc<dyn VirtioInterrupt>, 58 queue_evt: EventFd, 59 kill_evt: EventFd, 60 pause_evt: EventFd, 61 access_platform: Option<Arc<dyn AccessPlatform>>, 62 } 63 64 impl RngEpollHandler { 65 fn process_queue(&mut self) -> result::Result<bool, Error> { 66 let queue = &mut self.queue; 67 68 let mut used_descs = false; 69 while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) { 70 let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?; 71 72 // The descriptor must be write-only and non-zero length 73 if !(desc.is_write_only() && desc.len() > 0) { 74 return Err(Error::InvalidDescriptor); 75 } 76 77 // Fill the read with data from the random device on the host. 78 let len = desc_chain 79 .memory() 80 .read_volatile_from( 81 desc.addr() 82 .translate_gva(self.access_platform.as_ref(), desc.len() as usize), 83 &mut self.random_file, 84 desc.len() as usize, 85 ) 86 .map_err(Error::GuestMemoryWrite)?; 87 88 queue 89 .add_used(desc_chain.memory(), desc_chain.head_index(), len as u32) 90 .map_err(Error::QueueAddUsed)?; 91 used_descs = true; 92 } 93 94 Ok(used_descs) 95 } 96 97 fn signal_used_queue(&self) -> result::Result<(), DeviceError> { 98 self.interrupt_cb 99 .trigger(VirtioInterruptType::Queue(0)) 100 .map_err(|e| { 101 error!("Failed to signal used queue: {:?}", e); 102 DeviceError::FailedSignalingUsedQueue(e) 103 }) 104 } 105 106 fn run( 107 &mut self, 108 paused: Arc<AtomicBool>, 109 paused_sync: Arc<Barrier>, 110 ) -> result::Result<(), EpollHelperError> { 111 let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?; 112 helper.add_event(self.queue_evt.as_raw_fd(), QUEUE_AVAIL_EVENT)?; 113 helper.run(paused, paused_sync, self)?; 114 115 Ok(()) 116 } 117 } 118 119 impl EpollHelperHandler for RngEpollHandler { 120 fn handle_event( 121 &mut self, 122 _helper: &mut EpollHelper, 123 event: &epoll::Event, 124 ) -> result::Result<(), EpollHelperError> { 125 let ev_type = event.data as u16; 126 match ev_type { 127 QUEUE_AVAIL_EVENT => { 128 self.queue_evt.read().map_err(|e| { 129 EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e)) 130 })?; 131 let needs_notification = self.process_queue().map_err(|e| { 132 EpollHelperError::HandleEvent(anyhow!("Failed to process queue : {:?}", e)) 133 })?; 134 if needs_notification { 135 self.signal_used_queue().map_err(|e| { 136 EpollHelperError::HandleEvent(anyhow!( 137 "Failed to signal used queue: {:?}", 138 e 139 )) 140 })?; 141 } 142 } 143 _ => { 144 return Err(EpollHelperError::HandleEvent(anyhow!( 145 "Unexpected event: {}", 146 ev_type 147 ))); 148 } 149 } 150 Ok(()) 151 } 152 } 153 154 /// Virtio device for exposing entropy to the guest OS through virtio. 155 pub struct Rng { 156 common: VirtioCommon, 157 id: String, 158 random_file: Option<File>, 159 seccomp_action: SeccompAction, 160 exit_evt: EventFd, 161 } 162 163 #[derive(Deserialize, Serialize)] 164 pub struct RngState { 165 pub avail_features: u64, 166 pub acked_features: u64, 167 } 168 169 impl Rng { 170 /// Create a new virtio rng device that gets random data from /dev/urandom. 171 pub fn new( 172 id: String, 173 path: &str, 174 iommu: bool, 175 seccomp_action: SeccompAction, 176 exit_evt: EventFd, 177 state: Option<RngState>, 178 ) -> io::Result<Rng> { 179 let random_file = File::open(path)?; 180 181 let (avail_features, acked_features, paused) = if let Some(state) = state { 182 info!("Restoring virtio-rng {}", id); 183 (state.avail_features, state.acked_features, true) 184 } else { 185 let mut avail_features = 1u64 << VIRTIO_F_VERSION_1; 186 187 if iommu { 188 avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; 189 } 190 191 (avail_features, 0, false) 192 }; 193 194 Ok(Rng { 195 common: VirtioCommon { 196 device_type: VirtioDeviceType::Rng as u32, 197 queue_sizes: QUEUE_SIZES.to_vec(), 198 paused_sync: Some(Arc::new(Barrier::new(2))), 199 avail_features, 200 acked_features, 201 min_queues: 1, 202 paused: Arc::new(AtomicBool::new(paused)), 203 ..Default::default() 204 }, 205 id, 206 random_file: Some(random_file), 207 seccomp_action, 208 exit_evt, 209 }) 210 } 211 212 fn state(&self) -> RngState { 213 RngState { 214 avail_features: self.common.avail_features, 215 acked_features: self.common.acked_features, 216 } 217 } 218 219 #[cfg(fuzzing)] 220 pub fn wait_for_epoll_threads(&mut self) { 221 self.common.wait_for_epoll_threads(); 222 } 223 } 224 225 impl Drop for Rng { 226 fn drop(&mut self) { 227 if let Some(kill_evt) = self.common.kill_evt.take() { 228 // Ignore the result because there is nothing we can do about it. 229 let _ = kill_evt.write(1); 230 } 231 self.common.wait_for_epoll_threads(); 232 } 233 } 234 235 impl VirtioDevice for Rng { 236 fn device_type(&self) -> u32 { 237 self.common.device_type 238 } 239 240 fn queue_max_sizes(&self) -> &[u16] { 241 &self.common.queue_sizes 242 } 243 244 fn features(&self) -> u64 { 245 self.common.avail_features 246 } 247 248 fn ack_features(&mut self, value: u64) { 249 self.common.ack_features(value) 250 } 251 252 fn activate( 253 &mut self, 254 mem: GuestMemoryAtomic<GuestMemoryMmap>, 255 interrupt_cb: Arc<dyn VirtioInterrupt>, 256 mut queues: Vec<(usize, Queue, EventFd)>, 257 ) -> ActivateResult { 258 self.common.activate(&queues, &interrupt_cb)?; 259 let (kill_evt, pause_evt) = self.common.dup_eventfds(); 260 261 if let Some(file) = self.random_file.as_ref() { 262 let random_file = file.try_clone().map_err(|e| { 263 error!("failed cloning rng source: {}", e); 264 ActivateError::BadActivate 265 })?; 266 267 let (_, queue, queue_evt) = queues.remove(0); 268 269 let mut handler = RngEpollHandler { 270 mem, 271 queue, 272 random_file, 273 interrupt_cb, 274 queue_evt, 275 kill_evt, 276 pause_evt, 277 access_platform: self.common.access_platform.clone(), 278 }; 279 280 let paused = self.common.paused.clone(); 281 let paused_sync = self.common.paused_sync.clone(); 282 let mut epoll_threads = Vec::new(); 283 spawn_virtio_thread( 284 &self.id, 285 &self.seccomp_action, 286 Thread::VirtioRng, 287 &mut epoll_threads, 288 &self.exit_evt, 289 move || handler.run(paused, paused_sync.unwrap()), 290 )?; 291 292 self.common.epoll_threads = Some(epoll_threads); 293 294 event!("virtio-device", "activated", "id", &self.id); 295 return Ok(()); 296 } 297 Err(ActivateError::BadActivate) 298 } 299 300 fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> { 301 let result = self.common.reset(); 302 event!("virtio-device", "reset", "id", &self.id); 303 result 304 } 305 306 fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) { 307 self.common.set_access_platform(access_platform) 308 } 309 } 310 311 impl Pausable for Rng { 312 fn pause(&mut self) -> result::Result<(), MigratableError> { 313 self.common.pause() 314 } 315 316 fn resume(&mut self) -> result::Result<(), MigratableError> { 317 self.common.resume() 318 } 319 } 320 321 impl Snapshottable for Rng { 322 fn id(&self) -> String { 323 self.id.clone() 324 } 325 326 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 327 Snapshot::new_from_state(&self.state()) 328 } 329 } 330 331 impl Transportable for Rng {} 332 impl Migratable for Rng {} 333