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