xref: /cloud-hypervisor/virtio-devices/src/rng.rs (revision 6f8bd27cf7629733582d930519e98d19e90afb16)
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, VirtioCommon,
8     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 anyhow::anyhow;
16 use seccompiler::SeccompAction;
17 use std::fs::File;
18 use std::io;
19 use std::os::unix::io::AsRawFd;
20 use std::result;
21 use std::sync::atomic::AtomicBool;
22 use std::sync::{Arc, Barrier};
23 use thiserror::Error;
24 use versionize::{VersionMap, Versionize, VersionizeResult};
25 use versionize_derive::Versionize;
26 use virtio_queue::{Queue, QueueT};
27 use vm_memory::{Bytes, GuestAddressSpace, GuestMemoryAtomic};
28 use vm_migration::VersionMapped;
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_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(Versionize)]
162 pub struct RngState {
163     pub avail_features: u64,
164     pub acked_features: u64,
165 }
166 
167 impl VersionMapped for RngState {}
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) = if let Some(state) = state {
182             info!("Restoring virtio-rng {}", id);
183             (state.avail_features, state.acked_features)
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)
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                 ..Default::default()
203             },
204             id,
205             random_file: Some(random_file),
206             seccomp_action,
207             exit_evt,
208         })
209     }
210 
211     fn state(&self) -> RngState {
212         RngState {
213             avail_features: self.common.avail_features,
214             acked_features: self.common.acked_features,
215         }
216     }
217 
218     #[cfg(fuzzing)]
219     pub fn wait_for_epoll_threads(&mut self) {
220         self.common.wait_for_epoll_threads();
221     }
222 }
223 
224 impl Drop for Rng {
225     fn drop(&mut self) {
226         if let Some(kill_evt) = self.common.kill_evt.take() {
227             // Ignore the result because there is nothing we can do about it.
228             let _ = kill_evt.write(1);
229         }
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_versioned_state(&self.id, &self.state())
326     }
327 }
328 
329 impl Transportable for Rng {}
330 impl Migratable for Rng {}
331