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