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