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