1 // Copyright © 2021 Intel Corporation 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 6 use crate::config::ConsoleOutputMode; 7 use crate::device_manager::PtyPair; 8 use crate::serial_buffer::SerialBuffer; 9 #[cfg(target_arch = "aarch64")] 10 use devices::legacy::Pl011; 11 #[cfg(target_arch = "x86_64")] 12 use devices::legacy::Serial; 13 use libc::EFD_NONBLOCK; 14 use std::fs::File; 15 use std::io::Read; 16 use std::os::unix::io::{AsRawFd, FromRawFd}; 17 use std::panic::AssertUnwindSafe; 18 use std::sync::{Arc, Mutex}; 19 use std::{io, result, thread}; 20 use thiserror::Error; 21 use vmm_sys_util::eventfd::EventFd; 22 23 #[derive(Debug, Error)] 24 pub enum Error { 25 /// Cannot clone File. 26 #[error("Error cloning File: {0}")] 27 FileClone(#[source] io::Error), 28 29 /// Cannot create epoll context. 30 #[error("Error creating epoll context: {0}")] 31 Epoll(#[source] io::Error), 32 33 /// Cannot handle the VM input stream. 34 #[error("Error handling VM input: {0:?}")] 35 ReadInput(#[source] io::Error), 36 37 /// Cannot queue input to the serial device. 38 #[error("Error queuing input to the serial device: {0}")] 39 QueueInput(#[source] vmm_sys_util::errno::Error), 40 41 /// Cannot flush output on the serial buffer. 42 #[error("Error flushing serial device's output buffer: {0}")] 43 FlushOutput(#[source] io::Error), 44 45 /// Cannot make the file descriptor non-blocking. 46 #[error("Error making input file descriptor non-blocking: {0}")] 47 SetNonBlocking(#[source] io::Error), 48 49 /// Cannot create EventFd. 50 #[error("Error creating EventFd: {0}")] 51 EventFd(#[source] io::Error), 52 53 /// Cannot spawn SerialManager thread. 54 #[error("Error spawning SerialManager thread: {0}")] 55 SpawnSerialManager(#[source] io::Error), 56 } 57 pub type Result<T> = result::Result<T, Error>; 58 59 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 60 #[repr(u64)] 61 pub enum EpollDispatch { 62 File = 0, 63 Kill = 1, 64 Unknown, 65 } 66 67 impl From<u64> for EpollDispatch { 68 fn from(v: u64) -> Self { 69 use EpollDispatch::*; 70 match v { 71 0 => File, 72 1 => Kill, 73 _ => Unknown, 74 } 75 } 76 } 77 78 pub struct SerialManager { 79 #[cfg(target_arch = "x86_64")] 80 serial: Arc<Mutex<Serial>>, 81 #[cfg(target_arch = "aarch64")] 82 serial: Arc<Mutex<Pl011>>, 83 epoll_file: File, 84 in_file: File, 85 kill_evt: EventFd, 86 handle: Option<thread::JoinHandle<()>>, 87 } 88 89 impl SerialManager { 90 pub fn new( 91 #[cfg(target_arch = "x86_64")] serial: Arc<Mutex<Serial>>, 92 #[cfg(target_arch = "aarch64")] serial: Arc<Mutex<Pl011>>, 93 pty_pair: Option<Arc<Mutex<PtyPair>>>, 94 mode: ConsoleOutputMode, 95 ) -> Result<Option<Self>> { 96 let in_file = match mode { 97 ConsoleOutputMode::Pty => { 98 if let Some(pty_pair) = pty_pair { 99 pty_pair 100 .lock() 101 .unwrap() 102 .main 103 .try_clone() 104 .map_err(Error::FileClone)? 105 } else { 106 return Ok(None); 107 } 108 } 109 ConsoleOutputMode::Tty => { 110 // If running on an interactive TTY then accept input 111 if unsafe { libc::isatty(libc::STDIN_FILENO) == 1 } { 112 let stdin_clone = unsafe { File::from_raw_fd(libc::dup(libc::STDIN_FILENO)) }; 113 let ret = unsafe { 114 let mut flags = libc::fcntl(stdin_clone.as_raw_fd(), libc::F_GETFL); 115 flags |= libc::O_NONBLOCK; 116 libc::fcntl(stdin_clone.as_raw_fd(), libc::F_SETFL, flags) 117 }; 118 119 if ret < 0 { 120 return Err(Error::SetNonBlocking(std::io::Error::last_os_error())); 121 } 122 123 stdin_clone 124 } else { 125 return Ok(None); 126 } 127 } 128 _ => return Ok(None), 129 }; 130 131 let epoll_fd = epoll::create(true).map_err(Error::Epoll)?; 132 let kill_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?; 133 134 epoll::ctl( 135 epoll_fd, 136 epoll::ControlOptions::EPOLL_CTL_ADD, 137 kill_evt.as_raw_fd(), 138 epoll::Event::new(epoll::Events::EPOLLIN, EpollDispatch::Kill as u64), 139 ) 140 .map_err(Error::Epoll)?; 141 142 epoll::ctl( 143 epoll_fd, 144 epoll::ControlOptions::EPOLL_CTL_ADD, 145 in_file.as_raw_fd(), 146 epoll::Event::new(epoll::Events::EPOLLIN, EpollDispatch::File as u64), 147 ) 148 .map_err(Error::Epoll)?; 149 150 if mode == ConsoleOutputMode::Pty { 151 let writer = in_file.try_clone().map_err(Error::FileClone)?; 152 let mut buffer = SerialBuffer::new(Box::new(writer)); 153 buffer.add_out_fd(in_file.as_raw_fd()); 154 buffer.add_epoll_fd(epoll_fd); 155 serial.as_ref().lock().unwrap().set_out(Box::new(buffer)); 156 } 157 158 // Use 'File' to enforce closing on 'epoll_fd' 159 let epoll_file = unsafe { File::from_raw_fd(epoll_fd) }; 160 161 Ok(Some(SerialManager { 162 serial, 163 epoll_file, 164 in_file, 165 kill_evt, 166 handle: None, 167 })) 168 } 169 170 pub fn start_thread(&mut self, exit_evt: EventFd) -> Result<()> { 171 // Don't allow this to be run if the handle exists 172 if self.handle.is_some() { 173 warn!("Tried to start multiple SerialManager threads, ignoring"); 174 return Ok(()); 175 } 176 177 let epoll_fd = self.epoll_file.as_raw_fd(); 178 let mut in_file = self.in_file.try_clone().map_err(Error::FileClone)?; 179 let serial = self.serial.clone(); 180 181 let thread = thread::Builder::new() 182 .name("serial-manager".to_string()) 183 .spawn(move || { 184 std::panic::catch_unwind(AssertUnwindSafe(move || { 185 // 3 for File, Kill, and Unknown 186 const EPOLL_EVENTS_LEN: usize = 3; 187 188 let mut events = 189 vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; 190 191 loop { 192 let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) { 193 Ok(res) => res, 194 Err(e) => { 195 if e.kind() == io::ErrorKind::Interrupted { 196 // It's well defined from the epoll_wait() syscall 197 // documentation that the epoll loop can be interrupted 198 // before any of the requested events occurred or the 199 // timeout expired. In both those cases, epoll_wait() 200 // returns an error of type EINTR, but this should not 201 // be considered as a regular error. Instead it is more 202 // appropriate to retry, by calling into epoll_wait(). 203 continue; 204 } 205 return Err(Error::Epoll(e)); 206 } 207 }; 208 209 for event in events.iter().take(num_events) { 210 let dispatch_event: EpollDispatch = event.data.into(); 211 match dispatch_event { 212 EpollDispatch::Unknown => { 213 let event = event.data; 214 warn!("Unknown serial manager loop event: {}", event); 215 } 216 EpollDispatch::File => { 217 if event.events & libc::EPOLLOUT as u32 != 0 { 218 serial 219 .as_ref() 220 .lock() 221 .unwrap() 222 .flush_output() 223 .map_err(Error::FlushOutput)?; 224 } 225 if event.events & libc::EPOLLIN as u32 != 0 { 226 let mut input = [0u8; 64]; 227 let count = 228 in_file.read(&mut input).map_err(Error::ReadInput)?; 229 230 // Replace "\n" with "\r" to deal with Windows SAC (#1170) 231 if count == 1 && input[0] == 0x0a { 232 input[0] = 0x0d; 233 } 234 235 serial 236 .as_ref() 237 .lock() 238 .unwrap() 239 .queue_input_bytes(&input[..count]) 240 .map_err(Error::QueueInput)?; 241 } 242 } 243 EpollDispatch::Kill => { 244 info!("KILL event received, stopping epoll loop"); 245 return Ok(()); 246 } 247 } 248 } 249 } 250 })) 251 .map_err(|_| { 252 error!("serial-manager thread panicked"); 253 exit_evt.write(1).ok() 254 }) 255 .ok(); 256 }) 257 .map_err(Error::SpawnSerialManager)?; 258 self.handle = Some(thread); 259 Ok(()) 260 } 261 } 262 263 impl Drop for SerialManager { 264 fn drop(&mut self) { 265 self.kill_evt.write(1).ok(); 266 if let Some(handle) = self.handle.take() { 267 handle.join().ok(); 268 } 269 } 270 } 271