xref: /cloud-hypervisor/virtio-devices/src/rng.rs (revision d10f20eb718023742143fa847a37f3d6114ead52)
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 // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
6 
7 use super::Error as DeviceError;
8 use super::{
9     ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon,
10     VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM,
11     VIRTIO_F_VERSION_1,
12 };
13 use crate::seccomp_filters::Thread;
14 use crate::thread_helper::spawn_virtio_thread;
15 use crate::GuestMemoryMmap;
16 use crate::{VirtioInterrupt, VirtioInterruptType};
17 use anyhow::anyhow;
18 use seccompiler::SeccompAction;
19 use serde::{Deserialize, Serialize};
20 use std::fs::File;
21 use std::io;
22 use std::os::unix::io::AsRawFd;
23 use std::result;
24 use std::sync::atomic::AtomicBool;
25 use std::sync::{Arc, Barrier};
26 use thiserror::Error;
27 use virtio_queue::{Queue, QueueT};
28 use vm_memory::{GuestAddressSpace, GuestMemory, GuestMemoryAtomic};
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(Deserialize, Serialize)]
162 pub struct RngState {
163     pub avail_features: u64,
164     pub acked_features: u64,
165 }
166 
167 impl Rng {
168     /// Create a new virtio rng device that gets random data from /dev/urandom.
169     pub fn new(
170         id: String,
171         path: &str,
172         iommu: bool,
173         seccomp_action: SeccompAction,
174         exit_evt: EventFd,
175         state: Option<RngState>,
176     ) -> io::Result<Rng> {
177         let random_file = File::open(path)?;
178 
179         let (avail_features, acked_features, paused) = if let Some(state) = state {
180             info!("Restoring virtio-rng {}", id);
181             (state.avail_features, state.acked_features, true)
182         } else {
183             let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
184 
185             if iommu {
186                 avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
187             }
188 
189             (avail_features, 0, false)
190         };
191 
192         Ok(Rng {
193             common: VirtioCommon {
194                 device_type: VirtioDeviceType::Rng as u32,
195                 queue_sizes: QUEUE_SIZES.to_vec(),
196                 paused_sync: Some(Arc::new(Barrier::new(2))),
197                 avail_features,
198                 acked_features,
199                 min_queues: 1,
200                 paused: Arc::new(AtomicBool::new(paused)),
201                 ..Default::default()
202             },
203             id,
204             random_file: Some(random_file),
205             seccomp_action,
206             exit_evt,
207         })
208     }
209 
210     fn state(&self) -> RngState {
211         RngState {
212             avail_features: self.common.avail_features,
213             acked_features: self.common.acked_features,
214         }
215     }
216 
217     #[cfg(fuzzing)]
218     pub fn wait_for_epoll_threads(&mut self) {
219         self.common.wait_for_epoll_threads();
220     }
221 }
222 
223 impl Drop for Rng {
224     fn drop(&mut self) {
225         if let Some(kill_evt) = self.common.kill_evt.take() {
226             // Ignore the result because there is nothing we can do about it.
227             let _ = kill_evt.write(1);
228         }
229         self.common.wait_for_epoll_threads();
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_state(&self.state())
326     }
327 }
328 
329 impl Transportable for Rng {}
330 impl Migratable for Rng {}
331