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