1*0d6140beSAndroid Build Coastguard Worker //
2*0d6140beSAndroid Build Coastguard Worker // Copyright 2019, Google Inc.
3*0d6140beSAndroid Build Coastguard Worker // All rights reserved.
4*0d6140beSAndroid Build Coastguard Worker //
5*0d6140beSAndroid Build Coastguard Worker // Redistribution and use in source and binary forms, with or without
6*0d6140beSAndroid Build Coastguard Worker // modification, are permitted provided that the following conditions are
7*0d6140beSAndroid Build Coastguard Worker // met:
8*0d6140beSAndroid Build Coastguard Worker //
9*0d6140beSAndroid Build Coastguard Worker // * Redistributions of source code must retain the above copyright
10*0d6140beSAndroid Build Coastguard Worker // notice, this list of conditions and the following disclaimer.
11*0d6140beSAndroid Build Coastguard Worker // * Redistributions in binary form must reproduce the above
12*0d6140beSAndroid Build Coastguard Worker // copyright notice, this list of conditions and the following disclaimer
13*0d6140beSAndroid Build Coastguard Worker // in the documentation and/or other materials provided with the
14*0d6140beSAndroid Build Coastguard Worker // distribution.
15*0d6140beSAndroid Build Coastguard Worker // * Neither the name of Google Inc. nor the names of its
16*0d6140beSAndroid Build Coastguard Worker // contributors may be used to endorse or promote products derived from
17*0d6140beSAndroid Build Coastguard Worker // this software without specific prior written permission.
18*0d6140beSAndroid Build Coastguard Worker //
19*0d6140beSAndroid Build Coastguard Worker // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20*0d6140beSAndroid Build Coastguard Worker // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21*0d6140beSAndroid Build Coastguard Worker // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22*0d6140beSAndroid Build Coastguard Worker // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23*0d6140beSAndroid Build Coastguard Worker // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24*0d6140beSAndroid Build Coastguard Worker // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25*0d6140beSAndroid Build Coastguard Worker // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26*0d6140beSAndroid Build Coastguard Worker // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27*0d6140beSAndroid Build Coastguard Worker // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28*0d6140beSAndroid Build Coastguard Worker // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29*0d6140beSAndroid Build Coastguard Worker // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*0d6140beSAndroid Build Coastguard Worker //
31*0d6140beSAndroid Build Coastguard Worker // Alternatively, this software may be distributed under the terms of the
32*0d6140beSAndroid Build Coastguard Worker // GNU General Public License ("GPL") version 2 as published by the Free
33*0d6140beSAndroid Build Coastguard Worker // Software Foundation.
34*0d6140beSAndroid Build Coastguard Worker //
35*0d6140beSAndroid Build Coastguard Worker
36*0d6140beSAndroid Build Coastguard Worker use super::cros_sysinfo;
37*0d6140beSAndroid Build Coastguard Worker use super::tester::{self, OutputFormat, TestCase, TestEnv, TestResult};
38*0d6140beSAndroid Build Coastguard Worker use super::utils::{self, LayoutNames};
39*0d6140beSAndroid Build Coastguard Worker use flashrom::{FlashChip, Flashrom};
40*0d6140beSAndroid Build Coastguard Worker use std::collections::{HashMap, HashSet};
41*0d6140beSAndroid Build Coastguard Worker use std::convert::TryInto;
42*0d6140beSAndroid Build Coastguard Worker use std::fs::{self, File};
43*0d6140beSAndroid Build Coastguard Worker use std::io::BufRead;
44*0d6140beSAndroid Build Coastguard Worker use std::sync::atomic::AtomicBool;
45*0d6140beSAndroid Build Coastguard Worker
46*0d6140beSAndroid Build Coastguard Worker const ELOG_FILE: &str = "/tmp/elog.file";
47*0d6140beSAndroid Build Coastguard Worker const FW_MAIN_B_PATH: &str = "/tmp/FW_MAIN_B.bin";
48*0d6140beSAndroid Build Coastguard Worker
49*0d6140beSAndroid Build Coastguard Worker /// Iterate over tests, yielding only those tests with names matching filter_names.
50*0d6140beSAndroid Build Coastguard Worker ///
51*0d6140beSAndroid Build Coastguard Worker /// If filter_names is None, all tests will be run. None is distinct from Some(∅);
52*0d6140beSAndroid Build Coastguard Worker // Some(∅) runs no tests.
53*0d6140beSAndroid Build Coastguard Worker ///
54*0d6140beSAndroid Build Coastguard Worker /// Name comparisons are performed in lower-case: values in filter_names must be
55*0d6140beSAndroid Build Coastguard Worker /// converted to lowercase specifically.
56*0d6140beSAndroid Build Coastguard Worker ///
57*0d6140beSAndroid Build Coastguard Worker /// When an entry in filter_names matches a test, it is removed from that set.
58*0d6140beSAndroid Build Coastguard Worker /// This allows the caller to determine if any entries in the original set failed
59*0d6140beSAndroid Build Coastguard Worker /// to match any test, which may be user error.
filter_tests<'n, 't: 'n, T: TestCase>( tests: &'t [T], filter_names: &'n mut Option<HashSet<String>>, ) -> impl 'n + Iterator<Item = &'t T>60*0d6140beSAndroid Build Coastguard Worker fn filter_tests<'n, 't: 'n, T: TestCase>(
61*0d6140beSAndroid Build Coastguard Worker tests: &'t [T],
62*0d6140beSAndroid Build Coastguard Worker filter_names: &'n mut Option<HashSet<String>>,
63*0d6140beSAndroid Build Coastguard Worker ) -> impl 'n + Iterator<Item = &'t T> {
64*0d6140beSAndroid Build Coastguard Worker tests.iter().filter(move |test| match filter_names {
65*0d6140beSAndroid Build Coastguard Worker // Accept all tests if no names are given
66*0d6140beSAndroid Build Coastguard Worker None => true,
67*0d6140beSAndroid Build Coastguard Worker Some(ref mut filter_names) => {
68*0d6140beSAndroid Build Coastguard Worker // Pop a match to the test name from the filter set, retaining the test
69*0d6140beSAndroid Build Coastguard Worker // if there was a match.
70*0d6140beSAndroid Build Coastguard Worker filter_names.remove(&test.get_name().to_lowercase())
71*0d6140beSAndroid Build Coastguard Worker }
72*0d6140beSAndroid Build Coastguard Worker })
73*0d6140beSAndroid Build Coastguard Worker }
74*0d6140beSAndroid Build Coastguard Worker
75*0d6140beSAndroid Build Coastguard Worker /// Run tests.
76*0d6140beSAndroid Build Coastguard Worker ///
77*0d6140beSAndroid Build Coastguard Worker /// Only returns an Error if there was an internal error; test failures are Ok.
78*0d6140beSAndroid Build Coastguard Worker ///
79*0d6140beSAndroid Build Coastguard Worker /// test_names is the case-insensitive names of tests to run; if None, then all
80*0d6140beSAndroid Build Coastguard Worker /// tests are run. Provided names that don't match any known test will be logged
81*0d6140beSAndroid Build Coastguard Worker /// as a warning.
82*0d6140beSAndroid Build Coastguard Worker #[allow(clippy::or_fun_call)] // This is used for to_string here and we don't care.
generic<'a, TN: Iterator<Item = &'a str>>( cmd: &dyn Flashrom, fc: FlashChip, print_layout: bool, output_format: OutputFormat, test_names: Option<TN>, terminate_flag: Option<&AtomicBool>, crossystem: String, ) -> Result<(), Box<dyn std::error::Error>>83*0d6140beSAndroid Build Coastguard Worker pub fn generic<'a, TN: Iterator<Item = &'a str>>(
84*0d6140beSAndroid Build Coastguard Worker cmd: &dyn Flashrom,
85*0d6140beSAndroid Build Coastguard Worker fc: FlashChip,
86*0d6140beSAndroid Build Coastguard Worker print_layout: bool,
87*0d6140beSAndroid Build Coastguard Worker output_format: OutputFormat,
88*0d6140beSAndroid Build Coastguard Worker test_names: Option<TN>,
89*0d6140beSAndroid Build Coastguard Worker terminate_flag: Option<&AtomicBool>,
90*0d6140beSAndroid Build Coastguard Worker crossystem: String,
91*0d6140beSAndroid Build Coastguard Worker ) -> Result<(), Box<dyn std::error::Error>> {
92*0d6140beSAndroid Build Coastguard Worker utils::ac_power_warning();
93*0d6140beSAndroid Build Coastguard Worker
94*0d6140beSAndroid Build Coastguard Worker info!("Record crossystem information.\n{}", crossystem);
95*0d6140beSAndroid Build Coastguard Worker
96*0d6140beSAndroid Build Coastguard Worker // Register tests to run:
97*0d6140beSAndroid Build Coastguard Worker let tests: &[&dyn TestCase] = &[
98*0d6140beSAndroid Build Coastguard Worker &("Get_device_name", get_device_name_test),
99*0d6140beSAndroid Build Coastguard Worker &("Coreboot_ELOG_sanity", elog_sanity_test),
100*0d6140beSAndroid Build Coastguard Worker &("Host_is_ChromeOS", host_is_chrome_test),
101*0d6140beSAndroid Build Coastguard Worker &("WP_Region_List", wp_region_list_test),
102*0d6140beSAndroid Build Coastguard Worker &("Erase_and_Write", erase_write_test),
103*0d6140beSAndroid Build Coastguard Worker &("Fail_to_verify", verify_fail_test),
104*0d6140beSAndroid Build Coastguard Worker &("HWWP_Locks_SWWP", hwwp_locks_swwp_test),
105*0d6140beSAndroid Build Coastguard Worker &("Lock_top_quad", partial_lock_test(LayoutNames::TopQuad)),
106*0d6140beSAndroid Build Coastguard Worker &(
107*0d6140beSAndroid Build Coastguard Worker "Lock_bottom_quad",
108*0d6140beSAndroid Build Coastguard Worker partial_lock_test(LayoutNames::BottomQuad),
109*0d6140beSAndroid Build Coastguard Worker ),
110*0d6140beSAndroid Build Coastguard Worker &(
111*0d6140beSAndroid Build Coastguard Worker "Lock_bottom_half",
112*0d6140beSAndroid Build Coastguard Worker partial_lock_test(LayoutNames::BottomHalf),
113*0d6140beSAndroid Build Coastguard Worker ),
114*0d6140beSAndroid Build Coastguard Worker &("Lock_top_half", partial_lock_test(LayoutNames::TopHalf)),
115*0d6140beSAndroid Build Coastguard Worker ];
116*0d6140beSAndroid Build Coastguard Worker
117*0d6140beSAndroid Build Coastguard Worker // Limit the tests to only those requested, unless none are requested
118*0d6140beSAndroid Build Coastguard Worker // in which case all tests are included.
119*0d6140beSAndroid Build Coastguard Worker let mut filter_names: Option<HashSet<String>> =
120*0d6140beSAndroid Build Coastguard Worker test_names.map(|names| names.map(|s| s.to_lowercase()).collect());
121*0d6140beSAndroid Build Coastguard Worker let tests = filter_tests(tests, &mut filter_names);
122*0d6140beSAndroid Build Coastguard Worker
123*0d6140beSAndroid Build Coastguard Worker let chip_name = cmd
124*0d6140beSAndroid Build Coastguard Worker .name()
125*0d6140beSAndroid Build Coastguard Worker .map(|x| format!("vendor=\"{}\" name=\"{}\"", x.0, x.1))
126*0d6140beSAndroid Build Coastguard Worker .unwrap_or("<Unknown chip>".into());
127*0d6140beSAndroid Build Coastguard Worker
128*0d6140beSAndroid Build Coastguard Worker // ------------------------.
129*0d6140beSAndroid Build Coastguard Worker // Run all the tests and collate the findings:
130*0d6140beSAndroid Build Coastguard Worker let results = tester::run_all_tests(fc, cmd, tests, terminate_flag, print_layout);
131*0d6140beSAndroid Build Coastguard Worker
132*0d6140beSAndroid Build Coastguard Worker // Any leftover filtered names were specified to be run but don't exist
133*0d6140beSAndroid Build Coastguard Worker for leftover in filter_names.iter().flatten() {
134*0d6140beSAndroid Build Coastguard Worker warn!("No test matches filter name \"{}\"", leftover);
135*0d6140beSAndroid Build Coastguard Worker }
136*0d6140beSAndroid Build Coastguard Worker
137*0d6140beSAndroid Build Coastguard Worker let os_release = sys_info::os_release().unwrap_or("<Unknown OS>".to_string());
138*0d6140beSAndroid Build Coastguard Worker let cros_release = cros_sysinfo::release_description()
139*0d6140beSAndroid Build Coastguard Worker .unwrap_or("<Unknown or not a ChromeOS release>".to_string());
140*0d6140beSAndroid Build Coastguard Worker let system_info = cros_sysinfo::system_info().unwrap_or("<Unknown System>".to_string());
141*0d6140beSAndroid Build Coastguard Worker let bios_info = cros_sysinfo::bios_info().unwrap_or("<Unknown BIOS>".to_string());
142*0d6140beSAndroid Build Coastguard Worker
143*0d6140beSAndroid Build Coastguard Worker let meta_data = tester::ReportMetaData {
144*0d6140beSAndroid Build Coastguard Worker chip_name,
145*0d6140beSAndroid Build Coastguard Worker os_release,
146*0d6140beSAndroid Build Coastguard Worker cros_release,
147*0d6140beSAndroid Build Coastguard Worker system_info,
148*0d6140beSAndroid Build Coastguard Worker bios_info,
149*0d6140beSAndroid Build Coastguard Worker };
150*0d6140beSAndroid Build Coastguard Worker tester::collate_all_test_runs(&results, meta_data, output_format);
151*0d6140beSAndroid Build Coastguard Worker Ok(())
152*0d6140beSAndroid Build Coastguard Worker }
153*0d6140beSAndroid Build Coastguard Worker
154*0d6140beSAndroid Build Coastguard Worker /// Query the programmer and chip name.
155*0d6140beSAndroid Build Coastguard Worker /// Success means we got something back, which is good enough.
get_device_name_test(env: &mut TestEnv) -> TestResult156*0d6140beSAndroid Build Coastguard Worker fn get_device_name_test(env: &mut TestEnv) -> TestResult {
157*0d6140beSAndroid Build Coastguard Worker env.cmd.name()?;
158*0d6140beSAndroid Build Coastguard Worker Ok(())
159*0d6140beSAndroid Build Coastguard Worker }
160*0d6140beSAndroid Build Coastguard Worker
161*0d6140beSAndroid Build Coastguard Worker /// List the write-protectable regions of flash.
162*0d6140beSAndroid Build Coastguard Worker /// NOTE: This is not strictly a 'test' as it is allowed to fail on some platforms.
163*0d6140beSAndroid Build Coastguard Worker /// However, we will warn when it does fail.
wp_region_list_test(env: &mut TestEnv) -> TestResult164*0d6140beSAndroid Build Coastguard Worker fn wp_region_list_test(env: &mut TestEnv) -> TestResult {
165*0d6140beSAndroid Build Coastguard Worker match env.cmd.wp_list() {
166*0d6140beSAndroid Build Coastguard Worker Ok(list_str) => info!("\n{}", list_str),
167*0d6140beSAndroid Build Coastguard Worker Err(e) => warn!("{}", e),
168*0d6140beSAndroid Build Coastguard Worker };
169*0d6140beSAndroid Build Coastguard Worker Ok(())
170*0d6140beSAndroid Build Coastguard Worker }
171*0d6140beSAndroid Build Coastguard Worker
172*0d6140beSAndroid Build Coastguard Worker /// Verify that enabling hardware and software write protect prevents chip erase.
erase_write_test(env: &mut TestEnv) -> TestResult173*0d6140beSAndroid Build Coastguard Worker fn erase_write_test(env: &mut TestEnv) -> TestResult {
174*0d6140beSAndroid Build Coastguard Worker if !env.is_golden() {
175*0d6140beSAndroid Build Coastguard Worker info!("Memory has been modified; reflashing to ensure erasure can be detected");
176*0d6140beSAndroid Build Coastguard Worker env.ensure_golden()?;
177*0d6140beSAndroid Build Coastguard Worker }
178*0d6140beSAndroid Build Coastguard Worker
179*0d6140beSAndroid Build Coastguard Worker // With write protect enabled erase should fail.
180*0d6140beSAndroid Build Coastguard Worker env.wp.set_sw(true)?.set_hw(true)?;
181*0d6140beSAndroid Build Coastguard Worker if env.erase().is_ok() {
182*0d6140beSAndroid Build Coastguard Worker info!("Flashrom returned Ok but this may be incorrect; verifying");
183*0d6140beSAndroid Build Coastguard Worker if !env.is_golden() {
184*0d6140beSAndroid Build Coastguard Worker return Err("Hardware write protect asserted however can still erase!".into());
185*0d6140beSAndroid Build Coastguard Worker }
186*0d6140beSAndroid Build Coastguard Worker info!("Erase claimed to succeed but verify is Ok; assume erase failed");
187*0d6140beSAndroid Build Coastguard Worker }
188*0d6140beSAndroid Build Coastguard Worker
189*0d6140beSAndroid Build Coastguard Worker // With write protect disabled erase should succeed.
190*0d6140beSAndroid Build Coastguard Worker env.wp.set_hw(false)?.set_sw(false)?;
191*0d6140beSAndroid Build Coastguard Worker env.erase()?;
192*0d6140beSAndroid Build Coastguard Worker if env.is_golden() {
193*0d6140beSAndroid Build Coastguard Worker return Err("Successful erase didn't modify memory".into());
194*0d6140beSAndroid Build Coastguard Worker }
195*0d6140beSAndroid Build Coastguard Worker
196*0d6140beSAndroid Build Coastguard Worker Ok(())
197*0d6140beSAndroid Build Coastguard Worker }
198*0d6140beSAndroid Build Coastguard Worker
199*0d6140beSAndroid Build Coastguard Worker /// Verify that enabling hardware write protect prevents disabling software write protect.
hwwp_locks_swwp_test(env: &mut TestEnv) -> TestResult200*0d6140beSAndroid Build Coastguard Worker fn hwwp_locks_swwp_test(env: &mut TestEnv) -> TestResult {
201*0d6140beSAndroid Build Coastguard Worker if !env.wp.can_control_hw_wp() {
202*0d6140beSAndroid Build Coastguard Worker return Err("Lock test requires ability to control hardware write protect".into());
203*0d6140beSAndroid Build Coastguard Worker }
204*0d6140beSAndroid Build Coastguard Worker
205*0d6140beSAndroid Build Coastguard Worker env.wp.set_hw(false)?.set_sw(true)?;
206*0d6140beSAndroid Build Coastguard Worker // Toggling software WP off should work when hardware WP is off.
207*0d6140beSAndroid Build Coastguard Worker // Then enable software WP again for the next test.
208*0d6140beSAndroid Build Coastguard Worker env.wp.set_sw(false)?.set_sw(true)?;
209*0d6140beSAndroid Build Coastguard Worker
210*0d6140beSAndroid Build Coastguard Worker // Toggling software WP off should not work when hardware WP is on.
211*0d6140beSAndroid Build Coastguard Worker env.wp.set_hw(true)?;
212*0d6140beSAndroid Build Coastguard Worker if env.wp.set_sw(false).is_ok() {
213*0d6140beSAndroid Build Coastguard Worker return Err("Software WP was reset despite hardware WP being enabled".into());
214*0d6140beSAndroid Build Coastguard Worker }
215*0d6140beSAndroid Build Coastguard Worker Ok(())
216*0d6140beSAndroid Build Coastguard Worker }
217*0d6140beSAndroid Build Coastguard Worker
218*0d6140beSAndroid Build Coastguard Worker /// Check that the elog contains *something*, as an indication that Coreboot
219*0d6140beSAndroid Build Coastguard Worker /// is actually able to write to the Flash. This only makes sense for chips
220*0d6140beSAndroid Build Coastguard Worker /// running Coreboot, which we assume is just host.
elog_sanity_test(env: &mut TestEnv) -> TestResult221*0d6140beSAndroid Build Coastguard Worker fn elog_sanity_test(env: &mut TestEnv) -> TestResult {
222*0d6140beSAndroid Build Coastguard Worker if env.chip_type() != FlashChip::INTERNAL {
223*0d6140beSAndroid Build Coastguard Worker info!("Skipping ELOG sanity check for non-internal chip");
224*0d6140beSAndroid Build Coastguard Worker return Ok(());
225*0d6140beSAndroid Build Coastguard Worker }
226*0d6140beSAndroid Build Coastguard Worker // flash should be back in the golden state
227*0d6140beSAndroid Build Coastguard Worker env.ensure_golden()?;
228*0d6140beSAndroid Build Coastguard Worker
229*0d6140beSAndroid Build Coastguard Worker const ELOG_RW_REGION_NAME: &str = "RW_ELOG";
230*0d6140beSAndroid Build Coastguard Worker env.cmd
231*0d6140beSAndroid Build Coastguard Worker .read_region_into_file(ELOG_FILE.as_ref(), ELOG_RW_REGION_NAME)?;
232*0d6140beSAndroid Build Coastguard Worker
233*0d6140beSAndroid Build Coastguard Worker // Just checking for the magic numer
234*0d6140beSAndroid Build Coastguard Worker // TODO: improve this test to read the events
235*0d6140beSAndroid Build Coastguard Worker if fs::metadata(ELOG_FILE)?.len() < 4 {
236*0d6140beSAndroid Build Coastguard Worker return Err("ELOG contained no data".into());
237*0d6140beSAndroid Build Coastguard Worker }
238*0d6140beSAndroid Build Coastguard Worker let data = fs::read(ELOG_FILE)?;
239*0d6140beSAndroid Build Coastguard Worker if u32::from_le_bytes(data[0..4].try_into()?) != 0x474f4c45 {
240*0d6140beSAndroid Build Coastguard Worker return Err("ELOG had bad magic number".into());
241*0d6140beSAndroid Build Coastguard Worker }
242*0d6140beSAndroid Build Coastguard Worker Ok(())
243*0d6140beSAndroid Build Coastguard Worker }
244*0d6140beSAndroid Build Coastguard Worker
245*0d6140beSAndroid Build Coastguard Worker /// Check that we are running ChromiumOS.
host_is_chrome_test(_env: &mut TestEnv) -> TestResult246*0d6140beSAndroid Build Coastguard Worker fn host_is_chrome_test(_env: &mut TestEnv) -> TestResult {
247*0d6140beSAndroid Build Coastguard Worker let release_info = if let Ok(f) = File::open("/etc/os-release") {
248*0d6140beSAndroid Build Coastguard Worker let buf = std::io::BufReader::new(f);
249*0d6140beSAndroid Build Coastguard Worker parse_os_release(buf.lines().flatten())
250*0d6140beSAndroid Build Coastguard Worker } else {
251*0d6140beSAndroid Build Coastguard Worker info!("Unable to read /etc/os-release to probe system information");
252*0d6140beSAndroid Build Coastguard Worker HashMap::new()
253*0d6140beSAndroid Build Coastguard Worker };
254*0d6140beSAndroid Build Coastguard Worker
255*0d6140beSAndroid Build Coastguard Worker match release_info.get("ID") {
256*0d6140beSAndroid Build Coastguard Worker Some(id) if id == "chromeos" || id == "chromiumos" => Ok(()),
257*0d6140beSAndroid Build Coastguard Worker oid => {
258*0d6140beSAndroid Build Coastguard Worker let id = match oid {
259*0d6140beSAndroid Build Coastguard Worker Some(s) => s,
260*0d6140beSAndroid Build Coastguard Worker None => "UNKNOWN",
261*0d6140beSAndroid Build Coastguard Worker };
262*0d6140beSAndroid Build Coastguard Worker Err(format!(
263*0d6140beSAndroid Build Coastguard Worker "Test host os-release \"{}\" should be but is not chromeos",
264*0d6140beSAndroid Build Coastguard Worker id
265*0d6140beSAndroid Build Coastguard Worker )
266*0d6140beSAndroid Build Coastguard Worker .into())
267*0d6140beSAndroid Build Coastguard Worker }
268*0d6140beSAndroid Build Coastguard Worker }
269*0d6140beSAndroid Build Coastguard Worker }
270*0d6140beSAndroid Build Coastguard Worker
271*0d6140beSAndroid Build Coastguard Worker /// Verify that software write protect for a range protects only the requested range.
partial_lock_test(section: LayoutNames) -> impl Fn(&mut TestEnv) -> TestResult272*0d6140beSAndroid Build Coastguard Worker fn partial_lock_test(section: LayoutNames) -> impl Fn(&mut TestEnv) -> TestResult {
273*0d6140beSAndroid Build Coastguard Worker move |env: &mut TestEnv| {
274*0d6140beSAndroid Build Coastguard Worker // Need a clean image for verification
275*0d6140beSAndroid Build Coastguard Worker env.ensure_golden()?;
276*0d6140beSAndroid Build Coastguard Worker
277*0d6140beSAndroid Build Coastguard Worker let (wp_section_name, start, len) = utils::layout_section(env.layout(), section);
278*0d6140beSAndroid Build Coastguard Worker // Disable hardware WP so we can modify the protected range.
279*0d6140beSAndroid Build Coastguard Worker env.wp.set_hw(false)?;
280*0d6140beSAndroid Build Coastguard Worker // Then enable software WP so the range is enforced and enable hardware
281*0d6140beSAndroid Build Coastguard Worker // WP so that flashrom does not disable software WP during the
282*0d6140beSAndroid Build Coastguard Worker // operation.
283*0d6140beSAndroid Build Coastguard Worker env.wp.set_range((start, len), true)?;
284*0d6140beSAndroid Build Coastguard Worker env.wp.set_hw(true)?;
285*0d6140beSAndroid Build Coastguard Worker
286*0d6140beSAndroid Build Coastguard Worker // Check that we cannot write to the protected region.
287*0d6140beSAndroid Build Coastguard Worker if env
288*0d6140beSAndroid Build Coastguard Worker .cmd
289*0d6140beSAndroid Build Coastguard Worker .write_from_file_region(env.random_data_file(), wp_section_name, &env.layout_file)
290*0d6140beSAndroid Build Coastguard Worker .is_ok()
291*0d6140beSAndroid Build Coastguard Worker {
292*0d6140beSAndroid Build Coastguard Worker return Err(
293*0d6140beSAndroid Build Coastguard Worker "Section should be locked, should not have been overwritable with random data"
294*0d6140beSAndroid Build Coastguard Worker .into(),
295*0d6140beSAndroid Build Coastguard Worker );
296*0d6140beSAndroid Build Coastguard Worker }
297*0d6140beSAndroid Build Coastguard Worker if !env.is_golden() {
298*0d6140beSAndroid Build Coastguard Worker return Err("Section didn't lock, has been overwritten with random data!".into());
299*0d6140beSAndroid Build Coastguard Worker }
300*0d6140beSAndroid Build Coastguard Worker
301*0d6140beSAndroid Build Coastguard Worker // Check that we can write to the non protected region.
302*0d6140beSAndroid Build Coastguard Worker let (non_wp_section_name, _, _) =
303*0d6140beSAndroid Build Coastguard Worker utils::layout_section(env.layout(), section.get_non_overlapping_section());
304*0d6140beSAndroid Build Coastguard Worker env.cmd.write_from_file_region(
305*0d6140beSAndroid Build Coastguard Worker env.random_data_file(),
306*0d6140beSAndroid Build Coastguard Worker non_wp_section_name,
307*0d6140beSAndroid Build Coastguard Worker &env.layout_file,
308*0d6140beSAndroid Build Coastguard Worker )?;
309*0d6140beSAndroid Build Coastguard Worker
310*0d6140beSAndroid Build Coastguard Worker Ok(())
311*0d6140beSAndroid Build Coastguard Worker }
312*0d6140beSAndroid Build Coastguard Worker }
313*0d6140beSAndroid Build Coastguard Worker
314*0d6140beSAndroid Build Coastguard Worker /// Check that flashrom 'verify' will fail if the provided data does not match the chip data.
verify_fail_test(env: &mut TestEnv) -> TestResult315*0d6140beSAndroid Build Coastguard Worker fn verify_fail_test(env: &mut TestEnv) -> TestResult {
316*0d6140beSAndroid Build Coastguard Worker env.ensure_golden()?;
317*0d6140beSAndroid Build Coastguard Worker // Verify that verify is Ok when the data matches. We verify only a region
318*0d6140beSAndroid Build Coastguard Worker // and not the whole chip because coprocessors or firmware may have written
319*0d6140beSAndroid Build Coastguard Worker // some data in other regions.
320*0d6140beSAndroid Build Coastguard Worker env.cmd
321*0d6140beSAndroid Build Coastguard Worker .read_region_into_file(FW_MAIN_B_PATH.as_ref(), "FW_MAIN_B")?;
322*0d6140beSAndroid Build Coastguard Worker env.cmd
323*0d6140beSAndroid Build Coastguard Worker .verify_region_from_file(FW_MAIN_B_PATH.as_ref(), "FW_MAIN_B")?;
324*0d6140beSAndroid Build Coastguard Worker
325*0d6140beSAndroid Build Coastguard Worker // Verify that verify is false when the data does not match
326*0d6140beSAndroid Build Coastguard Worker match env.verify(env.random_data_file()) {
327*0d6140beSAndroid Build Coastguard Worker Ok(_) => Err("Verification says flash is full of random data".into()),
328*0d6140beSAndroid Build Coastguard Worker Err(_) => Ok(()),
329*0d6140beSAndroid Build Coastguard Worker }
330*0d6140beSAndroid Build Coastguard Worker }
331*0d6140beSAndroid Build Coastguard Worker
332*0d6140beSAndroid Build Coastguard Worker /// Ad-hoc parsing of os-release(5); mostly according to the spec,
333*0d6140beSAndroid Build Coastguard Worker /// but ignores quotes and escaping.
parse_os_release<I: IntoIterator<Item = String>>(lines: I) -> HashMap<String, String>334*0d6140beSAndroid Build Coastguard Worker fn parse_os_release<I: IntoIterator<Item = String>>(lines: I) -> HashMap<String, String> {
335*0d6140beSAndroid Build Coastguard Worker fn parse_line(line: String) -> Option<(String, String)> {
336*0d6140beSAndroid Build Coastguard Worker if line.is_empty() || line.starts_with('#') {
337*0d6140beSAndroid Build Coastguard Worker return None;
338*0d6140beSAndroid Build Coastguard Worker }
339*0d6140beSAndroid Build Coastguard Worker
340*0d6140beSAndroid Build Coastguard Worker let delimiter = match line.find('=') {
341*0d6140beSAndroid Build Coastguard Worker Some(idx) => idx,
342*0d6140beSAndroid Build Coastguard Worker None => {
343*0d6140beSAndroid Build Coastguard Worker warn!("os-release entry seems malformed: {:?}", line);
344*0d6140beSAndroid Build Coastguard Worker return None;
345*0d6140beSAndroid Build Coastguard Worker }
346*0d6140beSAndroid Build Coastguard Worker };
347*0d6140beSAndroid Build Coastguard Worker Some((
348*0d6140beSAndroid Build Coastguard Worker line[..delimiter].to_owned(),
349*0d6140beSAndroid Build Coastguard Worker line[delimiter + 1..].to_owned(),
350*0d6140beSAndroid Build Coastguard Worker ))
351*0d6140beSAndroid Build Coastguard Worker }
352*0d6140beSAndroid Build Coastguard Worker
353*0d6140beSAndroid Build Coastguard Worker lines.into_iter().filter_map(parse_line).collect()
354*0d6140beSAndroid Build Coastguard Worker }
355*0d6140beSAndroid Build Coastguard Worker
356*0d6140beSAndroid Build Coastguard Worker #[test]
test_parse_os_release()357*0d6140beSAndroid Build Coastguard Worker fn test_parse_os_release() {
358*0d6140beSAndroid Build Coastguard Worker let lines = [
359*0d6140beSAndroid Build Coastguard Worker "BUILD_ID=12516.0.0",
360*0d6140beSAndroid Build Coastguard Worker "# this line is a comment followed by an empty line",
361*0d6140beSAndroid Build Coastguard Worker "",
362*0d6140beSAndroid Build Coastguard Worker "ID_LIKE=chromiumos",
363*0d6140beSAndroid Build Coastguard Worker "ID=chromeos",
364*0d6140beSAndroid Build Coastguard Worker "VERSION=79",
365*0d6140beSAndroid Build Coastguard Worker "EMPTY_VALUE=",
366*0d6140beSAndroid Build Coastguard Worker ];
367*0d6140beSAndroid Build Coastguard Worker let map = parse_os_release(lines.iter().map(|&s| s.to_owned()));
368*0d6140beSAndroid Build Coastguard Worker
369*0d6140beSAndroid Build Coastguard Worker fn get<'a, 'b>(m: &'a HashMap<String, String>, k: &'b str) -> Option<&'a str> {
370*0d6140beSAndroid Build Coastguard Worker m.get(k).map(|s| s.as_ref())
371*0d6140beSAndroid Build Coastguard Worker }
372*0d6140beSAndroid Build Coastguard Worker
373*0d6140beSAndroid Build Coastguard Worker assert_eq!(get(&map, "ID"), Some("chromeos"));
374*0d6140beSAndroid Build Coastguard Worker assert_eq!(get(&map, "BUILD_ID"), Some("12516.0.0"));
375*0d6140beSAndroid Build Coastguard Worker assert_eq!(get(&map, "EMPTY_VALUE"), Some(""));
376*0d6140beSAndroid Build Coastguard Worker assert_eq!(get(&map, ""), None);
377*0d6140beSAndroid Build Coastguard Worker }
378*0d6140beSAndroid Build Coastguard Worker
379*0d6140beSAndroid Build Coastguard Worker #[test]
test_name_filter()380*0d6140beSAndroid Build Coastguard Worker fn test_name_filter() {
381*0d6140beSAndroid Build Coastguard Worker let test_one = ("Test One", |_: &mut TestEnv| Ok(()));
382*0d6140beSAndroid Build Coastguard Worker let test_two = ("Test Two", |_: &mut TestEnv| Ok(()));
383*0d6140beSAndroid Build Coastguard Worker let tests: &[&dyn TestCase] = &[&test_one, &test_two];
384*0d6140beSAndroid Build Coastguard Worker
385*0d6140beSAndroid Build Coastguard Worker let mut names = None;
386*0d6140beSAndroid Build Coastguard Worker // All tests pass through
387*0d6140beSAndroid Build Coastguard Worker assert_eq!(filter_tests(tests, &mut names).count(), 2);
388*0d6140beSAndroid Build Coastguard Worker
389*0d6140beSAndroid Build Coastguard Worker names = Some(["test two"].iter().map(|s| s.to_string()).collect());
390*0d6140beSAndroid Build Coastguard Worker // Filtered out test one
391*0d6140beSAndroid Build Coastguard Worker assert_eq!(filter_tests(tests, &mut names).count(), 1);
392*0d6140beSAndroid Build Coastguard Worker
393*0d6140beSAndroid Build Coastguard Worker names = Some(["test three"].iter().map(|s| s.to_string()).collect());
394*0d6140beSAndroid Build Coastguard Worker // No tests emitted
395*0d6140beSAndroid Build Coastguard Worker assert_eq!(filter_tests(tests, &mut names).count(), 0);
396*0d6140beSAndroid Build Coastguard Worker // Name got left behind because no test matched it
397*0d6140beSAndroid Build Coastguard Worker assert_eq!(names.unwrap().len(), 1);
398*0d6140beSAndroid Build Coastguard Worker }
399