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