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