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