Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/console.rs
5394 views
1
// Copyright 2020 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
//! Virtio console device.
6
7
pub mod control;
8
pub mod device;
9
pub mod input;
10
pub mod output;
11
pub mod port;
12
pub mod worker;
13
14
mod sys;
15
16
use std::collections::BTreeMap;
17
18
use anyhow::Context;
19
use base::RawDescriptor;
20
use hypervisor::ProtectionType;
21
use snapshot::AnySnapshot;
22
use vm_memory::GuestMemory;
23
24
use crate::serial::sys::InStreamType;
25
use crate::virtio::console::device::ConsoleDevice;
26
use crate::virtio::console::device::ConsoleSnapshot;
27
use crate::virtio::console::port::ConsolePort;
28
use crate::virtio::DeviceType;
29
use crate::virtio::Interrupt;
30
use crate::virtio::Queue;
31
use crate::virtio::VirtioDevice;
32
use crate::PciAddress;
33
34
const QUEUE_SIZE: u16 = 256;
35
36
/// Virtio console device.
37
pub struct Console {
38
console: ConsoleDevice,
39
max_queue_sizes: Vec<u16>,
40
pci_address: Option<PciAddress>,
41
}
42
43
impl 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
// TODO: Move these checks into cmdline validation or something so it is more user
58
// friendly when it fails.
59
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
72
impl 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
// Stop and collect all the queues.
119
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)]
162
mod 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
// Initialize the device, starting the input thread, but don't activate any queues.
233
device.on_device_sandboxed();
234
235
// No queues were started, so `virtio_sleep()` should return `None`.
236
let sleep_result = device.virtio_sleep().expect("failed to sleep");
237
assert!(sleep_result.is_none());
238
239
// Inject some input data.
240
input_buffer.lock().extend(b"Hello".iter());
241
242
// Ensure snapshot does not fail and contains the buffered input data.
243
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
// Wake up the device, which should start the input thread again.
250
device.virtio_wake(None).expect("failed to wake");
251
}
252
}
253
254