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