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