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