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