• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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