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