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