xref: /aosp_15_r20/external/crosvm/devices/src/virtio/console.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
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