xref: /cloud-hypervisor/virtio-devices/src/rng.rs (revision 45b01d592a197192bee0249517c9b282bc665ede)
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::{GuestAddressSpace, GuestMemory, 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_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(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, 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_versioned_state(&self.state())
328     }
329 }
330 
331 impl Transportable for Rng {}
332 impl Migratable for Rng {}
333