1 //
2 // Copyright 2019, Google Inc.
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 // * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 //
31 // Alternatively, this software may be distributed under the terms of the
32 // GNU General Public License ("GPL") version 2 as published by the Free
33 // Software Foundation.
34 //
35
36 #[macro_use]
37 extern crate log;
38
39 mod logger;
40
41 use clap::{App, Arg};
42 use flashrom::{FlashChip, Flashrom, FlashromCmd, FlashromLib};
43 use flashrom_tester::{tester, tests};
44 use std::sync::atomic::AtomicBool;
45
46 pub mod built_info {
47 include!(concat!(env!("OUT_DIR"), "/built.rs"));
48 }
49
main()50 fn main() {
51 let matches = App::new("flashrom_tester")
52 .long_version(&*format!(
53 "{}-{}\n\
54 Target: {}\n\
55 Profile: {}\n\
56 Features: {:?}\n\
57 Build time: {}\n\
58 Compiler: {}",
59 built_info::PKG_VERSION,
60 option_env!("VCSID").unwrap_or("<unknown>"),
61 built_info::TARGET,
62 built_info::PROFILE,
63 built_info::FEATURES,
64 built_info::BUILT_TIME_UTC,
65 built_info::RUSTC_VERSION,
66 ))
67 .arg(
68 Arg::with_name("libflashrom")
69 .long("libflashrom")
70 .takes_value(false)
71 .help("Test the flashrom library instead of a binary"),
72 )
73 .arg(
74 Arg::with_name("flashrom_binary")
75 .long("flashrom_binary")
76 .short("b")
77 .takes_value(true)
78 .required_unless("libflashrom")
79 .conflicts_with("libflashrom")
80 .help("Path to flashrom binary to test"),
81 )
82 .arg(
83 Arg::with_name("ccd_target_type")
84 .required(true)
85 .possible_values(&["internal"]),
86 )
87 .arg(
88 Arg::with_name("print-layout")
89 .short("l")
90 .long("print-layout")
91 .help("Print the layout file's contents before running tests"),
92 )
93 .arg(
94 Arg::with_name("log_debug")
95 .short("d")
96 .long("debug")
97 .help("Write detailed logs, for debugging"),
98 )
99 .arg(
100 Arg::with_name("output-format")
101 .short("f")
102 .long("output-format")
103 .help("Set the test report format")
104 .takes_value(true)
105 .case_insensitive(true)
106 .possible_values(&["pretty", "json"])
107 .default_value("pretty"),
108 )
109 .arg(
110 Arg::with_name("test_name")
111 .multiple(true)
112 .help("Names of individual tests to run (run all if unspecified)"),
113 )
114 .get_matches();
115
116 logger::init(matches.is_present("log_debug"));
117 debug!("Args parsed and logging initialized OK");
118
119 debug!("Collecting crossystem info");
120 let crossystem =
121 flashrom_tester::utils::collect_crosssystem(&[]).expect("could not run crossystem");
122
123 let ccd_type = FlashChip::from(
124 matches
125 .value_of("ccd_target_type")
126 .expect("ccd_target_type should be required"),
127 )
128 .expect("ccd_target_type should admit only known types");
129
130 let cmd: Box<dyn Flashrom> = if matches.is_present("libflashrom") {
131 Box::new(FlashromLib::new(
132 ccd_type,
133 if matches.is_present("log_debug") {
134 flashrom::FLASHROM_MSG_DEBUG
135 } else {
136 flashrom::FLASHROM_MSG_WARN
137 },
138 ))
139 } else {
140 Box::new(FlashromCmd {
141 path: matches
142 .value_of("flashrom_binary")
143 .expect("flashrom_binary is required")
144 .to_string(),
145 fc: ccd_type,
146 })
147 };
148
149 let print_layout = matches.is_present("print-layout");
150 let output_format = matches
151 .value_of("output-format")
152 .expect("output-format should have a default value")
153 .parse::<tester::OutputFormat>()
154 .expect("output-format is not a parseable OutputFormat");
155 let test_names = matches.values_of("test_name");
156
157 if let Err(e) = tests::generic(
158 cmd.as_ref(),
159 ccd_type,
160 print_layout,
161 output_format,
162 test_names,
163 Some(handle_sigint()),
164 crossystem,
165 ) {
166 eprintln!("Failed to run tests: {:?}", e);
167 std::process::exit(1);
168 }
169 }
170
171 /// Catch exactly one SIGINT, printing a message in response and setting a flag.
172 ///
173 /// The returned value is false by default, becoming true after a SIGINT is
174 /// trapped.
175 ///
176 /// Once a signal is trapped, the default behavior is restored (terminating
177 /// the process) for future signals.
handle_sigint() -> &'static AtomicBool178 fn handle_sigint() -> &'static AtomicBool {
179 use libc::c_int;
180 use std::sync::atomic::Ordering;
181
182 unsafe {
183 let _ = libc::signal(libc::SIGINT, sigint_handler as libc::sighandler_t);
184 }
185 static TERMINATE_FLAG: AtomicBool = AtomicBool::new(false);
186
187 extern "C" fn sigint_handler(_: c_int) {
188 const STDERR_FILENO: c_int = 2;
189 static MESSAGE: &[u8] = b"
190 WARNING: terminating tests prematurely may leave Flash in an inconsistent state,
191 rendering your machine unbootable. Testing will end on completion of the current
192 test, or press ^C again to exit immediately (possibly bricking your machine).
193 ";
194
195 // Use raw write() because signal-safety is a very hard problem. Safe because this doesn't
196 // modify any memory.
197 let _ = unsafe {
198 libc::write(
199 STDERR_FILENO,
200 MESSAGE.as_ptr() as *const libc::c_void,
201 MESSAGE.len() as libc::size_t,
202 )
203 };
204 unsafe {
205 let _ = libc::signal(libc::SIGINT, libc::SIG_DFL);
206 }
207 TERMINATE_FLAG.store(true, Ordering::Release);
208 }
209
210 &TERMINATE_FLAG
211 }
212