xref: /cloud-hypervisor/virtio-devices/src/rng.rs (revision 9af2968a7dc47b89bf07ea9dc5e735084efcfa3a)
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::{get_seccomp_filter, Thread};
12 use crate::GuestMemoryMmap;
13 use crate::{VirtioInterrupt, VirtioInterruptType};
14 use seccomp::{SeccompAction, SeccompFilter};
15 use std::fs::File;
16 use std::io;
17 use std::os::unix::io::AsRawFd;
18 use std::result;
19 use std::sync::atomic::AtomicBool;
20 use std::sync::{Arc, Barrier};
21 use std::thread;
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 }
133 
134 #[derive(Versionize)]
135 pub struct RngState {
136     pub avail_features: u64,
137     pub acked_features: u64,
138 }
139 
140 impl VersionMapped for RngState {}
141 
142 impl Rng {
143     /// Create a new virtio rng device that gets random data from /dev/urandom.
144     pub fn new(
145         id: String,
146         path: &str,
147         iommu: bool,
148         seccomp_action: SeccompAction,
149     ) -> io::Result<Rng> {
150         let random_file = File::open(path)?;
151         let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
152 
153         if iommu {
154             avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
155         }
156 
157         Ok(Rng {
158             common: VirtioCommon {
159                 device_type: VirtioDeviceType::Rng as u32,
160                 queue_sizes: QUEUE_SIZES.to_vec(),
161                 paused_sync: Some(Arc::new(Barrier::new(2))),
162                 avail_features,
163                 min_queues: 1,
164                 ..Default::default()
165             },
166             id,
167             random_file: Some(random_file),
168             seccomp_action,
169         })
170     }
171 
172     fn state(&self) -> RngState {
173         RngState {
174             avail_features: self.common.avail_features,
175             acked_features: self.common.acked_features,
176         }
177     }
178 
179     fn set_state(&mut self, state: &RngState) {
180         self.common.avail_features = state.avail_features;
181         self.common.acked_features = state.acked_features;
182     }
183 }
184 
185 impl Drop for Rng {
186     fn drop(&mut self) {
187         if let Some(kill_evt) = self.common.kill_evt.take() {
188             // Ignore the result because there is nothing we can do about it.
189             let _ = kill_evt.write(1);
190         }
191     }
192 }
193 
194 impl VirtioDevice for Rng {
195     fn device_type(&self) -> u32 {
196         self.common.device_type
197     }
198 
199     fn queue_max_sizes(&self) -> &[u16] {
200         &self.common.queue_sizes
201     }
202 
203     fn features(&self) -> u64 {
204         self.common.avail_features
205     }
206 
207     fn ack_features(&mut self, value: u64) {
208         self.common.ack_features(value)
209     }
210 
211     fn activate(
212         &mut self,
213         mem: GuestMemoryAtomic<GuestMemoryMmap>,
214         interrupt_cb: Arc<dyn VirtioInterrupt>,
215         queues: Vec<Queue>,
216         mut queue_evts: Vec<EventFd>,
217     ) -> ActivateResult {
218         self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
219         let (kill_evt, pause_evt) = self.common.dup_eventfds();
220 
221         if let Some(file) = self.random_file.as_ref() {
222             let random_file = file.try_clone().map_err(|e| {
223                 error!("failed cloning rng source: {}", e);
224                 ActivateError::BadActivate
225             })?;
226             let mut handler = RngEpollHandler {
227                 queues,
228                 mem,
229                 random_file,
230                 interrupt_cb,
231                 queue_evt: queue_evts.remove(0),
232                 kill_evt,
233                 pause_evt,
234             };
235 
236             let paused = self.common.paused.clone();
237             let paused_sync = self.common.paused_sync.clone();
238             let mut epoll_threads = Vec::new();
239             // Retrieve seccomp filter for virtio_rng thread
240             let virtio_rng_seccomp_filter =
241                 get_seccomp_filter(&self.seccomp_action, Thread::VirtioRng)
242                     .map_err(ActivateError::CreateSeccompFilter)?;
243             thread::Builder::new()
244                 .name(self.id.clone())
245                 .spawn(move || {
246                     if let Err(e) = SeccompFilter::apply(virtio_rng_seccomp_filter) {
247                         error!("Error applying seccomp filter: {:?}", e);
248                     } else if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
249                         error!("Error running worker: {:?}", e);
250                     }
251                 })
252                 .map(|thread| epoll_threads.push(thread))
253                 .map_err(|e| {
254                     error!("failed to clone the virtio-rng epoll thread: {}", e);
255                     ActivateError::BadActivate
256                 })?;
257 
258             self.common.epoll_threads = Some(epoll_threads);
259 
260             event!("virtio-device", "activated", "id", &self.id);
261             return Ok(());
262         }
263         Err(ActivateError::BadActivate)
264     }
265 
266     fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
267         let result = self.common.reset();
268         event!("virtio-device", "reset", "id", &self.id);
269         result
270     }
271 }
272 
273 impl Pausable for Rng {
274     fn pause(&mut self) -> result::Result<(), MigratableError> {
275         self.common.pause()
276     }
277 
278     fn resume(&mut self) -> result::Result<(), MigratableError> {
279         self.common.resume()
280     }
281 }
282 
283 impl Snapshottable for Rng {
284     fn id(&self) -> String {
285         self.id.clone()
286     }
287 
288     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
289         Snapshot::new_from_versioned_state(&self.id, &self.state())
290     }
291 
292     fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
293         self.set_state(&snapshot.to_versioned_state(&self.id)?);
294         Ok(())
295     }
296 }
297 
298 impl Transportable for Rng {}
299 impl Migratable for Rng {}
300