xref: /cloud-hypervisor/virtio-devices/src/console.rs (revision 7d7bfb2034001d4cb15df2ddc56d2d350c8da30f)
1 // Copyright 2019 Intel Corporation. All Rights Reserved.
2 // SPDX-License-Identifier: Apache-2.0
3 
4 use super::Error as DeviceError;
5 use super::{
6     ActivateResult, EpollHelper, EpollHelperError, EpollHelperHandler, VirtioCommon, VirtioDevice,
7     VirtioDeviceType, VirtioInterruptType, EPOLL_HELPER_EVENT_LAST, VIRTIO_F_IOMMU_PLATFORM,
8     VIRTIO_F_VERSION_1,
9 };
10 use crate::seccomp_filters::Thread;
11 use crate::thread_helper::spawn_virtio_thread;
12 use crate::GuestMemoryMmap;
13 use crate::VirtioInterrupt;
14 use libc::{EFD_NONBLOCK, TIOCGWINSZ};
15 use seccompiler::SeccompAction;
16 use std::cmp;
17 use std::collections::VecDeque;
18 use std::fs::File;
19 use std::io;
20 use std::io::{Read, Write};
21 use std::os::unix::io::AsRawFd;
22 use std::result;
23 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
24 use std::sync::{Arc, Barrier, Mutex};
25 use versionize::{VersionMap, Versionize, VersionizeResult};
26 use versionize_derive::Versionize;
27 use virtio_queue::Queue;
28 use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic};
29 use vm_migration::VersionMapped;
30 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
31 use vm_virtio::{AccessPlatform, Translatable};
32 use vmm_sys_util::eventfd::EventFd;
33 
34 const QUEUE_SIZE: u16 = 256;
35 const NUM_QUEUES: usize = 2;
36 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
37 
38 // New descriptors are pending on the virtio queue.
39 const INPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
40 const OUTPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
41 // Some input from the VMM is ready to be injected into the VM.
42 const INPUT_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
43 // Console configuration change event is triggered.
44 const CONFIG_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4;
45 // File written to (input ready)
46 const FILE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 5;
47 // Console resized
48 const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 6;
49 
50 //Console size feature bit
51 const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
52 
53 #[derive(Copy, Clone, Debug, Versionize)]
54 #[repr(C, packed)]
55 pub struct VirtioConsoleConfig {
56     cols: u16,
57     rows: u16,
58     max_nr_ports: u32,
59     emerg_wr: u32,
60 }
61 
62 impl Default for VirtioConsoleConfig {
63     fn default() -> Self {
64         VirtioConsoleConfig {
65             cols: 0,
66             rows: 0,
67             max_nr_ports: 1,
68             emerg_wr: 0,
69         }
70     }
71 }
72 
73 // SAFETY: it only has data and has no implicit padding.
74 unsafe impl ByteValued for VirtioConsoleConfig {}
75 
76 struct ConsoleEpollHandler {
77     queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
78     interrupt_cb: Arc<dyn VirtioInterrupt>,
79     in_buffer: Arc<Mutex<VecDeque<u8>>>,
80     resizer: Arc<ConsoleResizer>,
81     endpoint: Endpoint,
82     input_queue_evt: EventFd,
83     output_queue_evt: EventFd,
84     input_evt: EventFd,
85     config_evt: EventFd,
86     resize_pipe: Option<File>,
87     kill_evt: EventFd,
88     pause_evt: EventFd,
89     access_platform: Option<Arc<dyn AccessPlatform>>,
90 }
91 
92 pub enum Endpoint {
93     File(File),
94     FilePair(File, File),
95     Null,
96 }
97 
98 impl Endpoint {
99     fn out_file(&self) -> Option<&File> {
100         match self {
101             Self::File(f) => Some(f),
102             Self::FilePair(f, _) => Some(f),
103             Self::Null => None,
104         }
105     }
106 
107     fn in_file(&self) -> Option<&File> {
108         match self {
109             Self::File(_) => None,
110             Self::FilePair(_, f) => Some(f),
111             Self::Null => None,
112         }
113     }
114 }
115 
116 impl Clone for Endpoint {
117     fn clone(&self) -> Self {
118         match self {
119             Self::File(f) => Self::File(f.try_clone().unwrap()),
120             Self::FilePair(f_out, f_in) => {
121                 Self::FilePair(f_out.try_clone().unwrap(), f_in.try_clone().unwrap())
122             }
123             Self::Null => Self::Null,
124         }
125     }
126 }
127 
128 impl ConsoleEpollHandler {
129     /*
130      * Each port of virtio console device has one receive
131      * queue. One or more empty buffers are placed by the
132      * driver in the receive queue for incoming data. Here,
133      * we place the input data to these empty buffers.
134      */
135     fn process_input_queue(&mut self) -> bool {
136         let mut in_buffer = self.in_buffer.lock().unwrap();
137         let recv_queue = &mut self.queues[0]; //receiveq
138         let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
139         let mut used_count = 0;
140 
141         if in_buffer.is_empty() {
142             return false;
143         }
144 
145         let mut avail_iter = recv_queue.iter().unwrap();
146         for mut desc_chain in &mut avail_iter {
147             let desc = desc_chain.next().unwrap();
148             let len = cmp::min(desc.len() as u32, in_buffer.len() as u32);
149             let source_slice = in_buffer.drain(..len as usize).collect::<Vec<u8>>();
150 
151             if let Err(e) = desc_chain.memory().write_slice(
152                 &source_slice[..],
153                 desc.addr()
154                     .translate_gva(self.access_platform.as_ref(), desc.len() as usize),
155             ) {
156                 error!("Failed to write slice: {:?}", e);
157                 avail_iter.go_to_previous_position();
158                 break;
159             }
160 
161             used_desc_heads[used_count] = (desc_chain.head_index(), len);
162             used_count += 1;
163 
164             if in_buffer.is_empty() {
165                 break;
166             }
167         }
168 
169         for &(desc_index, len) in &used_desc_heads[..used_count] {
170             recv_queue.add_used(desc_index, len).unwrap();
171         }
172 
173         used_count > 0
174     }
175 
176     /*
177      * Each port of virtio console device has one transmit
178      * queue. For outgoing data, characters are placed in
179      * the transmit queue by the driver. Therefore, here
180      * we read data from the transmit queue and flush them
181      * to the referenced address.
182      */
183     fn process_output_queue(&mut self) -> bool {
184         let trans_queue = &mut self.queues[1]; //transmitq
185         let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize];
186         let mut used_count = 0;
187 
188         for mut desc_chain in trans_queue.iter().unwrap() {
189             let desc = desc_chain.next().unwrap();
190             if let Some(ref mut out) = self.endpoint.out_file() {
191                 let _ = desc_chain.memory().write_to(
192                     desc.addr()
193                         .translate_gva(self.access_platform.as_ref(), desc.len() as usize),
194                     out,
195                     desc.len() as usize,
196                 );
197                 let _ = out.flush();
198             }
199             used_desc_heads[used_count] = (desc_chain.head_index(), desc.len());
200             used_count += 1;
201         }
202 
203         for &(desc_index, len) in &used_desc_heads[..used_count] {
204             trans_queue.add_used(desc_index, len).unwrap();
205         }
206         used_count > 0
207     }
208 
209     fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
210         self.interrupt_cb
211             .trigger(VirtioInterruptType::Queue(queue_index))
212             .map_err(|e| {
213                 error!("Failed to signal used queue: {:?}", e);
214                 DeviceError::FailedSignalingUsedQueue(e)
215             })
216     }
217 
218     fn run(
219         &mut self,
220         paused: Arc<AtomicBool>,
221         paused_sync: Arc<Barrier>,
222     ) -> result::Result<(), EpollHelperError> {
223         let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
224         helper.add_event(self.input_queue_evt.as_raw_fd(), INPUT_QUEUE_EVENT)?;
225         helper.add_event(self.output_queue_evt.as_raw_fd(), OUTPUT_QUEUE_EVENT)?;
226         helper.add_event(self.input_evt.as_raw_fd(), INPUT_EVENT)?;
227         helper.add_event(self.config_evt.as_raw_fd(), CONFIG_EVENT)?;
228         if let Some(resize_pipe) = self.resize_pipe.as_ref() {
229             helper.add_event(resize_pipe.as_raw_fd(), RESIZE_EVENT)?;
230         }
231         if let Some(in_file) = self.endpoint.in_file() {
232             helper.add_event(in_file.as_raw_fd(), FILE_EVENT)?;
233         }
234         helper.run(paused, paused_sync, self)?;
235 
236         Ok(())
237     }
238 }
239 
240 impl EpollHelperHandler for ConsoleEpollHandler {
241     fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool {
242         let ev_type = event.data as u16;
243         match ev_type {
244             INPUT_QUEUE_EVENT => {
245                 if let Err(e) = self.input_queue_evt.read() {
246                     error!("Failed to get queue event: {:?}", e);
247                     return true;
248                 } else if self.process_input_queue() {
249                     if let Err(e) = self.signal_used_queue(0) {
250                         error!("Failed to signal used queue: {:?}", e);
251                         return true;
252                     }
253                 }
254             }
255             OUTPUT_QUEUE_EVENT => {
256                 if let Err(e) = self.output_queue_evt.read() {
257                     error!("Failed to get queue event: {:?}", e);
258                     return true;
259                 } else if self.process_output_queue() {
260                     if let Err(e) = self.signal_used_queue(1) {
261                         error!("Failed to signal used queue: {:?}", e);
262                         return true;
263                     }
264                 }
265             }
266             INPUT_EVENT => {
267                 if let Err(e) = self.input_evt.read() {
268                     error!("Failed to get input event: {:?}", e);
269                     return true;
270                 } else if self.process_input_queue() {
271                     if let Err(e) = self.signal_used_queue(0) {
272                         error!("Failed to signal used queue: {:?}", e);
273                         return true;
274                     }
275                 }
276             }
277             CONFIG_EVENT => {
278                 if let Err(e) = self.config_evt.read() {
279                     error!("Failed to get config event: {:?}", e);
280                     return true;
281                 } else if let Err(e) = self.interrupt_cb.trigger(VirtioInterruptType::Config) {
282                     error!("Failed to signal console driver: {:?}", e);
283                     return true;
284                 }
285             }
286             RESIZE_EVENT => {
287                 if let Err(e) = self.resize_pipe.as_ref().unwrap().read_exact(&mut [0]) {
288                     error!("Failed to get resize event: {:?}", e);
289                     return true;
290                 }
291 
292                 self.resizer.update_console_size();
293             }
294             FILE_EVENT => {
295                 let mut input = [0u8; 64];
296                 if let Some(ref mut in_file) = self.endpoint.in_file() {
297                     if let Ok(count) = in_file.read(&mut input) {
298                         let mut in_buffer = self.in_buffer.lock().unwrap();
299                         in_buffer.extend(&input[..count]);
300                     }
301 
302                     if self.process_input_queue() {
303                         if let Err(e) = self.signal_used_queue(0) {
304                             error!("Failed to signal used queue: {:?}", e);
305                             return true;
306                         }
307                     }
308                 }
309             }
310             _ => {
311                 error!("Unknown event for virtio-console");
312                 return true;
313             }
314         }
315         false
316     }
317 }
318 
319 /// Resize handler
320 pub struct ConsoleResizer {
321     config_evt: EventFd,
322     tty: Option<File>,
323     config: Arc<Mutex<VirtioConsoleConfig>>,
324     acked_features: AtomicU64,
325 }
326 
327 impl ConsoleResizer {
328     pub fn update_console_size(&self) {
329         if let Some(tty) = self.tty.as_ref() {
330             let (cols, rows) = get_win_size(tty);
331             self.config.lock().unwrap().update_console_size(cols, rows);
332             if self
333                 .acked_features
334                 .fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel)
335                 != 0
336             {
337                 // Send the interrupt to the driver
338                 let _ = self.config_evt.write(1);
339             }
340         }
341     }
342 }
343 
344 impl VirtioConsoleConfig {
345     pub fn update_console_size(&mut self, cols: u16, rows: u16) {
346         self.cols = cols;
347         self.rows = rows;
348     }
349 }
350 
351 /// Virtio device for exposing console to the guest OS through virtio.
352 pub struct Console {
353     common: VirtioCommon,
354     id: String,
355     config: Arc<Mutex<VirtioConsoleConfig>>,
356     resizer: Arc<ConsoleResizer>,
357     resize_pipe: Option<File>,
358     endpoint: Endpoint,
359     seccomp_action: SeccompAction,
360     in_buffer: Arc<Mutex<VecDeque<u8>>>,
361     exit_evt: EventFd,
362 }
363 
364 #[derive(Versionize)]
365 pub struct ConsoleState {
366     avail_features: u64,
367     acked_features: u64,
368     config: VirtioConsoleConfig,
369     in_buffer: Vec<u8>,
370 }
371 
372 fn get_win_size(tty: &dyn AsRawFd) -> (u16, u16) {
373     #[repr(C)]
374     #[derive(Default)]
375     struct WindowSize {
376         rows: u16,
377         cols: u16,
378         xpixel: u16,
379         ypixel: u16,
380     }
381     let ws: WindowSize = WindowSize::default();
382 
383     unsafe {
384         libc::ioctl(tty.as_raw_fd(), TIOCGWINSZ, &ws);
385     }
386 
387     (ws.cols, ws.rows)
388 }
389 
390 impl VersionMapped for ConsoleState {}
391 
392 impl Console {
393     /// Create a new virtio console device that gets random data from /dev/urandom.
394     pub fn new(
395         id: String,
396         endpoint: Endpoint,
397         resize_pipe: Option<File>,
398         iommu: bool,
399         seccomp_action: SeccompAction,
400         exit_evt: EventFd,
401     ) -> io::Result<(Console, Arc<ConsoleResizer>)> {
402         let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_CONSOLE_F_SIZE;
403 
404         if iommu {
405             avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
406         }
407 
408         let config_evt = EventFd::new(EFD_NONBLOCK).unwrap();
409         let console_config = Arc::new(Mutex::new(VirtioConsoleConfig::default()));
410         let resizer = Arc::new(ConsoleResizer {
411             config_evt,
412             config: console_config.clone(),
413             tty: endpoint.out_file().as_ref().map(|t| t.try_clone().unwrap()),
414             acked_features: AtomicU64::new(0),
415         });
416 
417         resizer.update_console_size();
418 
419         Ok((
420             Console {
421                 common: VirtioCommon {
422                     device_type: VirtioDeviceType::Console as u32,
423                     queue_sizes: QUEUE_SIZES.to_vec(),
424                     avail_features,
425                     paused_sync: Some(Arc::new(Barrier::new(2))),
426                     min_queues: NUM_QUEUES as u16,
427                     ..Default::default()
428                 },
429                 id,
430                 config: console_config,
431                 resizer: resizer.clone(),
432                 resize_pipe,
433                 endpoint,
434                 seccomp_action,
435                 in_buffer: Arc::new(Mutex::new(VecDeque::new())),
436                 exit_evt,
437             },
438             resizer,
439         ))
440     }
441 
442     fn state(&self) -> ConsoleState {
443         ConsoleState {
444             avail_features: self.common.avail_features,
445             acked_features: self.common.acked_features,
446             config: *(self.config.lock().unwrap()),
447             in_buffer: self.in_buffer.lock().unwrap().clone().into(),
448         }
449     }
450 
451     fn set_state(&mut self, state: &ConsoleState) {
452         self.common.avail_features = state.avail_features;
453         self.common.acked_features = state.acked_features;
454         *(self.config.lock().unwrap()) = state.config;
455         *(self.in_buffer.lock().unwrap()) = state.in_buffer.clone().into();
456     }
457 }
458 
459 impl Drop for Console {
460     fn drop(&mut self) {
461         if let Some(kill_evt) = self.common.kill_evt.take() {
462             // Ignore the result because there is nothing we can do about it.
463             let _ = kill_evt.write(1);
464         }
465     }
466 }
467 
468 impl VirtioDevice for Console {
469     fn device_type(&self) -> u32 {
470         self.common.device_type
471     }
472 
473     fn queue_max_sizes(&self) -> &[u16] {
474         &self.common.queue_sizes
475     }
476 
477     fn features(&self) -> u64 {
478         self.common.avail_features
479     }
480 
481     fn ack_features(&mut self, value: u64) {
482         self.common.ack_features(value)
483     }
484 
485     fn read_config(&self, offset: u64, data: &mut [u8]) {
486         self.read_config_from_slice(self.config.lock().unwrap().as_slice(), offset, data);
487     }
488 
489     fn activate(
490         &mut self,
491         _mem: GuestMemoryAtomic<GuestMemoryMmap>,
492         interrupt_cb: Arc<dyn VirtioInterrupt>,
493         queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>,
494         mut queue_evts: Vec<EventFd>,
495     ) -> ActivateResult {
496         self.common.activate(&queues, &queue_evts, &interrupt_cb)?;
497         self.resizer
498             .acked_features
499             .store(self.common.acked_features, Ordering::Relaxed);
500 
501         if self.common.feature_acked(VIRTIO_CONSOLE_F_SIZE) {
502             if let Err(e) = interrupt_cb.trigger(VirtioInterruptType::Config) {
503                 error!("Failed to signal console driver: {:?}", e);
504             }
505         }
506 
507         let (kill_evt, pause_evt) = self.common.dup_eventfds();
508         let input_evt = EventFd::new(EFD_NONBLOCK).unwrap();
509 
510         let mut handler = ConsoleEpollHandler {
511             queues,
512             interrupt_cb,
513             in_buffer: self.in_buffer.clone(),
514             endpoint: self.endpoint.clone(),
515             input_queue_evt: queue_evts.remove(0),
516             output_queue_evt: queue_evts.remove(0),
517             input_evt,
518             config_evt: self.resizer.config_evt.try_clone().unwrap(),
519             resize_pipe: self.resize_pipe.as_ref().map(|p| p.try_clone().unwrap()),
520             resizer: Arc::clone(&self.resizer),
521             kill_evt,
522             pause_evt,
523             access_platform: self.common.access_platform.clone(),
524         };
525 
526         let paused = self.common.paused.clone();
527         let paused_sync = self.common.paused_sync.clone();
528         let mut epoll_threads = Vec::new();
529 
530         spawn_virtio_thread(
531             &self.id,
532             &self.seccomp_action,
533             Thread::VirtioConsole,
534             &mut epoll_threads,
535             &self.exit_evt,
536             move || {
537                 if let Err(e) = handler.run(paused, paused_sync.unwrap()) {
538                     error!("Error running worker: {:?}", e);
539                 }
540             },
541         )?;
542 
543         self.common.epoll_threads = Some(epoll_threads);
544 
545         event!("virtio-device", "activated", "id", &self.id);
546         Ok(())
547     }
548 
549     fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
550         let result = self.common.reset();
551         event!("virtio-device", "reset", "id", &self.id);
552         result
553     }
554 
555     fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) {
556         self.common.set_access_platform(access_platform)
557     }
558 }
559 
560 impl Pausable for Console {
561     fn pause(&mut self) -> result::Result<(), MigratableError> {
562         self.common.pause()
563     }
564 
565     fn resume(&mut self) -> result::Result<(), MigratableError> {
566         self.common.resume()
567     }
568 }
569 
570 impl Snapshottable for Console {
571     fn id(&self) -> String {
572         self.id.clone()
573     }
574 
575     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
576         Snapshot::new_from_versioned_state(&self.id, &self.state())
577     }
578 
579     fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
580         self.set_state(&snapshot.to_versioned_state(&self.id)?);
581         Ok(())
582     }
583 }
584 impl Transportable for Console {}
585 impl Migratable for Console {}
586