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