vmm/devices/legacy/
serial.rs

1// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
5// Use of this source code is governed by a BSD-style license that can be
6// found in the THIRD-PARTY file.
7
8//! Implements a wrapper over an UART serial device.
9use std::fmt::Debug;
10use std::fs::File;
11use std::io::{self, Read, Stdin, Write};
12use std::os::unix::io::{AsRawFd, RawFd};
13use std::sync::{Arc, Barrier};
14
15use event_manager::{EventOps, Events, MutEventSubscriber};
16use libc::EFD_NONBLOCK;
17use log::{error, warn};
18use serde::Serialize;
19use vm_superio::serial::{Error as SerialError, SerialEvents};
20use vm_superio::{Serial, Trigger};
21use vmm_sys_util::epoll::EventSet;
22use vmm_sys_util::eventfd::EventFd;
23
24use crate::devices::legacy::EventFdTrigger;
25use crate::logger::{IncMetric, SharedIncMetric};
26use crate::vstate::bus::BusDevice;
27
28/// Received Data Available interrupt - for letting the driver know that
29/// there is some pending data to be processed.
30pub const IER_RDA_BIT: u8 = 0b0000_0001;
31/// Received Data Available interrupt offset
32pub const IER_RDA_OFFSET: u8 = 1;
33
34/// Metrics specific to the UART device.
35#[derive(Debug, Serialize, Default)]
36pub struct SerialDeviceMetrics {
37    /// Errors triggered while using the UART device.
38    pub error_count: SharedIncMetric,
39    /// Number of flush operations.
40    pub flush_count: SharedIncMetric,
41    /// Number of read calls that did not trigger a read.
42    pub missed_read_count: SharedIncMetric,
43    /// Number of write calls that did not trigger a write.
44    pub missed_write_count: SharedIncMetric,
45    /// Number of succeeded read calls.
46    pub read_count: SharedIncMetric,
47    /// Number of succeeded write calls.
48    pub write_count: SharedIncMetric,
49}
50impl SerialDeviceMetrics {
51    /// Const default construction.
52    pub const fn new() -> Self {
53        Self {
54            error_count: SharedIncMetric::new(),
55            flush_count: SharedIncMetric::new(),
56            missed_read_count: SharedIncMetric::new(),
57            missed_write_count: SharedIncMetric::new(),
58            read_count: SharedIncMetric::new(),
59            write_count: SharedIncMetric::new(),
60        }
61    }
62}
63
64/// Stores aggregated metrics
65pub(super) static METRICS: SerialDeviceMetrics = SerialDeviceMetrics::new();
66
67#[derive(Debug, thiserror::Error, displaydoc::Display)]
68pub enum RawIOError {
69    /// Serial error: {0:?}
70    Serial(SerialError<io::Error>),
71}
72
73pub trait RawIOHandler {
74    /// Send raw input to this emulated device.
75    fn raw_input(&mut self, _data: &[u8]) -> Result<(), RawIOError>;
76}
77
78impl<EV: SerialEvents + Debug, W: Write + Debug> RawIOHandler for Serial<EventFdTrigger, EV, W> {
79    // This is not used for anything and is basically just a dummy implementation for `raw_input`.
80    fn raw_input(&mut self, data: &[u8]) -> Result<(), RawIOError> {
81        // Fail fast if the serial is serviced with more data than it can buffer.
82        if data.len() > self.fifo_capacity() {
83            return Err(RawIOError::Serial(SerialError::FullFifo));
84        }
85
86        // Before enqueuing bytes we first check if there is enough free space
87        // in the FIFO.
88        if self.fifo_capacity() >= data.len() {
89            self.enqueue_raw_bytes(data).map_err(RawIOError::Serial)?;
90        }
91        Ok(())
92    }
93}
94
95/// Wrapper over available events (i.e metrics, buffer ready etc).
96#[derive(Debug)]
97pub struct SerialEventsWrapper {
98    /// Buffer ready event.
99    pub buffer_ready_event_fd: Option<EventFdTrigger>,
100}
101
102impl SerialEvents for SerialEventsWrapper {
103    fn buffer_read(&self) {
104        METRICS.read_count.inc();
105    }
106
107    fn out_byte(&self) {
108        METRICS.write_count.inc();
109    }
110
111    fn tx_lost_byte(&self) {
112        METRICS.missed_write_count.inc();
113    }
114
115    fn in_buffer_empty(&self) {
116        match self
117            .buffer_ready_event_fd
118            .as_ref()
119            .map_or(Ok(()), |buf_ready| buf_ready.write(1))
120        {
121            Ok(_) => (),
122            Err(err) => error!(
123                "Could not signal that serial device buffer is ready: {:?}",
124                err
125            ),
126        }
127    }
128}
129
130#[derive(Debug)]
131pub enum SerialOut {
132    Sink,
133    Stdout(std::io::Stdout),
134    File(File),
135}
136impl std::io::Write for SerialOut {
137    fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
138        match self {
139            Self::Sink => Ok(buf.len()),
140            Self::Stdout(stdout) => stdout.write(buf),
141            Self::File(file) => file.write(buf),
142        }
143    }
144    fn flush(&mut self) -> std::io::Result<()> {
145        match self {
146            Self::Sink => Ok(()),
147            Self::Stdout(stdout) => stdout.flush(),
148            Self::File(file) => file.flush(),
149        }
150    }
151}
152
153/// Wrapper over the imported serial device.
154#[derive(Debug)]
155pub struct SerialWrapper<T: Trigger, EV: SerialEvents, I: Read + AsRawFd + Send> {
156    /// Serial device object.
157    pub serial: Serial<T, EV, SerialOut>,
158    /// Input to the serial device (needs to be readable).
159    pub input: Option<I>,
160}
161
162impl<I: Read + AsRawFd + Send + Debug> SerialWrapper<EventFdTrigger, SerialEventsWrapper, I> {
163    fn handle_ewouldblock(&self, ops: &mut EventOps) {
164        let buffer_ready_fd = self.buffer_ready_evt_fd();
165        let input_fd = self.serial_input_fd();
166        if input_fd < 0 || buffer_ready_fd < 0 {
167            error!("Serial does not have a configured input source.");
168            return;
169        }
170        match ops.add(Events::new(&input_fd, EventSet::IN)) {
171            Err(event_manager::Error::FdAlreadyRegistered) => (),
172            Err(err) => {
173                error!(
174                    "Could not register the serial input to the event manager: {:?}",
175                    err
176                );
177            }
178            Ok(()) => {
179                // Bytes might had come on the unregistered stdin. Try to consume any.
180                self.serial.events().in_buffer_empty()
181            }
182        };
183    }
184
185    fn recv_bytes(&mut self) -> io::Result<usize> {
186        let avail_cap = self.serial.fifo_capacity();
187        if avail_cap == 0 {
188            return Err(io::Error::from_raw_os_error(libc::ENOBUFS));
189        }
190
191        if let Some(input) = self.input.as_mut() {
192            let mut out = vec![0u8; avail_cap];
193            let count = input.read(&mut out)?;
194            if count > 0 {
195                self.serial
196                    .raw_input(&out[..count])
197                    .map_err(|_| io::Error::from_raw_os_error(libc::ENOBUFS))?;
198            }
199
200            return Ok(count);
201        }
202
203        Err(io::Error::from_raw_os_error(libc::ENOTTY))
204    }
205
206    #[inline]
207    fn buffer_ready_evt_fd(&self) -> RawFd {
208        self.serial
209            .events()
210            .buffer_ready_event_fd
211            .as_ref()
212            .map_or(-1, |buf_ready| buf_ready.as_raw_fd())
213    }
214
215    #[inline]
216    fn serial_input_fd(&self) -> RawFd {
217        self.input.as_ref().map_or(-1, |input| input.as_raw_fd())
218    }
219
220    fn consume_buffer_ready_event(&self) -> io::Result<u64> {
221        self.serial
222            .events()
223            .buffer_ready_event_fd
224            .as_ref()
225            .map_or(Ok(0), |buf_ready| buf_ready.read())
226    }
227}
228
229/// Type for representing a serial device.
230pub type SerialDevice = SerialWrapper<EventFdTrigger, SerialEventsWrapper, Stdin>;
231
232impl SerialDevice {
233    pub fn new(serial_in: Option<Stdin>, serial_out: SerialOut) -> Result<Self, std::io::Error> {
234        let interrupt_evt = EventFdTrigger::new(EventFd::new(EFD_NONBLOCK)?);
235        let buffer_read_event_fd = EventFdTrigger::new(EventFd::new(EFD_NONBLOCK)?);
236
237        let serial = Serial::with_events(
238            interrupt_evt,
239            SerialEventsWrapper {
240                buffer_ready_event_fd: Some(buffer_read_event_fd),
241            },
242            serial_out,
243        );
244
245        Ok(SerialDevice {
246            serial,
247            input: serial_in,
248        })
249    }
250}
251
252impl<I: Read + AsRawFd + Send + Debug> MutEventSubscriber
253    for SerialWrapper<EventFdTrigger, SerialEventsWrapper, I>
254{
255    /// Handle events on the serial input fd.
256    fn process(&mut self, event: Events, ops: &mut EventOps) {
257        #[inline]
258        fn unregister_source<T: AsRawFd + Debug>(ops: &mut EventOps, source: &T) {
259            match ops.remove(Events::new(source, EventSet::IN)) {
260                Ok(_) => (),
261                Err(_) => error!("Could not unregister source fd: {}", source.as_raw_fd()),
262            }
263        }
264
265        let input_fd = self.serial_input_fd();
266        let buffer_ready_fd = self.buffer_ready_evt_fd();
267        if input_fd < 0 || buffer_ready_fd < 0 {
268            error!("Serial does not have a configured input source.");
269            return;
270        }
271
272        if buffer_ready_fd == event.fd() {
273            match self.consume_buffer_ready_event() {
274                Ok(_) => (),
275                Err(err) => {
276                    error!(
277                        "Detach serial device input source due to error in consuming the buffer \
278                         ready event: {:?}",
279                        err
280                    );
281                    unregister_source(ops, &input_fd);
282                    unregister_source(ops, &buffer_ready_fd);
283                    return;
284                }
285            }
286        }
287
288        // We expect to receive: `EventSet::IN`, `EventSet::HANG_UP` or
289        // `EventSet::ERROR`. To process all these events we just have to
290        // read from the serial input.
291        match self.recv_bytes() {
292            Ok(count) => {
293                // Handle EOF if the event came from the input source.
294                if input_fd == event.fd() && count == 0 {
295                    unregister_source(ops, &input_fd);
296                    unregister_source(ops, &buffer_ready_fd);
297                    warn!("Detached the serial input due to peer close/error.");
298                }
299            }
300            Err(err) => {
301                match err.raw_os_error() {
302                    Some(errno) if errno == libc::ENOBUFS => {
303                        unregister_source(ops, &input_fd);
304                    }
305                    Some(errno) if errno == libc::EWOULDBLOCK => {
306                        self.handle_ewouldblock(ops);
307                    }
308                    Some(errno) if errno == libc::ENOTTY => {
309                        error!("The serial device does not have the input source attached.");
310                        unregister_source(ops, &input_fd);
311                        unregister_source(ops, &buffer_ready_fd);
312                    }
313                    Some(_) | None => {
314                        // Unknown error, detach the serial input source.
315                        unregister_source(ops, &input_fd);
316                        unregister_source(ops, &buffer_ready_fd);
317                        warn!("Detached the serial input due to peer close/error.");
318                    }
319                }
320            }
321        }
322    }
323
324    /// Initial registration of pollable objects.
325    /// If serial input is present, register the serial input FD as readable.
326    fn init(&mut self, ops: &mut EventOps) {
327        if self.input.is_some() && self.serial.events().buffer_ready_event_fd.is_some() {
328            let serial_fd = self.serial_input_fd();
329            let buf_ready_evt = self.buffer_ready_evt_fd();
330
331            // If the jailer is instructed to daemonize before exec-ing into firecracker, we set
332            // stdin, stdout and stderr to be open('/dev/null'). However, if stdin is redirected
333            // from /dev/null then trying to register FILENO_STDIN to epoll will fail with EPERM.
334            // Therefore, only try to register stdin to epoll if it is a terminal or a FIFO pipe.
335            // SAFETY: isatty has no invariants that need to be upheld. If serial_fd is an invalid
336            // argument, it will return 0 and set errno to EBADF.
337            if (unsafe { libc::isatty(serial_fd) } == 1 || is_fifo(serial_fd))
338                && let Err(err) = ops.add(Events::new(&serial_fd, EventSet::IN))
339            {
340                warn!("Failed to register serial input fd: {}", err);
341            }
342            if let Err(err) = ops.add(Events::new(&buf_ready_evt, EventSet::IN)) {
343                warn!("Failed to register serial buffer ready event: {}", err);
344            }
345        }
346    }
347}
348
349/// Checks whether the given file descriptor is a FIFO pipe.
350fn is_fifo(fd: RawFd) -> bool {
351    let mut stat = std::mem::MaybeUninit::<libc::stat>::uninit();
352
353    // SAFETY: No unsafety can be introduced by passing in an invalid file descriptor to fstat,
354    // it will return -1 and set errno to EBADF. The pointer passed to fstat is valid for writing
355    // a libc::stat structure.
356    if unsafe { libc::fstat(fd, stat.as_mut_ptr()) } < 0 {
357        return false;
358    }
359
360    // SAFETY: We can safely assume the libc::stat structure to be initialized, as libc::fstat
361    // returning 0 guarantees that the memory is now initialized with the requested file metadata.
362    let stat = unsafe { stat.assume_init() };
363
364    (stat.st_mode & libc::S_IFIFO) != 0
365}
366
367impl<I> BusDevice for SerialWrapper<EventFdTrigger, SerialEventsWrapper, I>
368where
369    I: Read + AsRawFd + Send,
370{
371    fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) {
372        if let (Ok(offset), 1) = (u8::try_from(offset), data.len()) {
373            data[0] = self.serial.read(offset);
374        } else {
375            METRICS.missed_read_count.inc();
376        }
377    }
378
379    fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option<Arc<Barrier>> {
380        if let (Ok(offset), 1) = (u8::try_from(offset), data.len()) {
381            if let Err(err) = self.serial.write(offset, data[0]) {
382                // Counter incremented for any handle_write() error.
383                error!("Failed the write to serial: {:?}", err);
384                METRICS.error_count.inc();
385            }
386        } else {
387            METRICS.missed_write_count.inc();
388        }
389        None
390    }
391}
392
393#[cfg(test)]
394mod tests {
395    #![allow(clippy::undocumented_unsafe_blocks)]
396
397    use vmm_sys_util::eventfd::EventFd;
398
399    use super::*;
400    use crate::logger::IncMetric;
401
402    #[test]
403    fn test_serial_bus_read() {
404        let intr_evt = EventFdTrigger::new(EventFd::new(libc::EFD_NONBLOCK).unwrap());
405
406        let metrics = &METRICS;
407
408        let mut serial = SerialDevice {
409            serial: Serial::with_events(
410                intr_evt,
411                SerialEventsWrapper {
412                    buffer_ready_event_fd: None,
413                },
414                SerialOut::Sink,
415            ),
416            input: None::<std::io::Stdin>,
417        };
418        serial.serial.raw_input(b"abc").unwrap();
419
420        let invalid_reads_before = metrics.missed_read_count.count();
421        let mut v = [0x00; 2];
422        serial.read(0x0, 0u64, &mut v);
423
424        let invalid_reads_after = metrics.missed_read_count.count();
425        assert_eq!(invalid_reads_before + 1, invalid_reads_after);
426
427        let mut v = [0x00; 1];
428        serial.read(0x0, 0u64, &mut v);
429        assert_eq!(v[0], b'a');
430
431        let invalid_reads_after_2 = metrics.missed_read_count.count();
432        // The `invalid_read_count` metric should be the same as before the one-byte reads.
433        assert_eq!(invalid_reads_after_2, invalid_reads_after);
434    }
435
436    #[test]
437    fn test_is_fifo() {
438        // invalid file descriptors arent fifos
439        let invalid = -1;
440        assert!(!is_fifo(invalid));
441
442        // Fifos are fifos
443        let mut fds: [libc::c_int; 2] = [0; 2];
444        let rc = unsafe { libc::pipe(fds.as_mut_ptr()) };
445        assert!(rc == 0);
446
447        assert!(is_fifo(fds[0]));
448        assert!(is_fifo(fds[1]));
449
450        // Files arent fifos
451        let tmp_file = vmm_sys_util::tempfile::TempFile::new().unwrap();
452        assert!(!is_fifo(tmp_file.as_file().as_raw_fd()));
453    }
454
455    #[test]
456    fn test_serial_dev_metrics() {
457        let serial_metrics: SerialDeviceMetrics = SerialDeviceMetrics::new();
458        let serial_metrics_local: String = serde_json::to_string(&serial_metrics).unwrap();
459        // the 1st serialize flushes the metrics and resets values to 0 so that
460        // we can compare the values with local metrics.
461        serde_json::to_string(&METRICS).unwrap();
462        let serial_metrics_global: String = serde_json::to_string(&METRICS).unwrap();
463        assert_eq!(serial_metrics_local, serial_metrics_global);
464        serial_metrics.read_count.inc();
465        assert_eq!(serial_metrics.read_count.count(), 1);
466    }
467}