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