xref: /aosp_15_r20/external/flashrom/util/flashrom_tester/src/tests.rs (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
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