1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2020 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 //! Virtio console device. 6*bb4ee6a4SAndroid Build Coastguard Worker 7*bb4ee6a4SAndroid Build Coastguard Worker pub mod control; 8*bb4ee6a4SAndroid Build Coastguard Worker pub mod device; 9*bb4ee6a4SAndroid Build Coastguard Worker pub mod input; 10*bb4ee6a4SAndroid Build Coastguard Worker pub mod output; 11*bb4ee6a4SAndroid Build Coastguard Worker pub mod port; 12*bb4ee6a4SAndroid Build Coastguard Worker pub mod worker; 13*bb4ee6a4SAndroid Build Coastguard Worker 14*bb4ee6a4SAndroid Build Coastguard Worker mod sys; 15*bb4ee6a4SAndroid Build Coastguard Worker 16*bb4ee6a4SAndroid Build Coastguard Worker use std::collections::BTreeMap; 17*bb4ee6a4SAndroid Build Coastguard Worker 18*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Context; 19*bb4ee6a4SAndroid Build Coastguard Worker use base::RawDescriptor; 20*bb4ee6a4SAndroid Build Coastguard Worker use hypervisor::ProtectionType; 21*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestMemory; 22*bb4ee6a4SAndroid Build Coastguard Worker 23*bb4ee6a4SAndroid Build Coastguard Worker use crate::serial::sys::InStreamType; 24*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::console::device::ConsoleDevice; 25*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::console::device::ConsoleSnapshot; 26*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::console::port::ConsolePort; 27*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::DeviceType; 28*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Interrupt; 29*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::Queue; 30*bb4ee6a4SAndroid Build Coastguard Worker use crate::virtio::VirtioDevice; 31*bb4ee6a4SAndroid Build Coastguard Worker use crate::PciAddress; 32*bb4ee6a4SAndroid Build Coastguard Worker 33*bb4ee6a4SAndroid Build Coastguard Worker const QUEUE_SIZE: u16 = 256; 34*bb4ee6a4SAndroid Build Coastguard Worker 35*bb4ee6a4SAndroid Build Coastguard Worker /// Virtio console device. 36*bb4ee6a4SAndroid Build Coastguard Worker pub struct Console { 37*bb4ee6a4SAndroid Build Coastguard Worker console: ConsoleDevice, 38*bb4ee6a4SAndroid Build Coastguard Worker queue_sizes: Vec<u16>, 39*bb4ee6a4SAndroid Build Coastguard Worker pci_address: Option<PciAddress>, 40*bb4ee6a4SAndroid Build Coastguard Worker } 41*bb4ee6a4SAndroid Build Coastguard Worker 42*bb4ee6a4SAndroid Build Coastguard Worker impl Console { new( protection_type: ProtectionType, input: Option<InStreamType>, output: Option<Box<dyn std::io::Write + Send>>, keep_rds: Vec<RawDescriptor>, pci_address: Option<PciAddress>, ) -> Console43*bb4ee6a4SAndroid Build Coastguard Worker fn new( 44*bb4ee6a4SAndroid Build Coastguard Worker protection_type: ProtectionType, 45*bb4ee6a4SAndroid Build Coastguard Worker input: Option<InStreamType>, 46*bb4ee6a4SAndroid Build Coastguard Worker output: Option<Box<dyn std::io::Write + Send>>, 47*bb4ee6a4SAndroid Build Coastguard Worker keep_rds: Vec<RawDescriptor>, 48*bb4ee6a4SAndroid Build Coastguard Worker pci_address: Option<PciAddress>, 49*bb4ee6a4SAndroid Build Coastguard Worker ) -> Console { 50*bb4ee6a4SAndroid Build Coastguard Worker let port = ConsolePort::new(input, output, None, keep_rds); 51*bb4ee6a4SAndroid Build Coastguard Worker let console = ConsoleDevice::new_single_port(protection_type, port); 52*bb4ee6a4SAndroid Build Coastguard Worker let queue_sizes = vec![QUEUE_SIZE; console.max_queues()]; 53*bb4ee6a4SAndroid Build Coastguard Worker 54*bb4ee6a4SAndroid Build Coastguard Worker Console { 55*bb4ee6a4SAndroid Build Coastguard Worker console, 56*bb4ee6a4SAndroid Build Coastguard Worker queue_sizes, 57*bb4ee6a4SAndroid Build Coastguard Worker pci_address, 58*bb4ee6a4SAndroid Build Coastguard Worker } 59*bb4ee6a4SAndroid Build Coastguard Worker } 60*bb4ee6a4SAndroid Build Coastguard Worker } 61*bb4ee6a4SAndroid Build Coastguard Worker 62*bb4ee6a4SAndroid Build Coastguard Worker impl VirtioDevice for Console { keep_rds(&self) -> Vec<RawDescriptor>63*bb4ee6a4SAndroid Build Coastguard Worker fn keep_rds(&self) -> Vec<RawDescriptor> { 64*bb4ee6a4SAndroid Build Coastguard Worker self.console.keep_rds() 65*bb4ee6a4SAndroid Build Coastguard Worker } 66*bb4ee6a4SAndroid Build Coastguard Worker features(&self) -> u6467*bb4ee6a4SAndroid Build Coastguard Worker fn features(&self) -> u64 { 68*bb4ee6a4SAndroid Build Coastguard Worker self.console.features() 69*bb4ee6a4SAndroid Build Coastguard Worker } 70*bb4ee6a4SAndroid Build Coastguard Worker device_type(&self) -> DeviceType71*bb4ee6a4SAndroid Build Coastguard Worker fn device_type(&self) -> DeviceType { 72*bb4ee6a4SAndroid Build Coastguard Worker DeviceType::Console 73*bb4ee6a4SAndroid Build Coastguard Worker } 74*bb4ee6a4SAndroid Build Coastguard Worker queue_max_sizes(&self) -> &[u16]75*bb4ee6a4SAndroid Build Coastguard Worker fn queue_max_sizes(&self) -> &[u16] { 76*bb4ee6a4SAndroid Build Coastguard Worker &self.queue_sizes 77*bb4ee6a4SAndroid Build Coastguard Worker } 78*bb4ee6a4SAndroid Build Coastguard Worker read_config(&self, offset: u64, data: &mut [u8])79*bb4ee6a4SAndroid Build Coastguard Worker fn read_config(&self, offset: u64, data: &mut [u8]) { 80*bb4ee6a4SAndroid Build Coastguard Worker self.console.read_config(offset, data); 81*bb4ee6a4SAndroid Build Coastguard Worker } 82*bb4ee6a4SAndroid Build Coastguard Worker on_device_sandboxed(&mut self)83*bb4ee6a4SAndroid Build Coastguard Worker fn on_device_sandboxed(&mut self) { 84*bb4ee6a4SAndroid Build Coastguard Worker self.console.start_input_threads(); 85*bb4ee6a4SAndroid Build Coastguard Worker } 86*bb4ee6a4SAndroid Build Coastguard Worker activate( &mut self, _mem: GuestMemory, _interrupt: Interrupt, queues: BTreeMap<usize, Queue>, ) -> anyhow::Result<()>87*bb4ee6a4SAndroid Build Coastguard Worker fn activate( 88*bb4ee6a4SAndroid Build Coastguard Worker &mut self, 89*bb4ee6a4SAndroid Build Coastguard Worker _mem: GuestMemory, 90*bb4ee6a4SAndroid Build Coastguard Worker _interrupt: Interrupt, 91*bb4ee6a4SAndroid Build Coastguard Worker queues: BTreeMap<usize, Queue>, 92*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<()> { 93*bb4ee6a4SAndroid Build Coastguard Worker for (idx, queue) in queues.into_iter() { 94*bb4ee6a4SAndroid Build Coastguard Worker self.console.start_queue(idx, queue)? 95*bb4ee6a4SAndroid Build Coastguard Worker } 96*bb4ee6a4SAndroid Build Coastguard Worker Ok(()) 97*bb4ee6a4SAndroid Build Coastguard Worker } 98*bb4ee6a4SAndroid Build Coastguard Worker pci_address(&self) -> Option<PciAddress>99*bb4ee6a4SAndroid Build Coastguard Worker fn pci_address(&self) -> Option<PciAddress> { 100*bb4ee6a4SAndroid Build Coastguard Worker self.pci_address 101*bb4ee6a4SAndroid Build Coastguard Worker } 102*bb4ee6a4SAndroid Build Coastguard Worker reset(&mut self) -> anyhow::Result<()>103*bb4ee6a4SAndroid Build Coastguard Worker fn reset(&mut self) -> anyhow::Result<()> { 104*bb4ee6a4SAndroid Build Coastguard Worker self.console.reset() 105*bb4ee6a4SAndroid Build Coastguard Worker } 106*bb4ee6a4SAndroid Build Coastguard Worker virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>>107*bb4ee6a4SAndroid Build Coastguard Worker fn virtio_sleep(&mut self) -> anyhow::Result<Option<BTreeMap<usize, Queue>>> { 108*bb4ee6a4SAndroid Build Coastguard Worker // Stop and collect all the queues. 109*bb4ee6a4SAndroid Build Coastguard Worker let mut queues = BTreeMap::new(); 110*bb4ee6a4SAndroid Build Coastguard Worker for idx in 0..self.console.max_queues() { 111*bb4ee6a4SAndroid Build Coastguard Worker if let Some(queue) = self 112*bb4ee6a4SAndroid Build Coastguard Worker .console 113*bb4ee6a4SAndroid Build Coastguard Worker .stop_queue(idx) 114*bb4ee6a4SAndroid Build Coastguard Worker .with_context(|| format!("failed to stop queue {idx}"))? 115*bb4ee6a4SAndroid Build Coastguard Worker { 116*bb4ee6a4SAndroid Build Coastguard Worker queues.insert(idx, queue); 117*bb4ee6a4SAndroid Build Coastguard Worker } 118*bb4ee6a4SAndroid Build Coastguard Worker } 119*bb4ee6a4SAndroid Build Coastguard Worker 120*bb4ee6a4SAndroid Build Coastguard Worker if !queues.is_empty() { 121*bb4ee6a4SAndroid Build Coastguard Worker Ok(Some(queues)) 122*bb4ee6a4SAndroid Build Coastguard Worker } else { 123*bb4ee6a4SAndroid Build Coastguard Worker Ok(None) 124*bb4ee6a4SAndroid Build Coastguard Worker } 125*bb4ee6a4SAndroid Build Coastguard Worker } 126*bb4ee6a4SAndroid Build Coastguard Worker virtio_wake( &mut self, queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>, ) -> anyhow::Result<()>127*bb4ee6a4SAndroid Build Coastguard Worker fn virtio_wake( 128*bb4ee6a4SAndroid Build Coastguard Worker &mut self, 129*bb4ee6a4SAndroid Build Coastguard Worker queues_state: Option<(GuestMemory, Interrupt, BTreeMap<usize, Queue>)>, 130*bb4ee6a4SAndroid Build Coastguard Worker ) -> anyhow::Result<()> { 131*bb4ee6a4SAndroid Build Coastguard Worker if let Some((_mem, _interrupt, queues)) = queues_state { 132*bb4ee6a4SAndroid Build Coastguard Worker for (idx, queue) in queues.into_iter() { 133*bb4ee6a4SAndroid Build Coastguard Worker self.console.start_queue(idx, queue)?; 134*bb4ee6a4SAndroid Build Coastguard Worker } 135*bb4ee6a4SAndroid Build Coastguard Worker } 136*bb4ee6a4SAndroid Build Coastguard Worker Ok(()) 137*bb4ee6a4SAndroid Build Coastguard Worker } 138*bb4ee6a4SAndroid Build Coastguard Worker virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value>139*bb4ee6a4SAndroid Build Coastguard Worker fn virtio_snapshot(&mut self) -> anyhow::Result<serde_json::Value> { 140*bb4ee6a4SAndroid Build Coastguard Worker let snap = self.console.snapshot()?; 141*bb4ee6a4SAndroid Build Coastguard Worker serde_json::to_value(snap).context("failed to snapshot virtio console") 142*bb4ee6a4SAndroid Build Coastguard Worker } 143*bb4ee6a4SAndroid Build Coastguard Worker virtio_restore(&mut self, data: serde_json::Value) -> anyhow::Result<()>144*bb4ee6a4SAndroid Build Coastguard Worker fn virtio_restore(&mut self, data: serde_json::Value) -> anyhow::Result<()> { 145*bb4ee6a4SAndroid Build Coastguard Worker let snap: ConsoleSnapshot = 146*bb4ee6a4SAndroid Build Coastguard Worker serde_json::from_value(data).context("failed to deserialize virtio console")?; 147*bb4ee6a4SAndroid Build Coastguard Worker self.console.restore(&snap) 148*bb4ee6a4SAndroid Build Coastguard Worker } 149*bb4ee6a4SAndroid Build Coastguard Worker } 150*bb4ee6a4SAndroid Build Coastguard Worker 151*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(test)] 152*bb4ee6a4SAndroid Build Coastguard Worker mod tests { 153*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(windows)] 154*bb4ee6a4SAndroid Build Coastguard Worker use base::windows::named_pipes; 155*bb4ee6a4SAndroid Build Coastguard Worker use tempfile::tempfile; 156*bb4ee6a4SAndroid Build Coastguard Worker use vm_memory::GuestAddress; 157*bb4ee6a4SAndroid Build Coastguard Worker 158*bb4ee6a4SAndroid Build Coastguard Worker use super::*; 159*bb4ee6a4SAndroid Build Coastguard Worker use crate::suspendable_virtio_tests; 160*bb4ee6a4SAndroid Build Coastguard Worker 161*bb4ee6a4SAndroid Build Coastguard Worker struct ConsoleContext { 162*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(windows)] 163*bb4ee6a4SAndroid Build Coastguard Worker input_pipe_client: named_pipes::PipeConnection, 164*bb4ee6a4SAndroid Build Coastguard Worker } 165*bb4ee6a4SAndroid Build Coastguard Worker modify_device(_context: &mut ConsoleContext, b: &mut Console)166*bb4ee6a4SAndroid Build Coastguard Worker fn modify_device(_context: &mut ConsoleContext, b: &mut Console) { 167*bb4ee6a4SAndroid Build Coastguard Worker let input_buffer = b.console.ports[0].clone_input_buffer(); 168*bb4ee6a4SAndroid Build Coastguard Worker input_buffer.lock().push_back(0); 169*bb4ee6a4SAndroid Build Coastguard Worker } 170*bb4ee6a4SAndroid Build Coastguard Worker 171*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(any(target_os = "android", target_os = "linux"))] create_device() -> (ConsoleContext, Console)172*bb4ee6a4SAndroid Build Coastguard Worker fn create_device() -> (ConsoleContext, Console) { 173*bb4ee6a4SAndroid Build Coastguard Worker let input = Box::new(tempfile().unwrap()); 174*bb4ee6a4SAndroid Build Coastguard Worker let output = Box::new(tempfile().unwrap()); 175*bb4ee6a4SAndroid Build Coastguard Worker 176*bb4ee6a4SAndroid Build Coastguard Worker let console = Console::new( 177*bb4ee6a4SAndroid Build Coastguard Worker hypervisor::ProtectionType::Unprotected, 178*bb4ee6a4SAndroid Build Coastguard Worker Some(input), 179*bb4ee6a4SAndroid Build Coastguard Worker Some(output), 180*bb4ee6a4SAndroid Build Coastguard Worker Vec::new(), 181*bb4ee6a4SAndroid Build Coastguard Worker None, 182*bb4ee6a4SAndroid Build Coastguard Worker ); 183*bb4ee6a4SAndroid Build Coastguard Worker 184*bb4ee6a4SAndroid Build Coastguard Worker let context = ConsoleContext {}; 185*bb4ee6a4SAndroid Build Coastguard Worker (context, console) 186*bb4ee6a4SAndroid Build Coastguard Worker } 187*bb4ee6a4SAndroid Build Coastguard Worker 188*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(windows)] create_device() -> (ConsoleContext, Console)189*bb4ee6a4SAndroid Build Coastguard Worker fn create_device() -> (ConsoleContext, Console) { 190*bb4ee6a4SAndroid Build Coastguard Worker let (input_pipe_server, input_pipe_client) = named_pipes::pair( 191*bb4ee6a4SAndroid Build Coastguard Worker &named_pipes::FramingMode::Byte, 192*bb4ee6a4SAndroid Build Coastguard Worker &named_pipes::BlockingMode::NoWait, 193*bb4ee6a4SAndroid Build Coastguard Worker 0, 194*bb4ee6a4SAndroid Build Coastguard Worker ) 195*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 196*bb4ee6a4SAndroid Build Coastguard Worker 197*bb4ee6a4SAndroid Build Coastguard Worker let input = Box::new(input_pipe_server); 198*bb4ee6a4SAndroid Build Coastguard Worker let output = Box::new(tempfile().unwrap()); 199*bb4ee6a4SAndroid Build Coastguard Worker 200*bb4ee6a4SAndroid Build Coastguard Worker let console = Console::new( 201*bb4ee6a4SAndroid Build Coastguard Worker hypervisor::ProtectionType::Unprotected, 202*bb4ee6a4SAndroid Build Coastguard Worker Some(input), 203*bb4ee6a4SAndroid Build Coastguard Worker Some(output), 204*bb4ee6a4SAndroid Build Coastguard Worker Vec::new(), 205*bb4ee6a4SAndroid Build Coastguard Worker None, 206*bb4ee6a4SAndroid Build Coastguard Worker ); 207*bb4ee6a4SAndroid Build Coastguard Worker 208*bb4ee6a4SAndroid Build Coastguard Worker let context = ConsoleContext { input_pipe_client }; 209*bb4ee6a4SAndroid Build Coastguard Worker 210*bb4ee6a4SAndroid Build Coastguard Worker (context, console) 211*bb4ee6a4SAndroid Build Coastguard Worker } 212*bb4ee6a4SAndroid Build Coastguard Worker 213*bb4ee6a4SAndroid Build Coastguard Worker suspendable_virtio_tests!(console, create_device, 2, modify_device); 214*bb4ee6a4SAndroid Build Coastguard Worker 215*bb4ee6a4SAndroid Build Coastguard Worker #[test] test_inactive_sleep_resume()216*bb4ee6a4SAndroid Build Coastguard Worker fn test_inactive_sleep_resume() { 217*bb4ee6a4SAndroid Build Coastguard Worker let (_ctx, mut device) = create_device(); 218*bb4ee6a4SAndroid Build Coastguard Worker 219*bb4ee6a4SAndroid Build Coastguard Worker let input_buffer = device.console.ports[0].clone_input_buffer(); 220*bb4ee6a4SAndroid Build Coastguard Worker 221*bb4ee6a4SAndroid Build Coastguard Worker // Initialize the device, starting the input thread, but don't activate any queues. 222*bb4ee6a4SAndroid Build Coastguard Worker device.on_device_sandboxed(); 223*bb4ee6a4SAndroid Build Coastguard Worker 224*bb4ee6a4SAndroid Build Coastguard Worker // No queues were started, so `virtio_sleep()` should return `None`. 225*bb4ee6a4SAndroid Build Coastguard Worker let sleep_result = device.virtio_sleep().expect("failed to sleep"); 226*bb4ee6a4SAndroid Build Coastguard Worker assert!(sleep_result.is_none()); 227*bb4ee6a4SAndroid Build Coastguard Worker 228*bb4ee6a4SAndroid Build Coastguard Worker // Inject some input data. 229*bb4ee6a4SAndroid Build Coastguard Worker input_buffer.lock().extend(b"Hello".iter()); 230*bb4ee6a4SAndroid Build Coastguard Worker 231*bb4ee6a4SAndroid Build Coastguard Worker // Ensure snapshot does not fail and contains the buffered input data. 232*bb4ee6a4SAndroid Build Coastguard Worker let snapshot = device.virtio_snapshot().expect("failed to snapshot"); 233*bb4ee6a4SAndroid Build Coastguard Worker 234*bb4ee6a4SAndroid Build Coastguard Worker let snapshot_input_buffer = snapshot 235*bb4ee6a4SAndroid Build Coastguard Worker .get("ports") 236*bb4ee6a4SAndroid Build Coastguard Worker .unwrap() 237*bb4ee6a4SAndroid Build Coastguard Worker .get(0) 238*bb4ee6a4SAndroid Build Coastguard Worker .unwrap() 239*bb4ee6a4SAndroid Build Coastguard Worker .get("input_buffer") 240*bb4ee6a4SAndroid Build Coastguard Worker .unwrap() 241*bb4ee6a4SAndroid Build Coastguard Worker .as_array() 242*bb4ee6a4SAndroid Build Coastguard Worker .unwrap(); 243*bb4ee6a4SAndroid Build Coastguard Worker 244*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(snapshot_input_buffer.len(), b"Hello".len()); 245*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(snapshot_input_buffer[0].as_i64(), Some(b'H' as i64)); 246*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(snapshot_input_buffer[1].as_i64(), Some(b'e' as i64)); 247*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(snapshot_input_buffer[2].as_i64(), Some(b'l' as i64)); 248*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(snapshot_input_buffer[3].as_i64(), Some(b'l' as i64)); 249*bb4ee6a4SAndroid Build Coastguard Worker assert_eq!(snapshot_input_buffer[4].as_i64(), Some(b'o' as i64)); 250*bb4ee6a4SAndroid Build Coastguard Worker 251*bb4ee6a4SAndroid Build Coastguard Worker // Wake up the device, which should start the input thread again. 252*bb4ee6a4SAndroid Build Coastguard Worker device.virtio_wake(None).expect("failed to wake"); 253*bb4ee6a4SAndroid Build Coastguard Worker } 254*bb4ee6a4SAndroid Build Coastguard Worker } 255