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