xref: /aosp_15_r20/external/crosvm/devices/src/virtio/rng.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2017 The ChromiumOS Authors
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 std::collections::BTreeMap;
6 use std::io::Write;
7 
8 use anyhow::anyhow;
9 use anyhow::Context;
10 use base::error;
11 use base::warn;
12 use base::Event;
13 use base::EventToken;
14 use base::RawDescriptor;
15 use base::WaitContext;
16 use base::WorkerThread;
17 use rand::rngs::OsRng;
18 use rand::RngCore;
19 use vm_memory::GuestMemory;
20 
21 use super::DeviceType;
22 use super::Interrupt;
23 use super::Queue;
24 use super::VirtioDevice;
25 
26 const QUEUE_SIZE: u16 = 256;
27 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
28 
29 // Chosen to match the Linux guest driver RNG buffer refill size.
30 const CHUNK_SIZE: usize = 64;
31 
32 struct Worker {
33     interrupt: Interrupt,
34     queue: Queue,
35 }
36 
37 impl Worker {
process_queue(&mut self)38     fn process_queue(&mut self) {
39         let mut rand_bytes = [0u8; CHUNK_SIZE];
40         let mut needs_interrupt = false;
41 
42         while let Some(mut avail_desc) = self.queue.pop() {
43             let writer = &mut avail_desc.writer;
44             while writer.available_bytes() > 0 {
45                 let chunk_size = writer.available_bytes().min(CHUNK_SIZE);
46                 let chunk = &mut rand_bytes[..chunk_size];
47                 OsRng.fill_bytes(chunk);
48                 if let Err(e) = writer.write_all(chunk) {
49                     warn!("Failed to write random data to the guest: {}", e);
50                     break;
51                 }
52             }
53 
54             let written_size = writer.bytes_written();
55             self.queue.add_used(avail_desc, written_size as u32);
56             needs_interrupt = true;
57         }
58 
59         if needs_interrupt {
60             self.queue.trigger_interrupt();
61         }
62     }
63 
run(&mut self, kill_evt: Event) -> anyhow::Result<()>64     fn run(&mut self, kill_evt: Event) -> anyhow::Result<()> {
65         #[derive(EventToken)]
66         enum Token {
67             QueueAvailable,
68             InterruptResample,
69             Kill,
70         }
71 
72         let wait_ctx = WaitContext::build_with(&[
73             (self.queue.event(), Token::QueueAvailable),
74             (&kill_evt, Token::Kill),
75         ])
76         .context("failed creating WaitContext")?;
77 
78         if let Some(resample_evt) = self.interrupt.get_resample_evt() {
79             wait_ctx
80                 .add(resample_evt, Token::InterruptResample)
81                 .context("failed adding resample event to WaitContext.")?;
82         }
83 
84         let mut exiting = false;
85         while !exiting {
86             let events = wait_ctx.wait().context("failed polling for events")?;
87             for event in events.iter().filter(|e| e.is_readable) {
88                 match event.token {
89                     Token::QueueAvailable => {
90                         self.queue
91                             .event()
92                             .wait()
93                             .context("failed reading queue Event")?;
94                         self.process_queue();
95                     }
96                     Token::InterruptResample => {
97                         self.interrupt.interrupt_resample();
98                     }
99                     Token::Kill => exiting = true,
100                 }
101             }
102         }
103 
104         Ok(())
105     }
106 }
107 
108 /// Virtio device for exposing entropy to the guest OS through virtio.
109 pub struct Rng {
110     worker_thread: Option<WorkerThread<Worker>>,
111     virtio_features: u64,
112 }
113 
114 impl Rng {
115     /// Create a new virtio rng device that gets random data from /dev/urandom.
new(virtio_features: u64) -> anyhow::Result<Rng>116     pub fn new(virtio_features: u64) -> anyhow::Result<Rng> {
117         Ok(Rng {
118             worker_thread: None,
119             virtio_features,
120         })
121     }
122 }
123 
124 impl VirtioDevice for Rng {
keep_rds(&self) -> Vec<RawDescriptor>125     fn keep_rds(&self) -> Vec<RawDescriptor> {
126         Vec::new()
127     }
128 
device_type(&self) -> DeviceType129     fn device_type(&self) -> DeviceType {
130         DeviceType::Rng
131     }
132 
queue_max_sizes(&self) -> &[u16]133     fn queue_max_sizes(&self) -> &[u16] {
134         QUEUE_SIZES
135     }
136 
features(&self) -> u64137     fn features(&self) -> u64 {
138         self.virtio_features
139     }
140 
activate( &mut self, _mem: GuestMemory, interrupt: Interrupt, mut queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>141     fn activate(
142         &mut self,
143         _mem: GuestMemory,
144         interrupt: Interrupt,
145         mut queues: BTreeMap<usize, Queue>,
146     ) -> anyhow::Result<()> {
147         if queues.len() != 1 {
148             return Err(anyhow!("expected 1 queue, got {}", queues.len()));
149         }
150 
151         let queue = queues.remove(&0).unwrap();
152 
153         self.worker_thread = Some(WorkerThread::start("v_rng", move |kill_evt| {
154             let mut worker = Worker { interrupt, queue };
155             if let Err(e) = worker.run(kill_evt) {
156                 error!("rng worker thread failed: {:#}", e);
157             }
158             worker
159         }));
160 
161         Ok(())
162     }
163 
reset(&mut self) -> anyhow::Result<()>164     fn reset(&mut self) -> anyhow::Result<()> {
165         if let Some(worker_thread) = self.worker_thread.take() {
166             let _worker = worker_thread.stop();
167         }
168         Ok(())
169     }
170 
virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>>171     fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> {
172         if let Some(worker_thread) = self.worker_thread.take() {
173             let worker = worker_thread.stop();
174             return Ok(Some(BTreeMap::from([(0, worker.queue)])));
175         }
176         Ok(None)
177     }
178 
virtio_wake( &mut self, queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>, ) -> anyhow::Result<()>179     fn virtio_wake(
180         &mut self,
181         queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>,
182     ) -> anyhow::Result<()> {
183         if let Some((mem, interrupt, queues)) = queues_state {
184             self.activate(mem, interrupt, queues)?;
185         }
186         Ok(())
187     }
188 
virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value>189     fn virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value> {
190         // `virtio_sleep` ensures there is no pending state, except for the `Queue`s, which are
191         // handled at a higher layer.
192         Ok(serde_json::Value::Null)
193     }
194 
virtio_restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>195     fn virtio_restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> {
196         anyhow::ensure!(
197             data == serde_json::Value::Null,
198             "unexpected snapshot data: should be null, got {}",
199             data,
200         );
201         Ok(())
202     }
203 }
204