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