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