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