1 // Copyright 2021 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 use std::collections::VecDeque;
6 use std::io::{self, stdin};
7 use std::ops::DerefMut;
8 use std::path::PathBuf;
9 use std::sync::Arc;
10
11 use anyhow::{anyhow, bail, Context};
12 use base::{error, warn, Event, FileSync, RawDescriptor, Terminal};
13 use cros_async::{EventAsync, Executor};
14 use data_model::DataInit;
15
16 use argh::FromArgs;
17 use futures::future::{AbortHandle, Abortable};
18 use hypervisor::ProtectionType;
19 use sync::Mutex;
20 use vm_memory::GuestMemory;
21 use vmm_vhost::message::{VhostUserProtocolFeatures, VhostUserVirtioFeatures};
22
23 use crate::serial_device::{SerialDevice, SerialHardware, SerialParameters, SerialType};
24 use crate::virtio::console::{
25 handle_input, process_transmit_queue, spawn_input_thread, virtio_console_config, ConsoleError,
26 };
27 use crate::virtio::vhost::user::device::handler::{
28 DeviceRequestHandler, Doorbell, VhostUserBackend,
29 };
30 use crate::virtio::vhost::user::device::vvu::pci::VvuPciDevice;
31 use crate::virtio::{self, copy_config};
32
run_tx_queue( mut queue: virtio::Queue, mem: GuestMemory, doorbell: Arc<Mutex<Doorbell>>, kick_evt: EventAsync, mut output: Box<dyn io::Write>, )33 async fn run_tx_queue(
34 mut queue: virtio::Queue,
35 mem: GuestMemory,
36 doorbell: Arc<Mutex<Doorbell>>,
37 kick_evt: EventAsync,
38 mut output: Box<dyn io::Write>,
39 ) {
40 loop {
41 if let Err(e) = kick_evt.next_val().await {
42 error!("Failed to read kick event for tx queue: {}", e);
43 break;
44 }
45 process_transmit_queue(&mem, &doorbell, &mut queue, &mut output);
46 }
47 }
48
run_rx_queue( mut queue: virtio::Queue, mem: GuestMemory, doorbell: Arc<Mutex<Doorbell>>, kick_evt: EventAsync, in_buffer: Arc<Mutex<VecDeque<u8>>>, in_avail_evt: EventAsync, )49 async fn run_rx_queue(
50 mut queue: virtio::Queue,
51 mem: GuestMemory,
52 doorbell: Arc<Mutex<Doorbell>>,
53 kick_evt: EventAsync,
54 in_buffer: Arc<Mutex<VecDeque<u8>>>,
55 in_avail_evt: EventAsync,
56 ) {
57 loop {
58 if let Err(e) = in_avail_evt.next_val().await {
59 error!("Failed reading in_avail_evt: {}", e);
60 break;
61 }
62 match handle_input(&mem, &doorbell, in_buffer.lock().deref_mut(), &mut queue) {
63 Ok(()) => {}
64 Err(ConsoleError::RxDescriptorsExhausted) => {
65 if let Err(e) = kick_evt.next_val().await {
66 error!("Failed to read kick event for rx queue: {}", e);
67 break;
68 }
69 }
70 }
71 }
72 }
73
74 struct ConsoleDevice {
75 input: Option<Box<dyn io::Read + Send>>,
76 output: Option<Box<dyn io::Write + Send>>,
77 avail_features: u64,
78 }
79
80 impl SerialDevice for ConsoleDevice {
new( protected_vm: ProtectionType, _evt: Event, input: Option<Box<dyn io::Read + Send>>, output: Option<Box<dyn io::Write + Send>>, _sync: Option<Box<dyn FileSync + Send>>, _out_timestamp: bool, _keep_rds: Vec<RawDescriptor>, ) -> ConsoleDevice81 fn new(
82 protected_vm: ProtectionType,
83 _evt: Event,
84 input: Option<Box<dyn io::Read + Send>>,
85 output: Option<Box<dyn io::Write + Send>>,
86 _sync: Option<Box<dyn FileSync + Send>>,
87 _out_timestamp: bool,
88 _keep_rds: Vec<RawDescriptor>,
89 ) -> ConsoleDevice {
90 let avail_features =
91 virtio::base_features(protected_vm) | VhostUserVirtioFeatures::PROTOCOL_FEATURES.bits();
92 ConsoleDevice {
93 input,
94 output,
95 avail_features,
96 }
97 }
98 }
99
100 struct ConsoleBackend {
101 ex: Executor,
102 device: ConsoleDevice,
103 acked_features: u64,
104 acked_protocol_features: VhostUserProtocolFeatures,
105 workers: [Option<AbortHandle>; Self::MAX_QUEUE_NUM],
106 }
107
108 impl ConsoleBackend {
new(ex: &Executor, device: ConsoleDevice) -> Self109 fn new(ex: &Executor, device: ConsoleDevice) -> Self {
110 Self {
111 ex: ex.clone(),
112 device,
113 acked_features: 0,
114 acked_protocol_features: VhostUserProtocolFeatures::empty(),
115 workers: Default::default(),
116 }
117 }
118 }
119
120 impl VhostUserBackend for ConsoleBackend {
121 const MAX_QUEUE_NUM: usize = 2; /* transmit and receive queues */
122 const MAX_VRING_LEN: u16 = 256;
123
124 type Error = anyhow::Error;
125
features(&self) -> u64126 fn features(&self) -> u64 {
127 self.device.avail_features
128 }
129
ack_features(&mut self, value: u64) -> anyhow::Result<()>130 fn ack_features(&mut self, value: u64) -> anyhow::Result<()> {
131 let unrequested_features = value & !self.device.avail_features;
132 if unrequested_features != 0 {
133 bail!("invalid features are given: {:#x}", unrequested_features);
134 }
135
136 self.acked_features |= value;
137
138 Ok(())
139 }
140
acked_features(&self) -> u64141 fn acked_features(&self) -> u64 {
142 self.acked_features
143 }
144
protocol_features(&self) -> VhostUserProtocolFeatures145 fn protocol_features(&self) -> VhostUserProtocolFeatures {
146 VhostUserProtocolFeatures::CONFIG
147 }
148
ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()>149 fn ack_protocol_features(&mut self, features: u64) -> anyhow::Result<()> {
150 let features = VhostUserProtocolFeatures::from_bits(features)
151 .ok_or_else(|| anyhow!("invalid protocol features are given: {:#x}", features))?;
152 let supported = self.protocol_features();
153 self.acked_protocol_features = features & supported;
154 Ok(())
155 }
156
acked_protocol_features(&self) -> u64157 fn acked_protocol_features(&self) -> u64 {
158 self.acked_protocol_features.bits()
159 }
160
read_config(&self, offset: u64, data: &mut [u8])161 fn read_config(&self, offset: u64, data: &mut [u8]) {
162 let config = virtio_console_config {
163 max_nr_ports: 1.into(),
164 ..Default::default()
165 };
166 copy_config(data, 0, config.as_slice(), offset);
167 }
168
reset(&mut self)169 fn reset(&mut self) {
170 for handle in self.workers.iter_mut().filter_map(Option::take) {
171 handle.abort();
172 }
173 }
174
start_queue( &mut self, idx: usize, mut queue: virtio::Queue, mem: GuestMemory, doorbell: Arc<Mutex<Doorbell>>, kick_evt: Event, ) -> anyhow::Result<()>175 fn start_queue(
176 &mut self,
177 idx: usize,
178 mut queue: virtio::Queue,
179 mem: GuestMemory,
180 doorbell: Arc<Mutex<Doorbell>>,
181 kick_evt: Event,
182 ) -> anyhow::Result<()> {
183 if let Some(handle) = self.workers.get_mut(idx).and_then(Option::take) {
184 warn!("Starting new queue handler without stopping old handler");
185 handle.abort();
186 }
187
188 // Enable any virtqueue features that were negotiated (like VIRTIO_RING_F_EVENT_IDX).
189 queue.ack_features(self.acked_features);
190
191 let kick_evt = EventAsync::new(kick_evt.0, &self.ex)
192 .context("Failed to create EventAsync for kick_evt")?;
193 let (handle, registration) = AbortHandle::new_pair();
194 match idx {
195 // ReceiveQueue
196 0 => {
197 // See explanation in devices/src/virtio/console.rs
198 // We need a multithreaded input polling because io::Read only provides
199 // a blocking interface which we cannot use in an async function.
200 let in_avail_evt = match Event::new() {
201 Ok(evt) => evt,
202 Err(e) => {
203 bail!("Failed creating Event: {}", e);
204 }
205 };
206
207 let input_unpacked = self
208 .device
209 .input
210 .take()
211 .ok_or_else(|| anyhow!("input source unavailable"))?;
212 let in_buffer = spawn_input_thread(input_unpacked, &in_avail_evt)
213 .take()
214 .ok_or_else(|| anyhow!("input channel unavailable"))?;
215
216 // Create the async 'in' event so we can await on it.
217 let in_avail_async_evt = EventAsync::new(in_avail_evt.0, &self.ex)
218 .context("Failed to create EventAsync for in_avail_evt")?;
219
220 self.ex
221 .spawn_local(Abortable::new(
222 run_rx_queue(
223 queue,
224 mem,
225 doorbell,
226 kick_evt,
227 in_buffer,
228 in_avail_async_evt,
229 ),
230 registration,
231 ))
232 .detach();
233 }
234 // TransmitQueue
235 1 => {
236 // Take ownership of output writer.
237 // Safe because output should always be initialized to something
238 let output_unwrapped: Box<dyn io::Write + Send> = self
239 .device
240 .output
241 .take()
242 .ok_or_else(|| anyhow!("no output available"))?;
243 self.ex
244 .spawn_local(Abortable::new(
245 run_tx_queue(queue, mem, doorbell, kick_evt, output_unwrapped),
246 registration,
247 ))
248 .detach();
249 }
250 _ => bail!("attempted to start unknown queue: {}", idx),
251 }
252
253 self.workers[idx] = Some(handle);
254 Ok(())
255 }
256
stop_queue(&mut self, idx: usize)257 fn stop_queue(&mut self, idx: usize) {
258 if let Some(handle) = self.workers.get_mut(idx).and_then(Option::take) {
259 handle.abort();
260 }
261 }
262 }
263
264 #[derive(FromArgs)]
265 #[argh(description = "")]
266 struct Options {
267 #[argh(option, description = "path to a vhost-user socket", arg_name = "PATH")]
268 socket: Option<String>,
269 #[argh(
270 option,
271 description = "VFIO-PCI device name (e.g. '0000:00:07.0')",
272 arg_name = "STRING"
273 )]
274 vfio: Option<String>,
275 #[argh(option, description = "path to a file", arg_name = "OUTFILE")]
276 output_file: Option<PathBuf>,
277 #[argh(option, description = "path to a file", arg_name = "INFILE")]
278 input_file: Option<PathBuf>,
279 }
280
281 /// Starts a vhost-user console device.
282 /// Returns an error if the given `args` is invalid or the device fails to run.
run_console_device(program_name: &str, args: &[&str]) -> anyhow::Result<()>283 pub fn run_console_device(program_name: &str, args: &[&str]) -> anyhow::Result<()> {
284 let opts = match Options::from_args(&[program_name], args) {
285 Ok(opts) => opts,
286 Err(e) => {
287 if e.status.is_err() {
288 bail!(e.output);
289 } else {
290 println!("{}", e.output);
291 }
292 return Ok(());
293 }
294 };
295
296 let type_ = match opts.output_file {
297 Some(_) => SerialType::File,
298 None => SerialType::Stdout,
299 };
300
301 let params = SerialParameters {
302 type_,
303 hardware: SerialHardware::VirtioConsole,
304 // Required only if type_ is SerialType::File or SerialType::UnixSocket
305 path: opts.output_file,
306 input: opts.input_file,
307 num: 1,
308 console: true,
309 earlycon: false,
310 // We do not support stdin-less mode
311 stdin: true,
312 out_timestamp: false,
313 };
314
315 let console = match params.create_serial_device::<ConsoleDevice>(
316 ProtectionType::Unprotected,
317 // We need to pass an event as per Serial Device API but we don't really use it anyway.
318 &Event::new()?,
319 // Same for keep_rds, we don't really use this.
320 &mut Vec::new(),
321 ) {
322 Ok(c) => c,
323 Err(e) => bail!(e),
324 };
325 let ex = Executor::new().context("Failed to create executor")?;
326 let backend = ConsoleBackend::new(&ex, console);
327 let handler = DeviceRequestHandler::new(backend);
328
329 // Set stdin() in raw mode so we can send over individual keystrokes unbuffered
330 stdin()
331 .set_raw_mode()
332 .context("Failed to set terminal raw mode")?;
333
334 let res = match (opts.socket, opts.vfio) {
335 (Some(socket), None) => {
336 // run_until() returns an Result<Result<..>> which the ? operator lets us flatten.
337 ex.run_until(handler.run(socket, &ex))?
338 }
339 (None, Some(vfio)) => {
340 let device = VvuPciDevice::new(&vfio, ConsoleBackend::MAX_QUEUE_NUM)?;
341 ex.run_until(handler.run_vvu(device, &ex))?
342 }
343 _ => Err(anyhow!("exactly one of `--socket` or `--vfio` is required")),
344 };
345
346 // Restore terminal capabilities back to what they were before
347 stdin()
348 .set_canon_mode()
349 .context("Failed to restore canonical mode for terminal")?;
350
351 res
352 }
353