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