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