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