Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/console/device.rs
5394 views
1
// Copyright 2024 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 and vhost-user-console device shared backend implementation
6
7
use base::RawDescriptor;
8
use data_model::Le32;
9
use hypervisor::ProtectionType;
10
use serde::Deserialize;
11
use serde::Serialize;
12
use zerocopy::IntoBytes;
13
14
use crate::virtio::base_features;
15
use crate::virtio::console::port::ConsolePort;
16
use crate::virtio::console::port::ConsolePortSnapshot;
17
use crate::virtio::console::worker::WorkerHandle;
18
use crate::virtio::console::worker::WorkerPort;
19
use crate::virtio::copy_config;
20
use crate::virtio::device_constants::console::virtio_console_config;
21
use crate::virtio::device_constants::console::VIRTIO_CONSOLE_F_MULTIPORT;
22
use crate::virtio::Queue;
23
24
pub struct ConsoleDevice {
25
avail_features: u64,
26
pub(crate) ports: Vec<ConsolePort>,
27
worker: Option<WorkerHandle>,
28
}
29
30
#[derive(Serialize, Deserialize)]
31
pub struct ConsoleSnapshot {
32
avail_features: u64,
33
pub(super) ports: Vec<ConsolePortSnapshot>,
34
}
35
36
impl ConsoleDevice {
37
/// Create a console device that does not support the multiport feature.
38
pub fn new_single_port(protection_type: ProtectionType, port: ConsolePort) -> ConsoleDevice {
39
ConsoleDevice {
40
avail_features: base_features(protection_type),
41
ports: vec![port],
42
worker: None,
43
}
44
}
45
46
/// Create a console device with the multiport feature enabled.
47
pub fn new_multi_port(
48
protection_type: ProtectionType,
49
ports: Vec<ConsolePort>,
50
) -> ConsoleDevice {
51
// Port 0 must always exist.
52
assert!(!ports.is_empty());
53
54
let avail_features = base_features(protection_type) | (1 << VIRTIO_CONSOLE_F_MULTIPORT);
55
56
ConsoleDevice {
57
avail_features,
58
ports,
59
worker: None,
60
}
61
}
62
63
pub fn features(&self) -> u64 {
64
self.avail_features
65
}
66
67
pub fn max_ports(&self) -> usize {
68
self.ports.len()
69
}
70
71
/// Returns the maximum number of queues supported by this device.
72
pub fn max_queues(&self) -> usize {
73
// The port 0 receive and transmit queues always exist;
74
// other queues only exist if VIRTIO_CONSOLE_F_MULTIPORT is set.
75
let num_queues = self.ports.len().max(1);
76
if self.avail_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT) != 0 {
77
// Each port has two queues (tx & rx), plus 2 for control receiveq and transmitq.
78
num_queues * 2 + 2
79
} else {
80
// port0 receiveq + transmitq
81
2
82
}
83
}
84
85
pub fn read_config(&self, offset: u64, data: &mut [u8]) {
86
let max_nr_ports = self.max_ports();
87
let config = virtio_console_config {
88
max_nr_ports: Le32::from(max_nr_ports as u32),
89
..Default::default()
90
};
91
copy_config(data, 0, config.as_bytes(), offset);
92
}
93
94
pub fn keep_rds(&self) -> Vec<RawDescriptor> {
95
self.ports.iter().flat_map(ConsolePort::keep_rds).collect()
96
}
97
98
fn ensure_worker_started(&mut self) -> &mut WorkerHandle {
99
self.worker.get_or_insert_with(|| {
100
let ports = self
101
.ports
102
.iter_mut()
103
.map(WorkerPort::from_console_port)
104
.collect();
105
WorkerHandle::new(ports).expect("failed to create console worker")
106
})
107
}
108
109
fn ensure_worker_stopped(&mut self) {
110
if let Some(worker) = self.worker.take() {
111
let ports = worker.stop();
112
for (worker_port, port) in ports.into_iter().zip(self.ports.iter_mut()) {
113
worker_port.into_console_port(port);
114
}
115
}
116
}
117
118
pub fn start_queue(&mut self, idx: usize, queue: Queue) -> anyhow::Result<()> {
119
let worker = self.ensure_worker_started();
120
worker.start_queue(idx, queue)
121
}
122
123
pub fn stop_queue(&mut self, idx: usize) -> anyhow::Result<Option<Queue>> {
124
match self.worker.as_mut() {
125
Some(worker) => worker.stop_queue(idx),
126
None => Ok(None),
127
}
128
}
129
130
pub fn reset(&mut self) -> anyhow::Result<()> {
131
for idx in 0..self.max_queues() {
132
let _ = self.stop_queue(idx);
133
}
134
self.ensure_worker_stopped();
135
Ok(())
136
}
137
138
pub fn start_input_threads(&mut self) {
139
for port in self.ports.iter_mut() {
140
port.start_input_thread();
141
}
142
}
143
144
pub fn stop_input_threads(&mut self) {
145
for port in self.ports.iter_mut() {
146
port.stop_input_thread();
147
}
148
}
149
150
pub fn snapshot(&mut self) -> anyhow::Result<ConsoleSnapshot> {
151
let mut ports = Vec::new();
152
for port in &mut self.ports {
153
ports.push(port.snapshot());
154
}
155
156
Ok(ConsoleSnapshot {
157
avail_features: self.avail_features,
158
ports,
159
})
160
}
161
162
pub fn restore(&mut self, snap: &ConsoleSnapshot) -> anyhow::Result<()> {
163
anyhow::ensure!(
164
self.avail_features == snap.avail_features,
165
"Virtio console incorrect features for restore: Expected: {}, Actual: {}",
166
self.avail_features,
167
snap.avail_features,
168
);
169
170
for (port, port_snap) in self.ports.iter_mut().zip(snap.ports.iter()) {
171
port.restore(port_snap);
172
}
173
174
Ok(())
175
}
176
}
177
178