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