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