devices/virtio/console/
device.rs1use base::RawDescriptor;
8use data_model::Le32;
9use hypervisor::ProtectionType;
10use serde::Deserialize;
11use serde::Serialize;
12use zerocopy::IntoBytes;
13
14use crate::virtio::base_features;
15use crate::virtio::console::port::ConsolePort;
16use crate::virtio::console::port::ConsolePortSnapshot;
17use crate::virtio::console::worker::WorkerHandle;
18use crate::virtio::console::worker::WorkerPort;
19use crate::virtio::copy_config;
20use crate::virtio::device_constants::console::virtio_console_config;
21use crate::virtio::device_constants::console::VIRTIO_CONSOLE_F_MULTIPORT;
22use crate::virtio::Queue;
23
24pub struct ConsoleDevice {
25 avail_features: u64,
26 pub(crate) ports: Vec<ConsolePort>,
27 worker: Option<WorkerHandle>,
28}
29
30#[derive(Serialize, Deserialize)]
31pub struct ConsoleSnapshot {
32 avail_features: u64,
33 pub(super) ports: Vec<ConsolePortSnapshot>,
34}
35
36impl ConsoleDevice {
37 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 pub fn new_multi_port(
48 protection_type: ProtectionType,
49 ports: Vec<ConsolePort>,
50 ) -> ConsoleDevice {
51 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 pub fn max_queues(&self) -> usize {
73 let num_queues = self.ports.len().max(1);
76 if self.avail_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT) != 0 {
77 num_queues * 2 + 2
79 } else {
80 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}