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 libc::{EFD_NONBLOCK, TIOCGWINSZ}; 15 use seccompiler::SeccompAction; 16 use std::cmp; 17 use std::collections::VecDeque; 18 use std::fs::File; 19 use std::io; 20 use std::io::{Read, Write}; 21 use std::os::unix::io::AsRawFd; 22 use std::result; 23 use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; 24 use std::sync::{Arc, Barrier, Mutex}; 25 use versionize::{VersionMap, Versionize, VersionizeResult}; 26 use versionize_derive::Versionize; 27 use virtio_queue::Queue; 28 use vm_memory::{ByteValued, Bytes, GuestMemoryAtomic}; 29 use vm_migration::VersionMapped; 30 use vm_migration::{Migratable, MigratableError, Pausable, Snapshot, Snapshottable, Transportable}; 31 use vm_virtio::{AccessPlatform, Translatable}; 32 use vmm_sys_util::eventfd::EventFd; 33 34 const QUEUE_SIZE: u16 = 256; 35 const NUM_QUEUES: usize = 2; 36 const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES]; 37 38 // New descriptors are pending on the virtio queue. 39 const INPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 1; 40 const OUTPUT_QUEUE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 2; 41 // Some input from the VMM is ready to be injected into the VM. 42 const INPUT_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 3; 43 // Console configuration change event is triggered. 44 const CONFIG_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 4; 45 // File written to (input ready) 46 const FILE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 5; 47 // Console resized 48 const RESIZE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 6; 49 50 //Console size feature bit 51 const VIRTIO_CONSOLE_F_SIZE: u64 = 0; 52 53 #[derive(Copy, Clone, Debug, Versionize)] 54 #[repr(C, packed)] 55 pub struct VirtioConsoleConfig { 56 cols: u16, 57 rows: u16, 58 max_nr_ports: u32, 59 emerg_wr: u32, 60 } 61 62 impl Default for VirtioConsoleConfig { 63 fn default() -> Self { 64 VirtioConsoleConfig { 65 cols: 0, 66 rows: 0, 67 max_nr_ports: 1, 68 emerg_wr: 0, 69 } 70 } 71 } 72 73 // SAFETY: it only has data and has no implicit padding. 74 unsafe impl ByteValued for VirtioConsoleConfig {} 75 76 struct ConsoleEpollHandler { 77 queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>, 78 interrupt_cb: Arc<dyn VirtioInterrupt>, 79 in_buffer: Arc<Mutex<VecDeque<u8>>>, 80 resizer: Arc<ConsoleResizer>, 81 endpoint: Endpoint, 82 input_queue_evt: EventFd, 83 output_queue_evt: EventFd, 84 input_evt: EventFd, 85 config_evt: EventFd, 86 resize_pipe: Option<File>, 87 kill_evt: EventFd, 88 pause_evt: EventFd, 89 access_platform: Option<Arc<dyn AccessPlatform>>, 90 } 91 92 pub enum Endpoint { 93 File(File), 94 FilePair(File, File), 95 Null, 96 } 97 98 impl Endpoint { 99 fn out_file(&self) -> Option<&File> { 100 match self { 101 Self::File(f) => Some(f), 102 Self::FilePair(f, _) => Some(f), 103 Self::Null => None, 104 } 105 } 106 107 fn in_file(&self) -> Option<&File> { 108 match self { 109 Self::File(_) => None, 110 Self::FilePair(_, f) => Some(f), 111 Self::Null => None, 112 } 113 } 114 } 115 116 impl Clone for Endpoint { 117 fn clone(&self) -> Self { 118 match self { 119 Self::File(f) => Self::File(f.try_clone().unwrap()), 120 Self::FilePair(f_out, f_in) => { 121 Self::FilePair(f_out.try_clone().unwrap(), f_in.try_clone().unwrap()) 122 } 123 Self::Null => Self::Null, 124 } 125 } 126 } 127 128 impl ConsoleEpollHandler { 129 /* 130 * Each port of virtio console device has one receive 131 * queue. One or more empty buffers are placed by the 132 * driver in the receive queue for incoming data. Here, 133 * we place the input data to these empty buffers. 134 */ 135 fn process_input_queue(&mut self) -> bool { 136 let mut in_buffer = self.in_buffer.lock().unwrap(); 137 let recv_queue = &mut self.queues[0]; //receiveq 138 let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize]; 139 let mut used_count = 0; 140 141 if in_buffer.is_empty() { 142 return false; 143 } 144 145 let mut avail_iter = recv_queue.iter().unwrap(); 146 for mut desc_chain in &mut avail_iter { 147 let desc = desc_chain.next().unwrap(); 148 let len = cmp::min(desc.len() as u32, in_buffer.len() as u32); 149 let source_slice = in_buffer.drain(..len as usize).collect::<Vec<u8>>(); 150 151 if let Err(e) = desc_chain.memory().write_slice( 152 &source_slice[..], 153 desc.addr() 154 .translate_gva(self.access_platform.as_ref(), desc.len() as usize), 155 ) { 156 error!("Failed to write slice: {:?}", e); 157 avail_iter.go_to_previous_position(); 158 break; 159 } 160 161 used_desc_heads[used_count] = (desc_chain.head_index(), len); 162 used_count += 1; 163 164 if in_buffer.is_empty() { 165 break; 166 } 167 } 168 169 for &(desc_index, len) in &used_desc_heads[..used_count] { 170 recv_queue.add_used(desc_index, len).unwrap(); 171 } 172 173 used_count > 0 174 } 175 176 /* 177 * Each port of virtio console device has one transmit 178 * queue. For outgoing data, characters are placed in 179 * the transmit queue by the driver. Therefore, here 180 * we read data from the transmit queue and flush them 181 * to the referenced address. 182 */ 183 fn process_output_queue(&mut self) -> bool { 184 let trans_queue = &mut self.queues[1]; //transmitq 185 let mut used_desc_heads = [(0, 0); QUEUE_SIZE as usize]; 186 let mut used_count = 0; 187 188 for mut desc_chain in trans_queue.iter().unwrap() { 189 let desc = desc_chain.next().unwrap(); 190 if let Some(ref mut out) = self.endpoint.out_file() { 191 let _ = desc_chain.memory().write_to( 192 desc.addr() 193 .translate_gva(self.access_platform.as_ref(), desc.len() as usize), 194 out, 195 desc.len() as usize, 196 ); 197 let _ = out.flush(); 198 } 199 used_desc_heads[used_count] = (desc_chain.head_index(), desc.len()); 200 used_count += 1; 201 } 202 203 for &(desc_index, len) in &used_desc_heads[..used_count] { 204 trans_queue.add_used(desc_index, len).unwrap(); 205 } 206 used_count > 0 207 } 208 209 fn signal_used_queue(&self, queue_index: u16) -> result::Result<(), DeviceError> { 210 self.interrupt_cb 211 .trigger(VirtioInterruptType::Queue(queue_index)) 212 .map_err(|e| { 213 error!("Failed to signal used queue: {:?}", e); 214 DeviceError::FailedSignalingUsedQueue(e) 215 }) 216 } 217 218 fn run( 219 &mut self, 220 paused: Arc<AtomicBool>, 221 paused_sync: Arc<Barrier>, 222 ) -> result::Result<(), EpollHelperError> { 223 let mut helper = EpollHelper::new(&self.kill_evt, &self.pause_evt)?; 224 helper.add_event(self.input_queue_evt.as_raw_fd(), INPUT_QUEUE_EVENT)?; 225 helper.add_event(self.output_queue_evt.as_raw_fd(), OUTPUT_QUEUE_EVENT)?; 226 helper.add_event(self.input_evt.as_raw_fd(), INPUT_EVENT)?; 227 helper.add_event(self.config_evt.as_raw_fd(), CONFIG_EVENT)?; 228 if let Some(resize_pipe) = self.resize_pipe.as_ref() { 229 helper.add_event(resize_pipe.as_raw_fd(), RESIZE_EVENT)?; 230 } 231 if let Some(in_file) = self.endpoint.in_file() { 232 helper.add_event(in_file.as_raw_fd(), FILE_EVENT)?; 233 } 234 helper.run(paused, paused_sync, self)?; 235 236 Ok(()) 237 } 238 } 239 240 impl EpollHelperHandler for ConsoleEpollHandler { 241 fn handle_event(&mut self, _helper: &mut EpollHelper, event: &epoll::Event) -> bool { 242 let ev_type = event.data as u16; 243 match ev_type { 244 INPUT_QUEUE_EVENT => { 245 if let Err(e) = self.input_queue_evt.read() { 246 error!("Failed to get queue event: {:?}", e); 247 return true; 248 } else if self.process_input_queue() { 249 if let Err(e) = self.signal_used_queue(0) { 250 error!("Failed to signal used queue: {:?}", e); 251 return true; 252 } 253 } 254 } 255 OUTPUT_QUEUE_EVENT => { 256 if let Err(e) = self.output_queue_evt.read() { 257 error!("Failed to get queue event: {:?}", e); 258 return true; 259 } else if self.process_output_queue() { 260 if let Err(e) = self.signal_used_queue(1) { 261 error!("Failed to signal used queue: {:?}", e); 262 return true; 263 } 264 } 265 } 266 INPUT_EVENT => { 267 if let Err(e) = self.input_evt.read() { 268 error!("Failed to get input event: {:?}", e); 269 return true; 270 } else if self.process_input_queue() { 271 if let Err(e) = self.signal_used_queue(0) { 272 error!("Failed to signal used queue: {:?}", e); 273 return true; 274 } 275 } 276 } 277 CONFIG_EVENT => { 278 if let Err(e) = self.config_evt.read() { 279 error!("Failed to get config event: {:?}", e); 280 return true; 281 } else if let Err(e) = self.interrupt_cb.trigger(VirtioInterruptType::Config) { 282 error!("Failed to signal console driver: {:?}", e); 283 return true; 284 } 285 } 286 RESIZE_EVENT => { 287 if let Err(e) = self.resize_pipe.as_ref().unwrap().read_exact(&mut [0]) { 288 error!("Failed to get resize event: {:?}", e); 289 return true; 290 } 291 292 self.resizer.update_console_size(); 293 } 294 FILE_EVENT => { 295 let mut input = [0u8; 64]; 296 if let Some(ref mut in_file) = self.endpoint.in_file() { 297 if let Ok(count) = in_file.read(&mut input) { 298 let mut in_buffer = self.in_buffer.lock().unwrap(); 299 in_buffer.extend(&input[..count]); 300 } 301 302 if self.process_input_queue() { 303 if let Err(e) = self.signal_used_queue(0) { 304 error!("Failed to signal used queue: {:?}", e); 305 return true; 306 } 307 } 308 } 309 } 310 _ => { 311 error!("Unknown event for virtio-console"); 312 return true; 313 } 314 } 315 false 316 } 317 } 318 319 /// Resize handler 320 pub struct ConsoleResizer { 321 config_evt: EventFd, 322 tty: Option<File>, 323 config: Arc<Mutex<VirtioConsoleConfig>>, 324 acked_features: AtomicU64, 325 } 326 327 impl ConsoleResizer { 328 pub fn update_console_size(&self) { 329 if let Some(tty) = self.tty.as_ref() { 330 let (cols, rows) = get_win_size(tty); 331 self.config.lock().unwrap().update_console_size(cols, rows); 332 if self 333 .acked_features 334 .fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel) 335 != 0 336 { 337 // Send the interrupt to the driver 338 let _ = self.config_evt.write(1); 339 } 340 } 341 } 342 } 343 344 impl VirtioConsoleConfig { 345 pub fn update_console_size(&mut self, cols: u16, rows: u16) { 346 self.cols = cols; 347 self.rows = rows; 348 } 349 } 350 351 /// Virtio device for exposing console to the guest OS through virtio. 352 pub struct Console { 353 common: VirtioCommon, 354 id: String, 355 config: Arc<Mutex<VirtioConsoleConfig>>, 356 resizer: Arc<ConsoleResizer>, 357 resize_pipe: Option<File>, 358 endpoint: Endpoint, 359 seccomp_action: SeccompAction, 360 in_buffer: Arc<Mutex<VecDeque<u8>>>, 361 exit_evt: EventFd, 362 } 363 364 #[derive(Versionize)] 365 pub struct ConsoleState { 366 avail_features: u64, 367 acked_features: u64, 368 config: VirtioConsoleConfig, 369 in_buffer: Vec<u8>, 370 } 371 372 fn get_win_size(tty: &dyn AsRawFd) -> (u16, u16) { 373 #[repr(C)] 374 #[derive(Default)] 375 struct WindowSize { 376 rows: u16, 377 cols: u16, 378 xpixel: u16, 379 ypixel: u16, 380 } 381 let ws: WindowSize = WindowSize::default(); 382 383 unsafe { 384 libc::ioctl(tty.as_raw_fd(), TIOCGWINSZ, &ws); 385 } 386 387 (ws.cols, ws.rows) 388 } 389 390 impl VersionMapped for ConsoleState {} 391 392 impl Console { 393 /// Create a new virtio console device that gets random data from /dev/urandom. 394 pub fn new( 395 id: String, 396 endpoint: Endpoint, 397 resize_pipe: Option<File>, 398 iommu: bool, 399 seccomp_action: SeccompAction, 400 exit_evt: EventFd, 401 ) -> io::Result<(Console, Arc<ConsoleResizer>)> { 402 let mut avail_features = 1u64 << VIRTIO_F_VERSION_1 | 1u64 << VIRTIO_CONSOLE_F_SIZE; 403 404 if iommu { 405 avail_features |= 1u64 << VIRTIO_F_IOMMU_PLATFORM; 406 } 407 408 let config_evt = EventFd::new(EFD_NONBLOCK).unwrap(); 409 let console_config = Arc::new(Mutex::new(VirtioConsoleConfig::default())); 410 let resizer = Arc::new(ConsoleResizer { 411 config_evt, 412 config: console_config.clone(), 413 tty: endpoint.out_file().as_ref().map(|t| t.try_clone().unwrap()), 414 acked_features: AtomicU64::new(0), 415 }); 416 417 resizer.update_console_size(); 418 419 Ok(( 420 Console { 421 common: VirtioCommon { 422 device_type: VirtioDeviceType::Console as u32, 423 queue_sizes: QUEUE_SIZES.to_vec(), 424 avail_features, 425 paused_sync: Some(Arc::new(Barrier::new(2))), 426 min_queues: NUM_QUEUES as u16, 427 ..Default::default() 428 }, 429 id, 430 config: console_config, 431 resizer: resizer.clone(), 432 resize_pipe, 433 endpoint, 434 seccomp_action, 435 in_buffer: Arc::new(Mutex::new(VecDeque::new())), 436 exit_evt, 437 }, 438 resizer, 439 )) 440 } 441 442 fn state(&self) -> ConsoleState { 443 ConsoleState { 444 avail_features: self.common.avail_features, 445 acked_features: self.common.acked_features, 446 config: *(self.config.lock().unwrap()), 447 in_buffer: self.in_buffer.lock().unwrap().clone().into(), 448 } 449 } 450 451 fn set_state(&mut self, state: &ConsoleState) { 452 self.common.avail_features = state.avail_features; 453 self.common.acked_features = state.acked_features; 454 *(self.config.lock().unwrap()) = state.config; 455 *(self.in_buffer.lock().unwrap()) = state.in_buffer.clone().into(); 456 } 457 } 458 459 impl Drop for Console { 460 fn drop(&mut self) { 461 if let Some(kill_evt) = self.common.kill_evt.take() { 462 // Ignore the result because there is nothing we can do about it. 463 let _ = kill_evt.write(1); 464 } 465 } 466 } 467 468 impl VirtioDevice for Console { 469 fn device_type(&self) -> u32 { 470 self.common.device_type 471 } 472 473 fn queue_max_sizes(&self) -> &[u16] { 474 &self.common.queue_sizes 475 } 476 477 fn features(&self) -> u64 { 478 self.common.avail_features 479 } 480 481 fn ack_features(&mut self, value: u64) { 482 self.common.ack_features(value) 483 } 484 485 fn read_config(&self, offset: u64, data: &mut [u8]) { 486 self.read_config_from_slice(self.config.lock().unwrap().as_slice(), offset, data); 487 } 488 489 fn activate( 490 &mut self, 491 _mem: GuestMemoryAtomic<GuestMemoryMmap>, 492 interrupt_cb: Arc<dyn VirtioInterrupt>, 493 queues: Vec<Queue<GuestMemoryAtomic<GuestMemoryMmap>>>, 494 mut queue_evts: Vec<EventFd>, 495 ) -> ActivateResult { 496 self.common.activate(&queues, &queue_evts, &interrupt_cb)?; 497 self.resizer 498 .acked_features 499 .store(self.common.acked_features, Ordering::Relaxed); 500 501 if self.common.feature_acked(VIRTIO_CONSOLE_F_SIZE) { 502 if let Err(e) = interrupt_cb.trigger(VirtioInterruptType::Config) { 503 error!("Failed to signal console driver: {:?}", e); 504 } 505 } 506 507 let (kill_evt, pause_evt) = self.common.dup_eventfds(); 508 let input_evt = EventFd::new(EFD_NONBLOCK).unwrap(); 509 510 let mut handler = ConsoleEpollHandler { 511 queues, 512 interrupt_cb, 513 in_buffer: self.in_buffer.clone(), 514 endpoint: self.endpoint.clone(), 515 input_queue_evt: queue_evts.remove(0), 516 output_queue_evt: queue_evts.remove(0), 517 input_evt, 518 config_evt: self.resizer.config_evt.try_clone().unwrap(), 519 resize_pipe: self.resize_pipe.as_ref().map(|p| p.try_clone().unwrap()), 520 resizer: Arc::clone(&self.resizer), 521 kill_evt, 522 pause_evt, 523 access_platform: self.common.access_platform.clone(), 524 }; 525 526 let paused = self.common.paused.clone(); 527 let paused_sync = self.common.paused_sync.clone(); 528 let mut epoll_threads = Vec::new(); 529 530 spawn_virtio_thread( 531 &self.id, 532 &self.seccomp_action, 533 Thread::VirtioConsole, 534 &mut epoll_threads, 535 &self.exit_evt, 536 move || { 537 if let Err(e) = handler.run(paused, paused_sync.unwrap()) { 538 error!("Error running worker: {:?}", e); 539 } 540 }, 541 )?; 542 543 self.common.epoll_threads = Some(epoll_threads); 544 545 event!("virtio-device", "activated", "id", &self.id); 546 Ok(()) 547 } 548 549 fn reset(&mut self) -> Option<Arc<dyn VirtioInterrupt>> { 550 let result = self.common.reset(); 551 event!("virtio-device", "reset", "id", &self.id); 552 result 553 } 554 555 fn set_access_platform(&mut self, access_platform: Arc<dyn AccessPlatform>) { 556 self.common.set_access_platform(access_platform) 557 } 558 } 559 560 impl Pausable for Console { 561 fn pause(&mut self) -> result::Result<(), MigratableError> { 562 self.common.pause() 563 } 564 565 fn resume(&mut self) -> result::Result<(), MigratableError> { 566 self.common.resume() 567 } 568 } 569 570 impl Snapshottable for Console { 571 fn id(&self) -> String { 572 self.id.clone() 573 } 574 575 fn snapshot(&mut self) -> std::result::Result<Snapshot, MigratableError> { 576 Snapshot::new_from_versioned_state(&self.id, &self.state()) 577 } 578 579 fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> { 580 self.set_state(&snapshot.to_versioned_state(&self.id)?); 581 Ok(()) 582 } 583 } 584 impl Transportable for Console {} 585 impl Migratable for Console {} 586