• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::fmt;
6 use std::fmt::Display;
7 use std::fs::File;
8 use std::fs::OpenOptions;
9 use std::io;
10 use std::io::stdin;
11 use std::io::stdout;
12 use std::path::PathBuf;
13 
14 use base::error;
15 use base::open_file;
16 #[cfg(windows)]
17 use base::platform::Console as WinConsole;
18 use base::syslog;
19 use base::AsRawDescriptor;
20 use base::Event;
21 use base::FileSync;
22 use base::RawDescriptor;
23 use base::ReadNotifier;
24 use hypervisor::ProtectionType;
25 use remain::sorted;
26 use serde::Deserialize;
27 use serde::Serialize;
28 use serde_keyvalue::FromKeyValues;
29 use thiserror::Error as ThisError;
30 
31 pub use crate::sys::serial_device::SerialDevice;
32 use crate::sys::serial_device::*;
33 
34 #[sorted]
35 #[derive(ThisError, Debug)]
36 pub enum Error {
37     #[error("Unable to clone an Event: {0}")]
38     CloneEvent(base::Error),
39     #[error("Unable to clone file: {0}")]
40     FileClone(std::io::Error),
41     #[error("Unable to create file '{1}': {0}")]
42     FileCreate(std::io::Error, PathBuf),
43     #[error("Unable to open file '{1}': {0}")]
44     FileOpen(std::io::Error, PathBuf),
45     #[error("Serial device path '{0} is invalid")]
46     InvalidPath(PathBuf),
47     #[error("Invalid serial hardware: {0}")]
48     InvalidSerialHardware(String),
49     #[error("Invalid serial type: {0}")]
50     InvalidSerialType(String),
51     #[error("Serial device type file requires a path")]
52     PathRequired,
53     #[error("Failed to connect to socket: {0}")]
54     SocketConnect(std::io::Error),
55     #[error("Failed to create unbound socket: {0}")]
56     SocketCreate(std::io::Error),
57     #[error("Unable to open system type serial: {0}")]
58     SystemTypeError(std::io::Error),
59     #[error("Serial device type {0} not implemented")]
60     Unimplemented(SerialType),
61 }
62 
63 /// Trait for types that can be used as input for a serial device.
64 pub trait SerialInput: io::Read + ReadNotifier + Send {}
65 impl SerialInput for File {}
66 #[cfg(windows)]
67 impl SerialInput for WinConsole {}
68 
69 /// Enum for possible type of serial devices
70 #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
71 #[serde(rename_all = "kebab-case")]
72 pub enum SerialType {
73     File,
74     Stdout,
75     Sink,
76     Syslog,
77     #[cfg_attr(unix, serde(rename = "unix"))]
78     #[cfg_attr(windows, serde(rename = "namedpipe"))]
79     SystemSerialType,
80 }
81 
82 impl Default for SerialType {
default() -> Self83     fn default() -> Self {
84         Self::Sink
85     }
86 }
87 
88 impl Display for SerialType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result89     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
90         let s = match &self {
91             SerialType::File => "File".to_string(),
92             SerialType::Stdout => "Stdout".to_string(),
93             SerialType::Sink => "Sink".to_string(),
94             SerialType::Syslog => "Syslog".to_string(),
95             SerialType::SystemSerialType => SYSTEM_SERIAL_TYPE_NAME.to_string(),
96         };
97 
98         write!(f, "{}", s)
99     }
100 }
101 
102 /// Serial device hardware types
103 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
104 #[serde(rename_all = "kebab-case")]
105 pub enum SerialHardware {
106     Serial,        // Standard PC-style (8250/16550 compatible) UART
107     VirtioConsole, // virtio-console device
108     Debugcon,      // Bochs style debug port
109 }
110 
111 impl Default for SerialHardware {
default() -> Self112     fn default() -> Self {
113         Self::Serial
114     }
115 }
116 
117 impl Display for SerialHardware {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result118     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119         let s = match &self {
120             SerialHardware::Serial => "serial".to_string(),
121             SerialHardware::VirtioConsole => "virtio-console".to_string(),
122             SerialHardware::Debugcon => "debugcon".to_string(),
123         };
124 
125         write!(f, "{}", s)
126     }
127 }
128 
serial_parameters_default_num() -> u8129 fn serial_parameters_default_num() -> u8 {
130     1
131 }
132 
serial_parameters_default_debugcon_port() -> u16133 fn serial_parameters_default_debugcon_port() -> u16 {
134     // Default to the port OVMF expects.
135     0x402
136 }
137 
138 #[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, FromKeyValues)]
139 #[serde(deny_unknown_fields, rename_all = "kebab-case", default)]
140 pub struct SerialParameters {
141     #[serde(rename = "type")]
142     pub type_: SerialType,
143     pub hardware: SerialHardware,
144     pub path: Option<PathBuf>,
145     pub input: Option<PathBuf>,
146     #[serde(default = "serial_parameters_default_num")]
147     pub num: u8,
148     pub console: bool,
149     pub earlycon: bool,
150     pub stdin: bool,
151     #[serde(alias = "out_timestamp")]
152     pub out_timestamp: bool,
153     #[serde(
154         alias = "debugcon_port",
155         default = "serial_parameters_default_debugcon_port"
156     )]
157     pub debugcon_port: u16,
158 }
159 
160 impl SerialParameters {
161     /// Helper function to create a serial device from the defined parameters.
162     ///
163     /// # Arguments
164     /// * `evt` - event used for interrupt events
165     /// * `keep_rds` - Vector of FDs required by this device if it were sandboxed in a child
166     ///                process. `evt` will always be added to this vector by this function.
create_serial_device<T: SerialDevice>( &self, protection_type: ProtectionType, evt: &Event, keep_rds: &mut Vec<RawDescriptor>, ) -> std::result::Result<T, Error>167     pub fn create_serial_device<T: SerialDevice>(
168         &self,
169         protection_type: ProtectionType,
170         evt: &Event,
171         keep_rds: &mut Vec<RawDescriptor>,
172     ) -> std::result::Result<T, Error> {
173         let evt = evt.try_clone().map_err(Error::CloneEvent)?;
174         keep_rds.push(evt.as_raw_descriptor());
175         cros_tracing::push_descriptors!(keep_rds);
176         let input: Option<Box<dyn SerialInput>> = if let Some(input_path) = &self.input {
177             let input_path = input_path.as_path();
178 
179             let input_file = open_file(input_path, OpenOptions::new().read(true))
180                 .map_err(|e| Error::FileOpen(e.into(), input_path.into()))?;
181 
182             keep_rds.push(input_file.as_raw_descriptor());
183             Some(Box::new(input_file))
184         } else if self.stdin {
185             keep_rds.push(stdin().as_raw_descriptor());
186             Some(Box::new(ConsoleInput::new()))
187         } else {
188             None
189         };
190         let (output, sync): (
191             Option<Box<dyn io::Write + Send>>,
192             Option<Box<dyn FileSync + Send>>,
193         ) = match self.type_ {
194             SerialType::Stdout => {
195                 keep_rds.push(stdout().as_raw_descriptor());
196                 (Some(Box::new(stdout())), None)
197             }
198             SerialType::Sink => (None, None),
199             SerialType::Syslog => {
200                 syslog::push_descriptors(keep_rds);
201                 (
202                     Some(Box::new(syslog::Syslogger::new(base::syslog::Level::Info))),
203                     None,
204                 )
205             }
206             SerialType::File => match &self.path {
207                 Some(path) => {
208                     let file = open_file(path, OpenOptions::new().append(true).create(true))
209                         .map_err(|e| Error::FileCreate(e.into(), path.clone()))?;
210                     let sync = file.try_clone().map_err(Error::FileClone)?;
211 
212                     keep_rds.push(file.as_raw_descriptor());
213                     keep_rds.push(sync.as_raw_descriptor());
214 
215                     (Some(Box::new(file)), Some(Box::new(sync)))
216                 }
217                 None => return Err(Error::PathRequired),
218             },
219             SerialType::SystemSerialType => {
220                 return create_system_type_serial_device(
221                     self,
222                     protection_type,
223                     evt,
224                     input,
225                     keep_rds,
226                 );
227             }
228         };
229         Ok(T::new(
230             protection_type,
231             evt,
232             input,
233             output,
234             sync,
235             self.out_timestamp,
236             keep_rds.to_vec(),
237         ))
238     }
239 }
240 
241 #[cfg(test)]
242 mod tests {
243     use serde_keyvalue::*;
244 
245     use super::*;
246 
from_serial_arg(options: &str) -> Result<SerialParameters, ParseError>247     fn from_serial_arg(options: &str) -> Result<SerialParameters, ParseError> {
248         from_key_values(options)
249     }
250 
251     #[test]
params_from_key_values()252     fn params_from_key_values() {
253         // Defaults
254         let params = from_serial_arg("").unwrap();
255         assert_eq!(
256             params,
257             SerialParameters {
258                 type_: SerialType::Sink,
259                 hardware: SerialHardware::Serial,
260                 path: None,
261                 input: None,
262                 num: 1,
263                 console: false,
264                 earlycon: false,
265                 stdin: false,
266                 out_timestamp: false,
267                 debugcon_port: 0x402,
268             }
269         );
270 
271         // type parameter
272         let params = from_serial_arg("type=file").unwrap();
273         assert_eq!(params.type_, SerialType::File);
274         let params = from_serial_arg("type=stdout").unwrap();
275         assert_eq!(params.type_, SerialType::Stdout);
276         let params = from_serial_arg("type=sink").unwrap();
277         assert_eq!(params.type_, SerialType::Sink);
278         let params = from_serial_arg("type=syslog").unwrap();
279         assert_eq!(params.type_, SerialType::Syslog);
280         #[cfg(unix)]
281         let opt = "type=unix";
282         #[cfg(windows)]
283         let opt = "type=namedpipe";
284         let params = from_serial_arg(opt).unwrap();
285         assert_eq!(params.type_, SerialType::SystemSerialType);
286         let params = from_serial_arg("type=foobar");
287         assert!(params.is_err());
288 
289         // hardware parameter
290         let params = from_serial_arg("hardware=serial").unwrap();
291         assert_eq!(params.hardware, SerialHardware::Serial);
292         let params = from_serial_arg("hardware=virtio-console").unwrap();
293         assert_eq!(params.hardware, SerialHardware::VirtioConsole);
294         let params = from_serial_arg("hardware=debugcon").unwrap();
295         assert_eq!(params.hardware, SerialHardware::Debugcon);
296         let params = from_serial_arg("hardware=foobar");
297         assert!(params.is_err());
298 
299         // path parameter
300         let params = from_serial_arg("path=/test/path").unwrap();
301         assert_eq!(params.path, Some("/test/path".into()));
302         let params = from_serial_arg("path");
303         assert!(params.is_err());
304 
305         // input parameter
306         let params = from_serial_arg("input=/path/to/input").unwrap();
307         assert_eq!(params.input, Some("/path/to/input".into()));
308         let params = from_serial_arg("input");
309         assert!(params.is_err());
310 
311         // console parameter
312         let params = from_serial_arg("console").unwrap();
313         assert!(params.console);
314         let params = from_serial_arg("console=true").unwrap();
315         assert!(params.console);
316         let params = from_serial_arg("console=false").unwrap();
317         assert!(!params.console);
318         let params = from_serial_arg("console=foobar");
319         assert!(params.is_err());
320 
321         // earlycon parameter
322         let params = from_serial_arg("earlycon").unwrap();
323         assert!(params.earlycon);
324         let params = from_serial_arg("earlycon=true").unwrap();
325         assert!(params.earlycon);
326         let params = from_serial_arg("earlycon=false").unwrap();
327         assert!(!params.earlycon);
328         let params = from_serial_arg("earlycon=foobar");
329         assert!(params.is_err());
330 
331         // stdin parameter
332         let params = from_serial_arg("stdin").unwrap();
333         assert!(params.stdin);
334         let params = from_serial_arg("stdin=true").unwrap();
335         assert!(params.stdin);
336         let params = from_serial_arg("stdin=false").unwrap();
337         assert!(!params.stdin);
338         let params = from_serial_arg("stdin=foobar");
339         assert!(params.is_err());
340 
341         // out-timestamp parameter
342         let params = from_serial_arg("out-timestamp").unwrap();
343         assert!(params.out_timestamp);
344         let params = from_serial_arg("out-timestamp=true").unwrap();
345         assert!(params.out_timestamp);
346         let params = from_serial_arg("out-timestamp=false").unwrap();
347         assert!(!params.out_timestamp);
348         let params = from_serial_arg("out-timestamp=foobar");
349         assert!(params.is_err());
350         // backward compatibility
351         let params = from_serial_arg("out_timestamp=true").unwrap();
352         assert!(params.out_timestamp);
353 
354         // debugcon-port parameter
355         let params = from_serial_arg("debugcon-port=1026").unwrap();
356         assert_eq!(params.debugcon_port, 1026);
357         // backward compatibility
358         let params = from_serial_arg("debugcon_port=1026").unwrap();
359         assert_eq!(params.debugcon_port, 1026);
360 
361         // all together
362         let params = from_serial_arg("type=stdout,path=/some/path,hardware=virtio-console,num=5,earlycon,console,stdin,input=/some/input,out_timestamp,debugcon_port=12").unwrap();
363         assert_eq!(
364             params,
365             SerialParameters {
366                 type_: SerialType::Stdout,
367                 hardware: SerialHardware::VirtioConsole,
368                 path: Some("/some/path".into()),
369                 input: Some("/some/input".into()),
370                 num: 5,
371                 console: true,
372                 earlycon: true,
373                 stdin: true,
374                 out_timestamp: true,
375                 debugcon_port: 12,
376             }
377         );
378 
379         // invalid field
380         let params = from_serial_arg("type=stdout,foo=bar");
381         assert!(params.is_err());
382     }
383 }
384