xref: /cloud-hypervisor/virtio-devices/src/console.rs (revision eea9bcea38e0c5649f444c829f3a4f9c22aa486c)
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 anyhow::anyhow;
15 use libc::{EFD_NONBLOCK, TIOCGWINSZ};
16 use seccompiler::SeccompAction;
17 use serial_buffer::SerialBuffer;
18 use std::cmp;
19 use std::collections::VecDeque;
20 use std::fs::File;
21 use std::io;
22 use std::io::{Read, Write};
23 use std::os::unix::io::AsRawFd;
24 use std::result;
25 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
26 use std::sync::{Arc, Barrier, Mutex};
27 use versionize::{VersionMap, Versionize, VersionizeResult};
28 use versionize_derive::Versionize;
29 use virtio_queue::{Queue, QueueOwnedT, QueueT};
30 use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemoryAtomic};
31 use vm_migration::VersionMapped;
32 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
33 use vm_virtio::{AccessPlatform, Translatable};
34 use vmm_sys_util::eventfd::EventFd;
35 
36 const QUEUE_SIZE: u16 = 256;
37 const NUM_QUEUES: usize = 2;
38 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
39 
40 // New descriptors are pending on the virtio queue.
41 const INPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
42 const OUTPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
43 // Some input from the VMM is ready to be injected into the VM.
44 const INPUT_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
45 // Console configuration change event is triggered.
46 const CONFIG_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4;
47 // File written to (input ready)
48 const FILE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 5;
49 // Console resized
50 const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 6;
51 
52 //Console size feature bit
53 const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
54 
55 #[derive(Copy, Clone, Debug, Versionize)]
56 #[repr(C, packed)]
57 pub struct VirtioConsoleConfig {
58     cols: u16,
59     rows: u16,
60     max_nr_ports: u32,
61     emerg_wr: u32,
62 }
63 
64 impl Default for VirtioConsoleConfig {
65     fn default() -> Self {
66         VirtioConsoleConfig {
67             cols: 0,
68             rows: 0,
69             max_nr_ports: 1,
70             emerg_wr: 0,
71         }
72     }
73 }
74 
75 // SAFETY: it only has data and has no implicit padding.
76 unsafe impl ByteValued for VirtioConsoleConfig {}
77 
78 struct ConsoleEpollHandler {
79     mem: GuestMemoryAtomic<GuestMemoryMmap>,
80     queues: Vec<Queue>,
81     interrupt_cb: Arc<dyn VirtioInterrupt>,
82     in_buffer: Arc<Mutex<VecDeque<u8>>>,
83     resizer: Arc<ConsoleResizer>,
84     endpoint: Endpoint,
85     input_queue_evt: EventFd,
86     output_queue_evt: EventFd,
87     input_evt: EventFd,
88     config_evt: EventFd,
89     resize_pipe: Option<File>,
90     kill_evt: EventFd,
91     pause_evt: EventFd,
92     access_platform: Option<Arc<dyn AccessPlatform>>,
93     out: Option<Box<dyn Write + Send>>,
94     write_out: Option<Arc<AtomicBool>>,
95     file_event_registered: bool,
96 }
97 
98 pub enum Endpoint {
99     File(File),
100     FilePair(File, File),
101     PtyPair(File, File),
102     Null,
103 }
104 
105 impl Endpoint {
106     fn out_file(&self) -> Option<&File> {
107         match self {
108             Self::File(f) => Some(f),
109             Self::FilePair(f, _) => Some(f),
110             Self::PtyPair(f, _) => Some(f),
111             Self::Null => None,
112         }
113     }
114 
115     fn in_file(&self) -> Option<&File> {
116         match self {
117             Self::File(_) => None,
118             Self::FilePair(_, f) => Some(f),
119             Self::PtyPair(_, f) => Some(f),
120             Self::Null => None,
121         }
122     }
123 
124     fn is_pty(&self) -> bool {
125         matches!(self, Self::PtyPair(_, _))
126     }
127 }
128 
129 impl Clone for Endpoint {
130     fn clone(&self) -> Self {
131         match self {
132             Self::File(f) => Self::File(f.try_clone().unwrap()),
133             Self::FilePair(f_out, f_in) => {
134                 Self::FilePair(f_out.try_clone().unwrap(), f_in.try_clone().unwrap())
135             }
136             Self::PtyPair(f_out, f_in) => {
137                 Self::PtyPair(f_out.try_clone().unwrap(), f_in.try_clone().unwrap())
138             }
139             Self::Null => Self::Null,
140         }
141     }
142 }
143 
144 impl ConsoleEpollHandler {
145     #[allow(clippy::too_many_arguments)]
146     fn new(
147         mem: GuestMemoryAtomic<GuestMemoryMmap>,
148         queues: Vec<Queue>,
149         interrupt_cb: Arc<dyn VirtioInterrupt>,
150         in_buffer: Arc<Mutex<VecDeque<u8>>>,
151         resizer: Arc<ConsoleResizer>,
152         endpoint: Endpoint,
153         input_queue_evt: EventFd,
154         output_queue_evt: EventFd,
155         input_evt: EventFd,
156         config_evt: EventFd,
157         resize_pipe: Option<File>,
158         kill_evt: EventFd,
159         pause_evt: EventFd,
160         access_platform: Option<Arc<dyn AccessPlatform>>,
161     ) -> Self {
162         let out_file = endpoint.out_file();
163         let (out, write_out) = if let Some(out_file) = out_file {
164             let writer = out_file.try_clone().unwrap();
165             if endpoint.is_pty() {
166                 let pty_write_out = Arc::new(AtomicBool::new(false));
167                 let write_out = Some(pty_write_out.clone());
168                 let buffer = SerialBuffer::new(Box::new(writer), pty_write_out);
169                 (Some(Box::new(buffer) as Box<dyn Write + Send>), write_out)
170             } else {
171                 (Some(Box::new(writer) as Box<dyn Write + Send>), None)
172             }
173         } else {
174             (None, None)
175         };
176 
177         ConsoleEpollHandler {
178             mem,
179             queues,
180             interrupt_cb,
181             in_buffer,
182             resizer,
183             endpoint,
184             input_queue_evt,
185             output_queue_evt,
186             input_evt,
187             config_evt,
188             resize_pipe,
189             kill_evt,
190             pause_evt,
191             access_platform,
192             out,
193             write_out,
194             file_event_registered: false,
195         }
196     }
197 
198     /*
199      * Each port of virtio console device has one receive
200      * queue. One or more empty buffers are placed by the
201      * driver in the receive queue for incoming data. Here,
202      * we place the input data to these empty buffers.
203      */
204     fn process_input_queue(&mut self) -> bool {
205         let mut in_buffer = self.in_buffer.lock().unwrap();
206         let recv_queue = &mut self.queues[0]; //receiveq
207         let mut used_descs = false;
208 
209         if in_buffer.is_empty() {
210             return false;
211         }
212 
213         while let Some(mut desc_chain) = recv_queue.pop_descriptor_chain(self.mem.memory()) {
214             let desc = desc_chain.next().unwrap();
215             let len = cmp::min(desc.len() as u32, in_buffer.len() as u32);
216             let source_slice = in_buffer.drain(..len as usize).collect::<Vec<u8>>();
217 
218             if let Err(e) = desc_chain.memory().write_slice(
219                 &source_slice[..],
220                 desc.addr()
221                     .translate_gva(self.access_platform.as_ref(), desc.len() as usize),
222             ) {
223                 error!("Failed to write slice: {:?}", e);
224                 recv_queue.go_to_previous_position();
225                 break;
226             }
227 
228             recv_queue
229                 .add_used(desc_chain.memory(), desc_chain.head_index(), len)
230                 .unwrap();
231             used_descs = true;
232 
233             if in_buffer.is_empty() {
234                 break;
235             }
236         }
237 
238         used_descs
239     }
240 
241     /*
242      * Each port of virtio console device has one transmit
243      * queue. For outgoing data, characters are placed in
244      * the transmit queue by the driver. Therefore, here
245      * we read data from the transmit queue and flush them
246      * to the referenced address.
247      */
248     fn process_output_queue(&mut self) -> bool {
249         let trans_queue = &mut self.queues[1]; //transmitq
250         let mut used_descs = false;
251 
252         while let Some(mut desc_chain) = trans_queue.pop_descriptor_chain(self.mem.memory()) {
253             let desc = desc_chain.next().unwrap();
254             if let Some(out) = &mut self.out {
255                 let _ = desc_chain.memory().write_to(
256                     desc.addr()
257                         .translate_gva(self.access_platform.as_ref(), desc.len() as usize),
258                     out,
259                     desc.len() as usize,
260                 );
261                 let _ = out.flush();
262             }
263             trans_queue
264                 .add_used(desc_chain.memory(), desc_chain.head_index(), desc.len())
265                 .unwrap();
266             used_descs = true;
267         }
268 
269         used_descs
270     }
271 
272     fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
273         self.interrupt_cb
274             .trigger(VirtioInterruptType::Queue(queue_index))
275             .map_err(|e| {
276                 error!("Failed to signal used queue: {:?}", e);
277                 DeviceError::FailedSignalingUsedQueue(e)
278             })
279     }
280 
281     fn run(
282         &mut self,
283         paused: Arc<AtomicBool>,
284         paused_sync: Arc<Barrier>,
285     ) -> result::Result<(), EpollHelperError> {
286         let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
287         helper.add_event(self.input_queue_evt.as_raw_fd(), INPUT_QUEUE_EVENT)?;
288         helper.add_event(self.output_queue_evt.as_raw_fd(), OUTPUT_QUEUE_EVENT)?;
289         helper.add_event(self.input_evt.as_raw_fd(), INPUT_EVENT)?;
290         helper.add_event(self.config_evt.as_raw_fd(), CONFIG_EVENT)?;
291         if let Some(resize_pipe) = self.resize_pipe.as_ref() {
292             helper.add_event(resize_pipe.as_raw_fd(), RESIZE_EVENT)?;
293         }
294         if let Some(in_file) = self.endpoint.in_file() {
295             let mut events = epoll::Events::EPOLLIN;
296             if self.endpoint.is_pty() {
297                 events |= epoll::Events::EPOLLONESHOT;
298             }
299             helper.add_event_custom(in_file.as_raw_fd(), FILE_EVENT, events)?;
300             self.file_event_registered = true;
301         }
302 
303         // In case of PTY, we want to be able to detect a connection on the
304         // other end of the PTY. This is done by detecting there's no event
305         // triggered on the epoll, which is the reason why we want the
306         // epoll_wait() function to return after the timeout expired.
307         // In case of TTY, we don't expect to detect such behavior, which is
308         // why we can afford to block until an actual event is triggered.
309         let (timeout, enable_event_list) = if self.endpoint.is_pty() {
310             (500, true)
311         } else {
312             (-1, false)
313         };
314         helper.run_with_timeout(paused, paused_sync, self, timeout, enable_event_list)?;
315 
316         Ok(())
317     }
318 
319     // This function should be called when the other end of the PTY is
320     // connected. It verifies if this is the first time it's been invoked
321     // after the connection happened, and if that's the case it flushes
322     // all output from the console to the PTY. Otherwise, it's a no-op.
323     fn trigger_pty_flush(&mut self) -> result::Result<(), anyhow::Error> {
324         if let (Some(pty_write_out), Some(out)) = (&self.write_out, &mut self.out) {
325             if pty_write_out.load(Ordering::Acquire) {
326                 return Ok(());
327             }
328             pty_write_out.store(true, Ordering::Release);
329             out.flush()
330                 .map_err(|e| anyhow!("Failed to flush PTY: {:?}", e))
331         } else {
332             Ok(())
333         }
334     }
335 
336     fn register_file_event(
337         &mut self,
338         helper: &mut EpollHelper,
339     ) -> result::Result<(), EpollHelperError> {
340         if self.file_event_registered {
341             return Ok(());
342         }
343 
344         // Re-arm the file event.
345         helper.mod_event_custom(
346             self.endpoint.in_file().unwrap().as_raw_fd(),
347             FILE_EVENT,
348             epoll::Events::EPOLLIN | epoll::Events::EPOLLONESHOT,
349         )?;
350         self.file_event_registered = true;
351 
352         Ok(())
353     }
354 }
355 
356 impl EpollHelperHandler for ConsoleEpollHandler {
357     fn handle_event(
358         &mut self,
359         helper: &mut EpollHelper,
360         event: &epoll::Event,
361     ) -> result::Result<(), EpollHelperError> {
362         let ev_type = event.data as u16;
363 
364         match ev_type {
365             INPUT_QUEUE_EVENT => {
366                 self.input_queue_evt.read().map_err(|e| {
367                     EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
368                 })?;
369                 if self.process_input_queue() {
370                     self.signal_used_queue(0).map_err(|e| {
371                         EpollHelperError::HandleEvent(anyhow!(
372                             "Failed to signal used queue: {:?}",
373                             e
374                         ))
375                     })?;
376                 }
377             }
378             OUTPUT_QUEUE_EVENT => {
379                 self.output_queue_evt.read().map_err(|e| {
380                     EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
381                 })?;
382                 if self.process_output_queue() {
383                     self.signal_used_queue(1).map_err(|e| {
384                         EpollHelperError::HandleEvent(anyhow!(
385                             "Failed to signal used queue: {:?}",
386                             e
387                         ))
388                     })?;
389                 }
390             }
391             INPUT_EVENT => {
392                 self.input_evt.read().map_err(|e| {
393                     EpollHelperError::HandleEvent(anyhow!("Failed to get input event: {:?}", e))
394                 })?;
395                 if self.process_input_queue() {
396                     self.signal_used_queue(0).map_err(|e| {
397                         EpollHelperError::HandleEvent(anyhow!(
398                             "Failed to signal used queue: {:?}",
399                             e
400                         ))
401                     })?;
402                 }
403             }
404             CONFIG_EVENT => {
405                 self.config_evt.read().map_err(|e| {
406                     EpollHelperError::HandleEvent(anyhow!("Failed to get config event: {:?}", e))
407                 })?;
408                 self.interrupt_cb
409                     .trigger(VirtioInterruptType::Config)
410                     .map_err(|e| {
411                         EpollHelperError::HandleEvent(anyhow!(
412                             "Failed to signal console driver: {:?}",
413                             e
414                         ))
415                     })?;
416             }
417             RESIZE_EVENT => {
418                 self.resize_pipe
419                     .as_ref()
420                     .unwrap()
421                     .read_exact(&mut [0])
422                     .map_err(|e| {
423                         EpollHelperError::HandleEvent(anyhow!(
424                             "Failed to get resize event: {:?}",
425                             e
426                         ))
427                     })?;
428                 self.resizer.update_console_size();
429             }
430             FILE_EVENT => {
431                 if event.events & libc::EPOLLIN as u32 != 0 {
432                     let mut input = [0u8; 64];
433                     if let Some(ref mut in_file) = self.endpoint.in_file() {
434                         if let Ok(count) = in_file.read(&mut input) {
435                             let mut in_buffer = self.in_buffer.lock().unwrap();
436                             in_buffer.extend(&input[..count]);
437                         }
438 
439                         if self.process_input_queue() {
440                             self.signal_used_queue(0).map_err(|e| {
441                                 EpollHelperError::HandleEvent(anyhow!(
442                                     "Failed to signal used queue: {:?}",
443                                     e
444                                 ))
445                             })?;
446                         }
447                     }
448                 }
449                 if self.endpoint.is_pty() {
450                     self.file_event_registered = false;
451                     if event.events & libc::EPOLLHUP as u32 != 0 {
452                         if let Some(pty_write_out) = &self.write_out {
453                             if pty_write_out.load(Ordering::Acquire) {
454                                 pty_write_out.store(false, Ordering::Release);
455                             }
456                         }
457                     } else {
458                         // If the EPOLLHUP flag is not up on the associated event, we
459                         // can assume the other end of the PTY is connected and therefore
460                         // we can flush the output of the serial to it.
461                         self.trigger_pty_flush()
462                             .map_err(EpollHelperError::HandleTimeout)?;
463 
464                         self.register_file_event(helper)?;
465                     }
466                 }
467             }
468             _ => {
469                 return Err(EpollHelperError::HandleEvent(anyhow!(
470                     "Unknown event for virtio-console"
471                 )));
472             }
473         }
474         Ok(())
475     }
476 
477     // This function will be invoked whenever the timeout is reached before
478     // any other event was triggered while waiting for the epoll.
479     fn handle_timeout(&mut self, helper: &mut EpollHelper) -> Result<(), EpollHelperError> {
480         if !self.endpoint.is_pty() {
481             return Ok(());
482         }
483 
484         if self.file_event_registered {
485             // This very specific case happens when the console is connected
486             // to a PTY. We know EPOLLHUP is always present when there's nothing
487             // connected at the other end of the PTY. That's why getting no event
488             // means we can flush the output of the console through the PTY.
489             self.trigger_pty_flush()
490                 .map_err(EpollHelperError::HandleTimeout)?;
491         }
492 
493         // Every time we hit the timeout, let's register the FILE_EVENT to give
494         // us a chance to catch a possible event that might have been triggered.
495         self.register_file_event(helper)
496     }
497 
498     // This function returns the full list of events found on the epoll before
499     // iterating through it calling handle_event(). It allows the detection of
500     // the PTY connection even when the timeout is not being triggered, which
501     // happens when there are other events preventing the timeout from being
502     // reached. This is an additional way of detecting a PTY connection.
503     fn event_list(
504         &mut self,
505         helper: &mut EpollHelper,
506         events: &[epoll::Event],
507     ) -> Result<(), EpollHelperError> {
508         if self.file_event_registered {
509             for event in events {
510                 if event.data as u16 == FILE_EVENT && (event.events & libc::EPOLLHUP as u32) != 0 {
511                     return Ok(());
512                 }
513             }
514 
515             // This very specific case happens when the console is connected
516             // to a PTY. We know EPOLLHUP is always present when there's nothing
517             // connected at the other end of the PTY. That's why getting no event
518             // means we can flush the output of the console through the PTY.
519             self.trigger_pty_flush()
520                 .map_err(EpollHelperError::HandleTimeout)?;
521         }
522 
523         self.register_file_event(helper)
524     }
525 }
526 
527 /// Resize handler
528 pub struct ConsoleResizer {
529     config_evt: EventFd,
530     tty: Option<File>,
531     config: Arc<Mutex<VirtioConsoleConfig>>,
532     acked_features: AtomicU64,
533 }
534 
535 impl ConsoleResizer {
536     pub fn update_console_size(&self) {
537         if let Some(tty) = self.tty.as_ref() {
538             let (cols, rows) = get_win_size(tty);
539             self.config.lock().unwrap().update_console_size(cols, rows);
540             if self
541                 .acked_features
542                 .fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel)
543                 != 0
544             {
545                 // Send the interrupt to the driver
546                 let _ = self.config_evt.write(1);
547             }
548         }
549     }
550 }
551 
552 impl VirtioConsoleConfig {
553     pub fn update_console_size(&mut self, cols: u16, rows: u16) {
554         self.cols = cols;
555         self.rows = rows;
556     }
557 }
558 
559 /// Virtio device for exposing console to the guest OS through virtio.
560 pub struct Console {
561     common: VirtioCommon,
562     id: String,
563     config: Arc<Mutex<VirtioConsoleConfig>>,
564     resizer: Arc<ConsoleResizer>,
565     resize_pipe: Option<File>,
566     endpoint: Endpoint,
567     seccomp_action: SeccompAction,
568     in_buffer: Arc<Mutex<VecDeque<u8>>>,
569     exit_evt: EventFd,
570 }
571 
572 #[derive(Versionize)]
573 pub struct ConsoleState {
574     avail_features: u64,
575     acked_features: u64,
576     config: VirtioConsoleConfig,
577     in_buffer: Vec<u8>,
578 }
579 
580 fn get_win_size(tty: &dyn AsRawFd) -> (u16, u16) {
581     #[repr(C)]
582     #[derive(Default)]
583     struct WindowSize {
584         rows: u16,
585         cols: u16,
586         xpixel: u16,
587         ypixel: u16,
588     }
589     let ws: WindowSize = WindowSize::default();
590 
591     unsafe {
592         libc::ioctl(tty.as_raw_fd(), TIOCGWINSZ, &ws);
593     }
594 
595     (ws.cols, ws.rows)
596 }
597 
598 impl VersionMapped for ConsoleState {}
599 
600 impl Console {
601     /// Create a new virtio console device that gets random data from /dev/urandom.
602     pub fn new(
603         id: String,
604         endpoint: Endpoint,
605         resize_pipe: Option<File>,
606         iommu: bool,
607         seccomp_action: SeccompAction,
608         exit_evt: EventFd,
609     ) -> io::Result<(Console, Arc<ConsoleResizer>)> {
610         let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_CONSOLE_F_SIZE;
611 
612         if iommu {
613             avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
614         }
615 
616         let config_evt = EventFd::new(EFD_NONBLOCK).unwrap();
617         let console_config = Arc::new(Mutex::new(VirtioConsoleConfig::default()));
618         let resizer = Arc::new(ConsoleResizer {
619             config_evt,
620             config: console_config.clone(),
621             tty: endpoint.out_file().as_ref().map(|t| t.try_clone().unwrap()),
622             acked_features: AtomicU64::new(0),
623         });
624 
625         resizer.update_console_size();
626 
627         Ok((
628             Console {
629                 common: VirtioCommon {
630                     device_type: VirtioDeviceType::Console as u32,
631                     queue_sizes: QUEUE_SIZES.to_vec(),
632                     avail_features,
633                     paused_sync: Some(Arc::new(Barrier::new(2))),
634                     min_queues: NUM_QUEUES as u16,
635                     ..Default::default()
636                 },
637                 id,
638                 config: console_config,
639                 resizer: resizer.clone(),
640                 resize_pipe,
641                 endpoint,
642                 seccomp_action,
643                 in_buffer: Arc::new(Mutex::new(VecDeque::new())),
644                 exit_evt,
645             },
646             resizer,
647         ))
648     }
649 
650     fn state(&self) -> ConsoleState {
651         ConsoleState {
652             avail_features: self.common.avail_features,
653             acked_features: self.common.acked_features,
654             config: *(self.config.lock().unwrap()),
655             in_buffer: self.in_buffer.lock().unwrap().clone().into(),
656         }
657     }
658 
659     fn set_state(&mut self, state: &ConsoleState) {
660         self.common.avail_features = state.avail_features;
661         self.common.acked_features = state.acked_features;
662         *(self.config.lock().unwrap()) = state.config;
663         *(self.in_buffer.lock().unwrap()) = state.in_buffer.clone().into();
664     }
665 }
666 
667 impl Drop for Console {
668     fn drop(&mut self) {
669         if let Some(kill_evt) = self.common.kill_evt.take() {
670             // Ignore the result because there is nothing we can do about it.
671             let _ = kill_evt.write(1);
672         }
673     }
674 }
675 
676 impl VirtioDevice for Console {
677     fn device_type(&self) -> u32 {
678         self.common.device_type
679     }
680 
681     fn queue_max_sizes(&self) -> &[u16] {
682         &self.common.queue_sizes
683     }
684 
685     fn features(&self) -> u64 {
686         self.common.avail_features
687     }
688 
689     fn ack_features(&mut self, value: u64) {
690         self.common.ack_features(value)
691     }
692 
693     fn read_config(&self, offset: u64, data: &mut [u8]) {
694         self.read_config_from_slice(self.config.lock().unwrap().as_slice(), offset, data);
695     }
696 
697     fn activate(
698         &mut self,
699         mem: GuestMemoryAtomic<GuestMemoryMmap>,
700         interrupt_cb: Arc<dyn VirtioInterrupt>,
701         mut queues: Vec<(usize, Queue, EventFd)>,
702     ) -> ActivateResult {
703         self.common.activate(&queues, &interrupt_cb)?;
704         self.resizer
705             .acked_features
706             .store(self.common.acked_features, Ordering::Relaxed);
707 
708         if self.common.feature_acked(VIRTIO_CONSOLE_F_SIZE) {
709             if let Err(e) = interrupt_cb.trigger(VirtioInterruptType::Config) {
710                 error!("Failed to signal console driver: {:?}", e);
711             }
712         }
713 
714         let (kill_evt, pause_evt) = self.common.dup_eventfds();
715         let input_evt = EventFd::new(EFD_NONBLOCK).unwrap();
716 
717         let mut virtqueues = Vec::new();
718         let (_, queue, queue_evt) = queues.remove(0);
719         virtqueues.push(queue);
720         let input_queue_evt = queue_evt;
721         let (_, queue, queue_evt) = queues.remove(0);
722         virtqueues.push(queue);
723         let output_queue_evt = queue_evt;
724 
725         let mut handler = ConsoleEpollHandler::new(
726             mem,
727             virtqueues,
728             interrupt_cb,
729             self.in_buffer.clone(),
730             Arc::clone(&self.resizer),
731             self.endpoint.clone(),
732             input_queue_evt,
733             output_queue_evt,
734             input_evt,
735             self.resizer.config_evt.try_clone().unwrap(),
736             self.resize_pipe.as_ref().map(|p| p.try_clone().unwrap()),
737             kill_evt,
738             pause_evt,
739             self.common.access_platform.clone(),
740         );
741 
742         let paused = self.common.paused.clone();
743         let paused_sync = self.common.paused_sync.clone();
744         let mut epoll_threads = Vec::new();
745 
746         spawn_virtio_thread(
747             &self.id,
748             &self.seccomp_action,
749             Thread::VirtioConsole,
750             &mut epoll_threads,
751             &self.exit_evt,
752             move || handler.run(paused, paused_sync.unwrap()),
753         )?;
754 
755         self.common.epoll_threads = Some(epoll_threads);
756 
757         event!("virtio-device", "activated", "id", &self.id);
758         Ok(())
759     }
760 
761     fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
762         let result = self.common.reset();
763         event!("virtio-device", "reset", "id", &self.id);
764         result
765     }
766 
767     fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) {
768         self.common.set_access_platform(access_platform)
769     }
770 }
771 
772 impl Pausable for Console {
773     fn pause(&mut self) -> result::Result<(), MigratableError> {
774         self.common.pause()
775     }
776 
777     fn resume(&mut self) -> result::Result<(), MigratableError> {
778         self.common.resume()
779     }
780 }
781 
782 impl Snapshottable for Console {
783     fn id(&self) -> String {
784         self.id.clone()
785     }
786 
787     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
788         Snapshot::new_from_versioned_state(&self.id, &self.state())
789     }
790 
791     fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> {
792         self.set_state(&snapshot.to_versioned_state(&self.id)?);
793         Ok(())
794     }
795 }
796 impl Transportable for Console {}
797 impl Migratable for Console {}
798