xref: /cloud-hypervisor/virtio-devices/src/rng.rs (revision 8e2973fe7cc5a0e2c212fc327014ba6efb77b8c8)
18e7579b2SChao Peng // Copyright 2017 The Chromium OS Authors. All rights reserved.
28e7579b2SChao Peng // Use of this source code is governed by a BSD-style license that can be
38e7579b2SChao Peng // found in the LICENSE file.
45e9886bbSRuslan Mstoi //
55e9886bbSRuslan Mstoi // SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause
68e7579b2SChao Peng 
788a9f799SRob Bradford use std::fs::File;
888a9f799SRob Bradford use std::os::unix::io::AsRawFd;
988a9f799SRob Bradford use std::sync::atomic::AtomicBool;
1088a9f799SRob Bradford use std::sync::{Arc, Barrier};
1161e57e1cSRuoqing He use std::{io, result};
1288a9f799SRob Bradford 
1388a9f799SRob Bradford use anyhow::anyhow;
1488a9f799SRob Bradford use seccompiler::SeccompAction;
1588a9f799SRob Bradford use serde::{Deserialize, Serialize};
1688a9f799SRob Bradford use thiserror::Error;
1788a9f799SRob Bradford use virtio_queue::{Queue, QueueT};
1888a9f799SRob Bradford use vm_memory::{GuestAddressSpace, GuestMemory, GuestMemoryAtomic};
1988a9f799SRob Bradford use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
2088a9f799SRob Bradford use vm_virtio::{AccessPlatform, Translatable};
2188a9f799SRob Bradford use vmm_sys_util::eventfd::EventFd;
2288a9f799SRob Bradford 
23b2589d4fSRob Bradford use super::{
2461e57e1cSRuoqing He     ActivateError, ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler,
2561e57e1cSRuoqing He     Error as DeviceError, VirtioCommon, VirtioDevice, VirtioDeviceType, EPOLL_HELPER_EVENT_LAST,
2661e57e1cSRuoqing He     VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_VERSION_1,
27b2589d4fSRob Bradford };
2854e523c3SRob Bradford use crate::seccomp_filters::Thread;
2954e523c3SRob Bradford use crate::thread_helper::spawn_virtio_thread;
3061e57e1cSRuoqing He use crate::{GuestMemoryMmap, VirtioInterrupt, VirtioInterruptType};
318e7579b2SChao Peng 
328e7579b2SChao Peng const QUEUE_SIZE: u16 = 256;
338e7579b2SChao Peng const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
348e7579b2SChao Peng 
358e7579b2SChao Peng // New descriptors are pending on the virtio queue.
36e093f0e8SRob Bradford const QUEUE_AVAIL_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
378e7579b2SChao Peng 
381c1bff93SBo Chen #[derive(Error, Debug)]
391c1bff93SBo Chen enum Error {
401c1bff93SBo Chen     #[error("Descriptor chain too short")]
411c1bff93SBo Chen     DescriptorChainTooShort,
42f0c55f52SBo Chen     #[error("Invalid descriptor")]
43f0c55f52SBo Chen     InvalidDescriptor,
44*8e2973feSPhilipp Schuster     #[error("Failed to write to guest memory")]
4528e0a954SPhilipp Schuster     GuestMemoryWrite(#[source] vm_memory::guest_memory::Error),
46*8e2973feSPhilipp Schuster     #[error("Failed adding used index")]
4728e0a954SPhilipp Schuster     QueueAddUsed(#[source] virtio_queue::Error),
481c1bff93SBo Chen }
491c1bff93SBo Chen 
508e7579b2SChao Peng struct RngEpollHandler {
51a423bf13SSebastien Boeuf     mem: GuestMemoryAtomic<GuestMemoryMmap>,
52a423bf13SSebastien Boeuf     queue: Queue,
538e7579b2SChao Peng     random_file: File,
54c396bacaSSebastien Boeuf     interrupt_cb: Arc<dyn VirtioInterrupt>,
558e7579b2SChao Peng     queue_evt: EventFd,
568e7579b2SChao Peng     kill_evt: EventFd,
57dae0b2efSSamuel Ortiz     pause_evt: EventFd,
5809f5b82fSSebastien Boeuf     access_platform: Option<Arc<dyn AccessPlatform>>,
598e7579b2SChao Peng }
608e7579b2SChao Peng 
618e7579b2SChao Peng impl RngEpollHandler {
process_queue(&mut self) -> result::Result<bool, Error>621c1bff93SBo Chen     fn process_queue(&mut self) -> result::Result<bool, Error> {
633f62a172SSebastien Boeuf         let queue = &mut self.queue;
648e7579b2SChao Peng 
65a4859ffeSSebastien Boeuf         let mut used_descs = false;
6687f57f7cSSebastien Boeuf         while let Some(mut desc_chain) = queue.pop_descriptor_chain(self.mem.memory()) {
671c1bff93SBo Chen             let desc = desc_chain.next().ok_or(Error::DescriptorChainTooShort)?;
688e7579b2SChao Peng 
69f0c55f52SBo Chen             // The descriptor must be write-only and non-zero length
70f0c55f52SBo Chen             if !(desc.is_write_only() && desc.len() > 0) {
71f0c55f52SBo Chen                 return Err(Error::InvalidDescriptor);
72f0c55f52SBo Chen             }
73f0c55f52SBo Chen 
748e7579b2SChao Peng             // Fill the read with data from the random device on the host.
75f0c55f52SBo Chen             let len = desc_chain
76f0c55f52SBo Chen                 .memory()
77d4892f41SBo Chen                 .read_volatile_from(
7877df4e67SSebastien Boeuf                     desc.addr()
79059e787cSSebastien Boeuf                         .translate_gva(self.access_platform.as_ref(), desc.len() as usize),
8077df4e67SSebastien Boeuf                     &mut self.random_file,
8177df4e67SSebastien Boeuf                     desc.len() as usize,
82f0c55f52SBo Chen                 )
8331ca22d4SRob Bradford                 .map_err(Error::GuestMemoryWrite)?;
848e7579b2SChao Peng 
85a4859ffeSSebastien Boeuf             queue
86f0c55f52SBo Chen                 .add_used(desc_chain.memory(), desc_chain.head_index(), len as u32)
871c1bff93SBo Chen                 .map_err(Error::QueueAddUsed)?;
88a4859ffeSSebastien Boeuf             used_descs = true;
898e7579b2SChao Peng         }
908e7579b2SChao Peng 
911c1bff93SBo Chen         Ok(used_descs)
928e7579b2SChao Peng     }
938e7579b2SChao Peng 
signal_used_queue(&self) -> result::Result<(), DeviceError>948e7579b2SChao Peng     fn signal_used_queue(&self) -> result::Result<(), DeviceError> {
95c396bacaSSebastien Boeuf         self.interrupt_cb
96de3e003eSSebastien Boeuf             .trigger(VirtioInterruptType::Queue(0))
97c396bacaSSebastien Boeuf             .map_err(|e| {
988e7579b2SChao Peng                 error!("Failed to signal used queue: {:?}", e);
998e7579b2SChao Peng                 DeviceError::FailedSignalingUsedQueue(e)
1008e7579b2SChao Peng             })
1018e7579b2SChao Peng     }
1028e7579b2SChao Peng 
run( &mut self, paused: Arc<AtomicBool>, paused_sync: Arc<Barrier>, ) -> result::Result<(), EpollHelperError>103aa57762cSSebastien Boeuf     fn run(
104aa57762cSSebastien Boeuf         &mut self,
105aa57762cSSebastien Boeuf         paused: Arc<AtomicBool>,
106aa57762cSSebastien Boeuf         paused_sync: Arc<Barrier>,
107aa57762cSSebastien Boeuf     ) -> result::Result<(), EpollHelperError> {
108e093f0e8SRob Bradford         let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
109e093f0e8SRob Bradford         helper.add_event(self.queue_evt.as_raw_fd(), QUEUE_AVAIL_EVENT)?;
110aa57762cSSebastien Boeuf         helper.run(paused, paused_sync, self)?;
1118e7579b2SChao Peng 
112e093f0e8SRob Bradford         Ok(())
113e093f0e8SRob Bradford     }
114e382dc66SSebastien Boeuf }
115e382dc66SSebastien Boeuf 
116e093f0e8SRob Bradford impl EpollHelperHandler for RngEpollHandler {
handle_event( &mut self, _helper: &mut EpollHelper, event: &epoll::Event, ) -> result::Result<(), EpollHelperError>117b1752994SBo Chen     fn handle_event(
118b1752994SBo Chen         &mut self,
119b1752994SBo Chen         _helper: &mut EpollHelper,
120b1752994SBo Chen         event: &epoll::Event,
121b1752994SBo Chen     ) -> result::Result<(), EpollHelperError> {
12201e7bd72SSebastien Boeuf         let ev_type = event.data as u16;
12301e7bd72SSebastien Boeuf         match ev_type {
1248e7579b2SChao Peng             QUEUE_AVAIL_EVENT => {
125b1752994SBo Chen                 self.queue_evt.read().map_err(|e| {
126b1752994SBo Chen                     EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
127b1752994SBo Chen                 })?;
1281c1bff93SBo Chen                 let needs_notification = self.process_queue().map_err(|e| {
1291c1bff93SBo Chen                     EpollHelperError::HandleEvent(anyhow!("Failed to process queue : {:?}", e))
1301c1bff93SBo Chen                 })?;
1311c1bff93SBo Chen                 if needs_notification {
132b1752994SBo Chen                     self.signal_used_queue().map_err(|e| {
133b1752994SBo Chen                         EpollHelperError::HandleEvent(anyhow!(
134b1752994SBo Chen                             "Failed to signal used queue: {:?}",
135b1752994SBo Chen                             e
136b1752994SBo Chen                         ))
137b1752994SBo Chen                     })?;
1388e7579b2SChao Peng                 }
1398e7579b2SChao Peng             }
1408e7579b2SChao Peng             _ => {
141b1752994SBo Chen                 return Err(EpollHelperError::HandleEvent(anyhow!(
142b1752994SBo Chen                     "Unexpected event: {}",
143b1752994SBo Chen                     ev_type
144b1752994SBo Chen                 )));
1458e7579b2SChao Peng             }
1468e7579b2SChao Peng         }
147b1752994SBo Chen         Ok(())
1488e7579b2SChao Peng     }
1498e7579b2SChao Peng }
1508e7579b2SChao Peng 
1518e7579b2SChao Peng /// Virtio device for exposing entropy to the guest OS through virtio.
1528e7579b2SChao Peng pub struct Rng {
15337e99bbbSRob Bradford     common: VirtioCommon,
1542e91b738SSebastien Boeuf     id: String,
1558e7579b2SChao Peng     random_file: Option<File>,
156a4262211SBo Chen     seccomp_action: SeccompAction,
157687d646cSRob Bradford     exit_evt: EventFd,
1588e7579b2SChao Peng }
1598e7579b2SChao Peng 
16010ab87d6SRob Bradford #[derive(Deserialize, Serialize)]
161a484aa7bSSamuel Ortiz pub struct RngState {
162a484aa7bSSamuel Ortiz     pub avail_features: u64,
163a484aa7bSSamuel Ortiz     pub acked_features: u64,
164a484aa7bSSamuel Ortiz }
165a484aa7bSSamuel Ortiz 
1668e7579b2SChao Peng impl Rng {
1678e7579b2SChao Peng     /// Create a new virtio rng device that gets random data from /dev/urandom.
new( id: String, path: &str, iommu: bool, seccomp_action: SeccompAction, exit_evt: EventFd, state: Option<RngState>, ) -> io::Result<Rng>168a4262211SBo Chen     pub fn new(
169a4262211SBo Chen         id: String,
170a4262211SBo Chen         path: &str,
171a4262211SBo Chen         iommu: bool,
172a4262211SBo Chen         seccomp_action: SeccompAction,
173687d646cSRob Bradford         exit_evt: EventFd,
1741f0e5eb6SSebastien Boeuf         state: Option<RngState>,
175a4262211SBo Chen     ) -> io::Result<Rng> {
1768e7579b2SChao Peng         let random_file = File::open(path)?;
1771f0e5eb6SSebastien Boeuf 
178b62a40efSSebastien Boeuf         let (avail_features, acked_features, paused) = if let Some(state) = state {
1791f0e5eb6SSebastien Boeuf             info!("Restoring virtio-rng {}", id);
180b62a40efSSebastien Boeuf             (state.avail_features, state.acked_features, true)
1811f0e5eb6SSebastien Boeuf         } else {
1829ab00dcbSSebastien Boeuf             let mut avail_features = 1u64 << VIRTIO_F_VERSION_1;
1839ab00dcbSSebastien Boeuf 
1849ab00dcbSSebastien Boeuf             if iommu {
1859ab00dcbSSebastien Boeuf                 avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
1869ab00dcbSSebastien Boeuf             }
1878e7579b2SChao Peng 
188b62a40efSSebastien Boeuf             (avail_features, 0, false)
1891f0e5eb6SSebastien Boeuf         };
1901f0e5eb6SSebastien Boeuf 
1918e7579b2SChao Peng         Ok(Rng {
19237e99bbbSRob Bradford             common: VirtioCommon {
193aa34d545SRob Bradford                 device_type: VirtioDeviceType::Rng as u32,
194c37fb5b6SRob Bradford                 queue_sizes: QUEUE_SIZES.to_vec(),
195c37fb5b6SRob Bradford                 paused_sync: Some(Arc::new(Barrier::new(2))),
19637e99bbbSRob Bradford                 avail_features,
1971f0e5eb6SSebastien Boeuf                 acked_features,
198c90f77e3SRob Bradford                 min_queues: 1,
199b62a40efSSebastien Boeuf                 paused: Arc::new(AtomicBool::new(paused)),
200a9a13846SRob Bradford                 ..Default::default()
20137e99bbbSRob Bradford             },
2022e91b738SSebastien Boeuf             id,
2038e7579b2SChao Peng             random_file: Some(random_file),
204a4262211SBo Chen             seccomp_action,
205687d646cSRob Bradford             exit_evt,
2068e7579b2SChao Peng         })
2078e7579b2SChao Peng     }
208a484aa7bSSamuel Ortiz 
state(&self) -> RngState209a484aa7bSSamuel Ortiz     fn state(&self) -> RngState {
210a484aa7bSSamuel Ortiz         RngState {
21137e99bbbSRob Bradford             avail_features: self.common.avail_features,
21237e99bbbSRob Bradford             acked_features: self.common.acked_features,
213a484aa7bSSamuel Ortiz         }
214a484aa7bSSamuel Ortiz     }
215a484aa7bSSamuel Ortiz 
216194b59f4SRob Bradford     #[cfg(fuzzing)]
wait_for_epoll_threads(&mut self)217194b59f4SRob Bradford     pub fn wait_for_epoll_threads(&mut self) {
218194b59f4SRob Bradford         self.common.wait_for_epoll_threads();
219194b59f4SRob Bradford     }
2208e7579b2SChao Peng }
2218e7579b2SChao Peng 
2228e7579b2SChao Peng impl Drop for Rng {
drop(&mut self)2238e7579b2SChao Peng     fn drop(&mut self) {
224c37fb5b6SRob Bradford         if let Some(kill_evt) = self.common.kill_evt.take() {
2258e7579b2SChao Peng             // Ignore the result because there is nothing we can do about it.
2268e7579b2SChao Peng             let _ = kill_evt.write(1);
2278e7579b2SChao Peng         }
228ad6c0ee5SPhilipp Schuster         self.common.wait_for_epoll_threads();
2298e7579b2SChao Peng     }
2308e7579b2SChao Peng }
2318e7579b2SChao Peng 
2328e7579b2SChao Peng impl VirtioDevice for Rng {
device_type(&self) -> u322338e7579b2SChao Peng     fn device_type(&self) -> u32 {
234c37fb5b6SRob Bradford         self.common.device_type
2358e7579b2SChao Peng     }
2368e7579b2SChao Peng 
queue_max_sizes(&self) -> &[u16]2378e7579b2SChao Peng     fn queue_max_sizes(&self) -> &[u16] {
238c37fb5b6SRob Bradford         &self.common.queue_sizes
2398e7579b2SChao Peng     }
2408e7579b2SChao Peng 
features(&self) -> u6424114eddf72SCathy Zhang     fn features(&self) -> u64 {
24237e99bbbSRob Bradford         self.common.avail_features
2438e7579b2SChao Peng     }
2448e7579b2SChao Peng 
ack_features(&mut self, value: u64)24514eddf72SCathy Zhang     fn ack_features(&mut self, value: u64) {
24637e99bbbSRob Bradford         self.common.ack_features(value)
2478e7579b2SChao Peng     }
2488e7579b2SChao Peng 
activate( &mut self, mem: GuestMemoryAtomic<GuestMemoryMmap>, interrupt_cb: Arc<dyn VirtioInterrupt>, mut queues: Vec<(usize, Queue, EventFd)>, ) -> ActivateResult2498e7579b2SChao Peng     fn activate(
2508e7579b2SChao Peng         &mut self,
251a423bf13SSebastien Boeuf         mem: GuestMemoryAtomic<GuestMemoryMmap>,
252c396bacaSSebastien Boeuf         interrupt_cb: Arc<dyn VirtioInterrupt>,
253a423bf13SSebastien Boeuf         mut queues: Vec<(usize, Queue, EventFd)>,
2548e7579b2SChao Peng     ) -> ActivateResult {
2553f62a172SSebastien Boeuf         self.common.activate(&queues, &interrupt_cb)?;
256280bef83SRob Bradford         let (kill_evt, pause_evt) = self.common.dup_eventfds();
257eb91bc81SSebastien Boeuf 
258eb91bc81SSebastien Boeuf         if let Some(file) = self.random_file.as_ref() {
259eb91bc81SSebastien Boeuf             let random_file = file.try_clone().map_err(|e| {
260eb91bc81SSebastien Boeuf                 error!("failed cloning rng source: {}", e);
261eb91bc81SSebastien Boeuf                 ActivateError::BadActivate
262eb91bc81SSebastien Boeuf             })?;
2633f62a172SSebastien Boeuf 
2643f62a172SSebastien Boeuf             let (_, queue, queue_evt) = queues.remove(0);
2653f62a172SSebastien Boeuf 
2668e7579b2SChao Peng             let mut handler = RngEpollHandler {
267a423bf13SSebastien Boeuf                 mem,
2683f62a172SSebastien Boeuf                 queue,
2698e7579b2SChao Peng                 random_file,
270d3c7b455SSebastien Boeuf                 interrupt_cb,
2713f62a172SSebastien Boeuf                 queue_evt,
2728e7579b2SChao Peng                 kill_evt,
273dae0b2efSSamuel Ortiz                 pause_evt,
27409f5b82fSSebastien Boeuf                 access_platform: self.common.access_platform.clone(),
2758e7579b2SChao Peng             };
2768e7579b2SChao Peng 
277c37fb5b6SRob Bradford             let paused = self.common.paused.clone();
278c37fb5b6SRob Bradford             let paused_sync = self.common.paused_sync.clone();
279f648f285SSamuel Ortiz             let mut epoll_threads = Vec::new();
28054e523c3SRob Bradford             spawn_virtio_thread(
28154e523c3SRob Bradford                 &self.id,
28254e523c3SRob Bradford                 &self.seccomp_action,
28354e523c3SRob Bradford                 Thread::VirtioRng,
28454e523c3SRob Bradford                 &mut epoll_threads,
285687d646cSRob Bradford                 &self.exit_evt,
286df5b803aSBo Chen                 move || handler.run(paused, paused_sync.unwrap()),
28754e523c3SRob Bradford             )?;
2888e7579b2SChao Peng 
289c37fb5b6SRob Bradford             self.common.epoll_threads = Some(epoll_threads);
290f648f285SSamuel Ortiz 
291c89095abSRob Bradford             event!("virtio-device", "activated", "id", &self.id);
2928e7579b2SChao Peng             return Ok(());
2938e7579b2SChao Peng         }
2948e7579b2SChao Peng         Err(ActivateError::BadActivate)
2958e7579b2SChao Peng     }
296eb91bc81SSebastien Boeuf 
reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>>29723f9ec50SRob Bradford     fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
298c89095abSRob Bradford         let result = self.common.reset();
299c89095abSRob Bradford         event!("virtio-device", "reset", "id", &self.id);
300c89095abSRob Bradford         result
301eb91bc81SSebastien Boeuf     }
30209f5b82fSSebastien Boeuf 
set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>)30309f5b82fSSebastien Boeuf     fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) {
30409f5b82fSSebastien Boeuf         self.common.set_access_platform(access_platform)
30509f5b82fSSebastien Boeuf     }
3068e7579b2SChao Peng }
307dae0b2efSSamuel Ortiz 
308c37fb5b6SRob Bradford impl Pausable for Rng {
pause(&mut self) -> result::Result<(), MigratableError>309c37fb5b6SRob Bradford     fn pause(&mut self) -> result::Result<(), MigratableError> {
310c37fb5b6SRob Bradford         self.common.pause()
311c37fb5b6SRob Bradford     }
312c37fb5b6SRob Bradford 
resume(&mut self) -> result::Result<(), MigratableError>313c37fb5b6SRob Bradford     fn resume(&mut self) -> result::Result<(), MigratableError> {
314c37fb5b6SRob Bradford         self.common.resume()
315c37fb5b6SRob Bradford     }
316c37fb5b6SRob Bradford }
317c37fb5b6SRob Bradford 
318a484aa7bSSamuel Ortiz impl Snapshottable for Rng {
id(&self) -> String319a484aa7bSSamuel Ortiz     fn id(&self) -> String {
3202e91b738SSebastien Boeuf         self.id.clone()
321a484aa7bSSamuel Ortiz     }
322a484aa7bSSamuel Ortiz 
snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError>323871138d5SSebastien Boeuf     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
32410ab87d6SRob Bradford         Snapshot::new_from_state(&self.state())
325a484aa7bSSamuel Ortiz     }
326a484aa7bSSamuel Ortiz }
327a484aa7bSSamuel Ortiz 
3281b1a2175SSamuel Ortiz impl Transportable for Rng {}
329dae0b2efSSamuel Ortiz impl Migratable for Rng {}
330