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