xref: /cloud-hypervisor/virtio-devices/src/console.rs (revision fee769bed4c58a07b67e25a7339cfd397f701f3a)
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 serde::{Deserialize, Serialize};
18 use serial_buffer::SerialBuffer;
19 use std::cmp;
20 use std::collections::VecDeque;
21 use std::fs::File;
22 use std::io;
23 use std::io::{Read, Write};
24 use std::os::unix::io::AsRawFd;
25 use std::result;
26 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
27 use std::sync::{Arc, Barrier, Mutex};
28 use thiserror::Error;
29 use virtio_queue::{Queue, QueueT};
30 use vm_memory::{ByteValued, Bytes, GuestAddressSpace, GuestMemory, GuestMemoryAtomic};
31 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable};
32 use vm_virtio::{AccessPlatform, Translatable};
33 use vmm_sys_util::eventfd::EventFd;
34 
35 const QUEUE_SIZE: u16 = 256;
36 const NUM_QUEUES: usize = 2;
37 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
38 
39 // New descriptors are pending on the virtio queue.
40 const INPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1;
41 const OUTPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2;
42 // Console configuration change event is triggered.
43 const CONFIG_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3;
44 // File written to (input ready)
45 const FILE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4;
46 // Console resized
47 const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 5;
48 
49 //Console size feature bit
50 const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
51 
52 #[derive(Error, Debug)]
53 enum Error {
54     #[error("Descriptor chain too short")]
55     DescriptorChainTooShort,
56     #[error("Failed to read from guest memory: {0}")]
57     GuestMemoryRead(vm_memory::guest_memory::Error),
58     #[error("Failed to write to guest memory: {0}")]
59     GuestMemoryWrite(vm_memory::guest_memory::Error),
60     #[error("Failed to write_all output: {0}")]
61     OutputWriteAll(io::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, Serialize, Deserialize)]
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                 let mut buf: Vec<u8> = Vec::new();
268                 desc_chain
269                     .memory()
270                     .write_volatile_to(
271                         desc.addr()
272                             .translate_gva(self.access_platform.as_ref(), desc.len() as usize),
273                         &mut buf,
274                         desc.len() as usize,
275                     )
276                     .map_err(Error::GuestMemoryRead)?;
277 
278                 out.write_all(&buf).map_err(Error::OutputWriteAll)?;
279                 out.flush().map_err(Error::OutputFlush)?;
280             }
281             trans_queue
282                 .add_used(desc_chain.memory(), desc_chain.head_index(), desc.len())
283                 .map_err(Error::QueueAddUsed)?;
284             used_descs = true;
285         }
286 
287         Ok(used_descs)
288     }
289 
290     fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> {
291         self.interrupt_cb
292             .trigger(VirtioInterruptType::Queue(queue_index))
293             .map_err(|e| {
294                 error!("Failed to signal used queue: {:?}", e);
295                 DeviceError::FailedSignalingUsedQueue(e)
296             })
297     }
298 
299     fn run(
300         &mut self,
301         paused: Arc<AtomicBool>,
302         paused_sync: Arc<Barrier>,
303     ) -> result::Result<(), EpollHelperError> {
304         let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?;
305         helper.add_event(self.input_queue_evt.as_raw_fd(), INPUT_QUEUE_EVENT)?;
306         helper.add_event(self.output_queue_evt.as_raw_fd(), OUTPUT_QUEUE_EVENT)?;
307         helper.add_event(self.config_evt.as_raw_fd(), CONFIG_EVENT)?;
308         if let Some(resize_pipe) = self.resize_pipe.as_ref() {
309             helper.add_event(resize_pipe.as_raw_fd(), RESIZE_EVENT)?;
310         }
311         if let Some(in_file) = self.endpoint.in_file() {
312             let mut events = epoll::Events::EPOLLIN;
313             if self.endpoint.is_pty() {
314                 events |= epoll::Events::EPOLLONESHOT;
315             }
316             helper.add_event_custom(in_file.as_raw_fd(), FILE_EVENT, events)?;
317             self.file_event_registered = true;
318         }
319 
320         // In case of PTY, we want to be able to detect a connection on the
321         // other end of the PTY. This is done by detecting there's no event
322         // triggered on the epoll, which is the reason why we want the
323         // epoll_wait() function to return after the timeout expired.
324         // In case of TTY, we don't expect to detect such behavior, which is
325         // why we can afford to block until an actual event is triggered.
326         let (timeout, enable_event_list) = if self.endpoint.is_pty() {
327             (500, true)
328         } else {
329             (-1, false)
330         };
331         helper.run_with_timeout(paused, paused_sync, self, timeout, enable_event_list)?;
332 
333         Ok(())
334     }
335 
336     // This function should be called when the other end of the PTY is
337     // connected. It verifies if this is the first time it's been invoked
338     // after the connection happened, and if that's the case it flushes
339     // all output from the console to the PTY. Otherwise, it's a no-op.
340     fn trigger_pty_flush(&mut self) -> result::Result<(), anyhow::Error> {
341         if let (Some(pty_write_out), Some(out)) = (&self.write_out, &mut self.out) {
342             if pty_write_out.load(Ordering::Acquire) {
343                 return Ok(());
344             }
345             pty_write_out.store(true, Ordering::Release);
346             out.flush()
347                 .map_err(|e| anyhow!("Failed to flush PTY: {:?}", e))
348         } else {
349             Ok(())
350         }
351     }
352 
353     fn register_file_event(
354         &mut self,
355         helper: &mut EpollHelper,
356     ) -> result::Result<(), EpollHelperError> {
357         if self.file_event_registered {
358             return Ok(());
359         }
360 
361         // Re-arm the file event.
362         helper.mod_event_custom(
363             self.endpoint.in_file().unwrap().as_raw_fd(),
364             FILE_EVENT,
365             epoll::Events::EPOLLIN | epoll::Events::EPOLLONESHOT,
366         )?;
367         self.file_event_registered = true;
368 
369         Ok(())
370     }
371 }
372 
373 impl EpollHelperHandler for ConsoleEpollHandler {
374     fn handle_event(
375         &mut self,
376         helper: &mut EpollHelper,
377         event: &epoll::Event,
378     ) -> result::Result<(), EpollHelperError> {
379         let ev_type = event.data as u16;
380 
381         match ev_type {
382             INPUT_QUEUE_EVENT => {
383                 self.input_queue_evt.read().map_err(|e| {
384                     EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
385                 })?;
386                 let needs_notification = self.process_input_queue().map_err(|e| {
387                     EpollHelperError::HandleEvent(anyhow!(
388                         "Failed to process input queue : {:?}",
389                         e
390                     ))
391                 })?;
392                 if needs_notification {
393                     self.signal_used_queue(0).map_err(|e| {
394                         EpollHelperError::HandleEvent(anyhow!(
395                             "Failed to signal used queue: {:?}",
396                             e
397                         ))
398                     })?;
399                 }
400             }
401             OUTPUT_QUEUE_EVENT => {
402                 self.output_queue_evt.read().map_err(|e| {
403                     EpollHelperError::HandleEvent(anyhow!("Failed to get queue event: {:?}", e))
404                 })?;
405                 let needs_notification = self.process_output_queue().map_err(|e| {
406                     EpollHelperError::HandleEvent(anyhow!(
407                         "Failed to process output queue : {:?}",
408                         e
409                     ))
410                 })?;
411                 if needs_notification {
412                     self.signal_used_queue(1).map_err(|e| {
413                         EpollHelperError::HandleEvent(anyhow!(
414                             "Failed to signal used queue: {:?}",
415                             e
416                         ))
417                     })?;
418                 }
419             }
420             CONFIG_EVENT => {
421                 self.config_evt.read().map_err(|e| {
422                     EpollHelperError::HandleEvent(anyhow!("Failed to get config event: {:?}", e))
423                 })?;
424                 self.interrupt_cb
425                     .trigger(VirtioInterruptType::Config)
426                     .map_err(|e| {
427                         EpollHelperError::HandleEvent(anyhow!(
428                             "Failed to signal console driver: {:?}",
429                             e
430                         ))
431                     })?;
432             }
433             RESIZE_EVENT => {
434                 self.resize_pipe
435                     .as_ref()
436                     .unwrap()
437                     .read_exact(&mut [0])
438                     .map_err(|e| {
439                         EpollHelperError::HandleEvent(anyhow!(
440                             "Failed to get resize event: {:?}",
441                             e
442                         ))
443                     })?;
444                 self.resizer.update_console_size();
445             }
446             FILE_EVENT => {
447                 if event.events & libc::EPOLLIN as u32 != 0 {
448                     let mut input = [0u8; 64];
449                     if let Some(ref mut in_file) = self.endpoint.in_file() {
450                         if let Ok(count) = in_file.read(&mut input) {
451                             let mut in_buffer = self.in_buffer.lock().unwrap();
452                             in_buffer.extend(&input[..count]);
453                         }
454 
455                         let needs_notification = self.process_input_queue().map_err(|e| {
456                             EpollHelperError::HandleEvent(anyhow!(
457                                 "Failed to process input queue : {:?}",
458                                 e
459                             ))
460                         })?;
461                         if needs_notification {
462                             self.signal_used_queue(0).map_err(|e| {
463                                 EpollHelperError::HandleEvent(anyhow!(
464                                     "Failed to signal used queue: {:?}",
465                                     e
466                                 ))
467                             })?;
468                         }
469                     }
470                 }
471                 if self.endpoint.is_pty() {
472                     self.file_event_registered = false;
473                     if event.events & libc::EPOLLHUP as u32 != 0 {
474                         if let Some(pty_write_out) = &self.write_out {
475                             if pty_write_out.load(Ordering::Acquire) {
476                                 pty_write_out.store(false, Ordering::Release);
477                             }
478                         }
479                     } else {
480                         // If the EPOLLHUP flag is not up on the associated event, we
481                         // can assume the other end of the PTY is connected and therefore
482                         // we can flush the output of the serial to it.
483                         self.trigger_pty_flush()
484                             .map_err(EpollHelperError::HandleTimeout)?;
485 
486                         self.register_file_event(helper)?;
487                     }
488                 }
489             }
490             _ => {
491                 return Err(EpollHelperError::HandleEvent(anyhow!(
492                     "Unknown event for virtio-console"
493                 )));
494             }
495         }
496         Ok(())
497     }
498 
499     // This function will be invoked whenever the timeout is reached before
500     // any other event was triggered while waiting for the epoll.
501     fn handle_timeout(&mut self, helper: &mut EpollHelper) -> Result<(), EpollHelperError> {
502         if !self.endpoint.is_pty() {
503             return Ok(());
504         }
505 
506         if self.file_event_registered {
507             // This very specific case happens when the console is connected
508             // to a PTY. We know EPOLLHUP is always present when there's nothing
509             // connected at the other end of the PTY. That's why getting no event
510             // means we can flush the output of the console through the PTY.
511             self.trigger_pty_flush()
512                 .map_err(EpollHelperError::HandleTimeout)?;
513         }
514 
515         // Every time we hit the timeout, let's register the FILE_EVENT to give
516         // us a chance to catch a possible event that might have been triggered.
517         self.register_file_event(helper)
518     }
519 
520     // This function returns the full list of events found on the epoll before
521     // iterating through it calling handle_event(). It allows the detection of
522     // the PTY connection even when the timeout is not being triggered, which
523     // happens when there are other events preventing the timeout from being
524     // reached. This is an additional way of detecting a PTY connection.
525     fn event_list(
526         &mut self,
527         helper: &mut EpollHelper,
528         events: &[epoll::Event],
529     ) -> Result<(), EpollHelperError> {
530         if self.file_event_registered {
531             for event in events {
532                 if event.data as u16 == FILE_EVENT && (event.events & libc::EPOLLHUP as u32) != 0 {
533                     return Ok(());
534                 }
535             }
536 
537             // This very specific case happens when the console is connected
538             // to a PTY. We know EPOLLHUP is always present when there's nothing
539             // connected at the other end of the PTY. That's why getting no event
540             // means we can flush the output of the console through the PTY.
541             self.trigger_pty_flush()
542                 .map_err(EpollHelperError::HandleTimeout)?;
543         }
544 
545         self.register_file_event(helper)
546     }
547 }
548 
549 /// Resize handler
550 pub struct ConsoleResizer {
551     config_evt: EventFd,
552     tty: Option<File>,
553     config: Arc<Mutex<VirtioConsoleConfig>>,
554     acked_features: AtomicU64,
555 }
556 
557 impl ConsoleResizer {
558     pub fn update_console_size(&self) {
559         if let Some(tty) = self.tty.as_ref() {
560             let (cols, rows) = get_win_size(tty);
561             self.config.lock().unwrap().update_console_size(cols, rows);
562             if self
563                 .acked_features
564                 .fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel)
565                 != 0
566             {
567                 // Send the interrupt to the driver
568                 let _ = self.config_evt.write(1);
569             }
570         }
571     }
572 }
573 
574 impl VirtioConsoleConfig {
575     pub fn update_console_size(&mut self, cols: u16, rows: u16) {
576         self.cols = cols;
577         self.rows = rows;
578     }
579 }
580 
581 /// Virtio device for exposing console to the guest OS through virtio.
582 pub struct Console {
583     common: VirtioCommon,
584     id: String,
585     config: Arc<Mutex<VirtioConsoleConfig>>,
586     resizer: Arc<ConsoleResizer>,
587     resize_pipe: Option<File>,
588     endpoint: Endpoint,
589     seccomp_action: SeccompAction,
590     in_buffer: Arc<Mutex<VecDeque<u8>>>,
591     exit_evt: EventFd,
592 }
593 
594 #[derive(Serialize, Deserialize)]
595 pub struct ConsoleState {
596     avail_features: u64,
597     acked_features: u64,
598     config: VirtioConsoleConfig,
599     in_buffer: Vec<u8>,
600 }
601 
602 fn get_win_size(tty: &dyn AsRawFd) -> (u16, u16) {
603     #[repr(C)]
604     #[derive(Default)]
605     struct WindowSize {
606         rows: u16,
607         cols: u16,
608         xpixel: u16,
609         ypixel: u16,
610     }
611     let mut ws: WindowSize = WindowSize::default();
612 
613     // SAFETY: FFI call with correct arguments
614     unsafe {
615         libc::ioctl(tty.as_raw_fd(), TIOCGWINSZ, &mut ws);
616     }
617 
618     (ws.cols, ws.rows)
619 }
620 
621 impl Console {
622     /// Create a new virtio console device
623     pub fn new(
624         id: String,
625         endpoint: Endpoint,
626         resize_pipe: Option<File>,
627         iommu: bool,
628         seccomp_action: SeccompAction,
629         exit_evt: EventFd,
630         state: Option<ConsoleState>,
631     ) -> io::Result<(Console, Arc<ConsoleResizer>)> {
632         let (avail_features, acked_features, config, in_buffer, paused) = if let Some(state) = state
633         {
634             info!("Restoring virtio-console {}", id);
635             (
636                 state.avail_features,
637                 state.acked_features,
638                 state.config,
639                 state.in_buffer.into(),
640                 true,
641             )
642         } else {
643             let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_CONSOLE_F_SIZE;
644             if iommu {
645                 avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM;
646             }
647 
648             (
649                 avail_features,
650                 0,
651                 VirtioConsoleConfig::default(),
652                 VecDeque::new(),
653                 false,
654             )
655         };
656 
657         let config_evt = EventFd::new(EFD_NONBLOCK).unwrap();
658         let console_config = Arc::new(Mutex::new(config));
659         let resizer = Arc::new(ConsoleResizer {
660             config_evt,
661             config: console_config.clone(),
662             tty: endpoint.out_file().as_ref().map(|t| t.try_clone().unwrap()),
663             acked_features: AtomicU64::new(acked_features),
664         });
665 
666         resizer.update_console_size();
667 
668         Ok((
669             Console {
670                 common: VirtioCommon {
671                     device_type: VirtioDeviceType::Console as u32,
672                     queue_sizes: QUEUE_SIZES.to_vec(),
673                     avail_features,
674                     acked_features,
675                     paused_sync: Some(Arc::new(Barrier::new(2))),
676                     min_queues: NUM_QUEUES as u16,
677                     paused: Arc::new(AtomicBool::new(paused)),
678                     ..Default::default()
679                 },
680                 id,
681                 config: console_config,
682                 resizer: resizer.clone(),
683                 resize_pipe,
684                 endpoint,
685                 seccomp_action,
686                 in_buffer: Arc::new(Mutex::new(in_buffer)),
687                 exit_evt,
688             },
689             resizer,
690         ))
691     }
692 
693     fn state(&self) -> ConsoleState {
694         ConsoleState {
695             avail_features: self.common.avail_features,
696             acked_features: self.common.acked_features,
697             config: *(self.config.lock().unwrap()),
698             in_buffer: self.in_buffer.lock().unwrap().clone().into(),
699         }
700     }
701 
702     #[cfg(fuzzing)]
703     pub fn wait_for_epoll_threads(&mut self) {
704         self.common.wait_for_epoll_threads();
705     }
706 }
707 
708 impl Drop for Console {
709     fn drop(&mut self) {
710         if let Some(kill_evt) = self.common.kill_evt.take() {
711             // Ignore the result because there is nothing we can do about it.
712             let _ = kill_evt.write(1);
713         }
714         self.common.wait_for_epoll_threads();
715     }
716 }
717 
718 impl VirtioDevice for Console {
719     fn device_type(&self) -> u32 {
720         self.common.device_type
721     }
722 
723     fn queue_max_sizes(&self) -> &[u16] {
724         &self.common.queue_sizes
725     }
726 
727     fn features(&self) -> u64 {
728         self.common.avail_features
729     }
730 
731     fn ack_features(&mut self, value: u64) {
732         self.common.ack_features(value)
733     }
734 
735     fn read_config(&self, offset: u64, data: &mut [u8]) {
736         self.read_config_from_slice(self.config.lock().unwrap().as_slice(), offset, data);
737     }
738 
739     fn activate(
740         &mut self,
741         mem: GuestMemoryAtomic<GuestMemoryMmap>,
742         interrupt_cb: Arc<dyn VirtioInterrupt>,
743         mut queues: Vec<(usize, Queue, EventFd)>,
744     ) -> ActivateResult {
745         self.common.activate(&queues, &interrupt_cb)?;
746         self.resizer
747             .acked_features
748             .store(self.common.acked_features, Ordering::Relaxed);
749 
750         if self.common.feature_acked(VIRTIO_CONSOLE_F_SIZE) {
751             if let Err(e) = interrupt_cb.trigger(VirtioInterruptType::Config) {
752                 error!("Failed to signal console driver: {:?}", e);
753             }
754         }
755 
756         let (kill_evt, pause_evt) = self.common.dup_eventfds();
757 
758         let (_, input_queue, input_queue_evt) = queues.remove(0);
759         let (_, output_queue, output_queue_evt) = queues.remove(0);
760 
761         let mut handler = ConsoleEpollHandler::new(
762             mem,
763             input_queue,
764             output_queue,
765             interrupt_cb,
766             self.in_buffer.clone(),
767             Arc::clone(&self.resizer),
768             self.endpoint.clone(),
769             input_queue_evt,
770             output_queue_evt,
771             self.resizer.config_evt.try_clone().unwrap(),
772             self.resize_pipe.as_ref().map(|p| p.try_clone().unwrap()),
773             kill_evt,
774             pause_evt,
775             self.common.access_platform.clone(),
776         );
777 
778         let paused = self.common.paused.clone();
779         let paused_sync = self.common.paused_sync.clone();
780         let mut epoll_threads = Vec::new();
781 
782         spawn_virtio_thread(
783             &self.id,
784             &self.seccomp_action,
785             Thread::VirtioConsole,
786             &mut epoll_threads,
787             &self.exit_evt,
788             move || handler.run(paused, paused_sync.unwrap()),
789         )?;
790 
791         self.common.epoll_threads = Some(epoll_threads);
792 
793         event!("virtio-device", "activated", "id", &self.id);
794         Ok(())
795     }
796 
797     fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> {
798         let result = self.common.reset();
799         event!("virtio-device", "reset", "id", &self.id);
800         result
801     }
802 
803     fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) {
804         self.common.set_access_platform(access_platform)
805     }
806 }
807 
808 impl Pausable for Console {
809     fn pause(&mut self) -> result::Result<(), MigratableError> {
810         self.common.pause()
811     }
812 
813     fn resume(&mut self) -> result::Result<(), MigratableError> {
814         self.common.resume()
815     }
816 }
817 
818 impl Snapshottable for Console {
819     fn id(&self) -> String {
820         self.id.clone()
821     }
822 
823     fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> {
824         Snapshot::new_from_state(&self.state())
825     }
826 }
827 impl Transportable for Console {}
828 impl Migratable for Console {}
829