| // |
| // Copyright 2019, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| // |
| // Alternatively, this software may be distributed under the terms of the |
| // GNU General Public License ("GPL") version 2 as published by the Free |
| // Software Foundation. |
| // |
| |
| extern crate sys_info; |
| |
| use super::mosys; |
| use super::flashrom; |
| use super::flashrom::{Flashrom}; |
| use super::cmd; |
| use super::types; |
| use super::utils; |
| use super::rand; |
| use super::tester; |
| |
| use std::io::{Error, ErrorKind}; |
| |
| pub fn generic(path: &str, fc: types::FlashChip) -> Result<(), std::io::Error> { |
| let p = path.to_string(); |
| let cmd = cmd::FlashromCmd{path: p, fc}; |
| |
| let chip_name = flashrom::name(&cmd)?; |
| info!("Found chip name: {}.", chip_name); |
| |
| info!("Calculate ROM partition sizes & Create the layout file."); |
| let rom_sz: i64 = cmd.get_size()?; |
| let layout_sizes = utils::get_layout_sizes(rom_sz)?; |
| utils::construct_layout_file("/tmp/layout.file", &layout_sizes)?; |
| |
| info!("Create a Binary with random contents."); |
| rand::gen_rand_testdata("/tmp/random_content.bin", rom_sz as usize)?; |
| |
| // run specialization tests: |
| // ================================================ |
| let default_test_params = match fc { |
| types::FlashChip::EC => ec(&cmd), |
| types::FlashChip::HOST => host(&cmd), |
| types::FlashChip::SERVOv2 => servov2(&cmd), |
| types::FlashChip::DEDIPROG => dediprog(&cmd), |
| }; |
| |
| // ================================================ |
| // |
| let wp_test_fn = |param: &tester::TestParams| { |
| // TODO(quasisec): Should this be in generic() ? |
| let wpen = if param.fc != types::FlashChip::SERVOv2 && param.fc != types::FlashChip::DEDIPROG { |
| let wp = utils::gather_system_info()?; |
| let state = if wp { "EN" } else { "DIS" }; |
| info!("Hardware write protect is {}ABLED", state); |
| wp |
| } else { true }; |
| |
| // NOTE: This is not strictly a 'test' as it is allowed to fail on some platforms. |
| // However, we will warn when it does fail. |
| match flashrom::wp_list(¶m.cmd) { |
| Ok(list_str) => info!("\n{}", list_str), |
| Err(e) => warn!("{:?}", e), |
| }; |
| |
| if !wpen && flashrom::wp_status(¶m.cmd, true)? { |
| warn!("ROM is write protected. Attempting to disable.."); |
| flashrom::wp_toggle(¶m.cmd, false)?; |
| } |
| if wpen && flashrom::wp_status(¶m.cmd, true)? { |
| warn!("Hardware write protect seems to be asserted, attempt to get user to de-assert it."); |
| utils::toggle_hw_wp(true); |
| warn!("ROM is write protected. Attempting to disable.."); |
| flashrom::wp_toggle(¶m.cmd, false)?; |
| } |
| if flashrom::wp_status(¶m.cmd, true)? { |
| return Err(Error::new(ErrorKind::Other, "Cannot disable write protect. Cannot continue.")); |
| } |
| |
| info!("Successfully disable Write-protect"); |
| Ok(()) |
| }; |
| let wp_test = tester::TestCase { |
| name: "Toggle WP", |
| params: &default_test_params, |
| test_fn: wp_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // ================================================ |
| // |
| let read_test_fn = |param: &tester::TestParams| { |
| flashrom::read(¶m.cmd, "/tmp/flashrom_tester_read.dat")?; |
| flashrom::verify(¶m.cmd, "/tmp/flashrom_tester_read.dat") |
| }; |
| let read_test = tester::TestCase { |
| name: "Read", |
| params: &default_test_params, |
| test_fn: read_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // ================================================ |
| // |
| let erase_write_test_fn = |param: &tester::TestParams| { |
| flashrom::read(¶m.cmd, "/tmp/flashrom_tester_read.dat")?; |
| flashrom::verify(¶m.cmd, "/tmp/flashrom_tester_read.dat")?; |
| flashrom::wp_toggle(¶m.cmd, false)?; |
| flashrom::erase(¶m.cmd)?; |
| flashrom::write(¶m.cmd, "/tmp/flashrom_tester_read.dat")?; |
| flashrom::wp_toggle(¶m.cmd, true); |
| Ok(()) |
| }; |
| let erase_write_test = tester::TestCase { |
| name: "Erase/Write", |
| params: &default_test_params, |
| test_fn: erase_write_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // ================================================ |
| // |
| let verify_fail_test_fn = |param: &tester::TestParams| { |
| flashrom::verify(¶m.cmd, "/tmp/random_content.bin") |
| }; |
| let verify_fail_test = tester::TestCase { |
| name: "Fail to verify", |
| params: &default_test_params, |
| test_fn: verify_fail_test_fn, |
| conclusion: tester::TestConclusion::Fail, |
| }; |
| |
| // ================================================ |
| // |
| let lock_test_fn = |param: &tester::TestParams| { |
| println!("Remove battery to de-assert hardware write-protect."); |
| utils::toggle_hw_wp(true); |
| |
| // Don't assume soft write-protect state, and so toggle back on. |
| flashrom::wp_toggle(¶m.cmd, true)?; |
| |
| // TODO(quasisec): Should this be in generic() ? |
| let wpen = if param.fc != types::FlashChip::SERVOv2 && param.fc != types::FlashChip::DEDIPROG { |
| let wp = utils::gather_system_info()?; |
| let state = if wp { "EN" } else { "DIS" }; |
| info!("Hardware write protect is {}ABLED", state); |
| wp |
| } else { true }; |
| |
| if !wpen && flashrom::wp_status(¶m.cmd, true)? { |
| info!("WP should unlock since hardware WP is de-asserted. Attempting to disable.."); |
| flashrom::wp_toggle(¶m.cmd, false)?; |
| } else { |
| return Err(Error::new(ErrorKind::Other, "Hardware write protect still asserted!")); |
| } |
| |
| // Validate we successfully disabled soft write-protect when hardware write-protect was |
| // de-asserted. |
| if flashrom::wp_status(¶m.cmd, true)? { |
| return Err(Error::new(ErrorKind::Other, "Cannot disable write protect. Cannot continue.")); |
| } |
| |
| // Toggle soft write-protect back on after we are done. |
| flashrom::wp_toggle(¶m.cmd, true)?; |
| |
| // --- deal with the other case of hardware wp being asserted. // |
| |
| println!("Replace battery to assert hardware write-protect."); |
| utils::toggle_hw_wp(false); |
| |
| // TODO(quasisec): Should this be in generic() ? |
| let wpen = if param.fc != types::FlashChip::SERVOv2 && param.fc != types::FlashChip::DEDIPROG { |
| let wp = utils::gather_system_info()?; |
| let state = if wp { "EN" } else { "DIS" }; |
| info!("Hardware write protect is {}ABLED", state); |
| wp |
| } else { true }; |
| |
| if wpen && flashrom::wp_status(¶m.cmd, true)? { |
| info!("WP should stay locked since hardware WP is asserted. Attempting to disable.."); |
| if flashrom::wp_toggle(¶m.cmd, false).is_ok() { |
| return Err(Error::new(ErrorKind::Other, "Soft write-protect didn't stay locked however hardware WP was asserted.")); |
| } |
| } else { |
| return Err(Error::new(ErrorKind::Other, "Hardware write protect was not asserted!")); |
| } |
| |
| // Validate we successfully disabled soft write-protect when hardware write-protect was |
| // de-asserted. |
| if flashrom::wp_status(¶m.cmd, false)? { |
| return Err(Error::new(ErrorKind::Other, "Soft write protect wasn't enabled.")); |
| } |
| Ok(()) |
| }; |
| let lock_test = tester::TestCase { |
| name: "Lock", |
| params: &default_test_params, |
| test_fn: lock_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // ================================================ |
| // |
| let overwrite_top_quad_test_fn = |param: &tester::TestParams| { |
| let rom_sz: i64 = param.cmd.get_size()?; |
| let layout_sizes = utils::get_layout_sizes(rom_sz)?; |
| |
| let (name, _, _) = utils::layout_section(&layout_sizes, utils::LayoutNames::TopQuad); |
| |
| let rws = flashrom::ROMWriteSpecifics { |
| layout_file: Some("/tmp/layout.file"), |
| write_file: Some("/tmp/random_content.bin"), |
| name_file: Some(name), |
| }; |
| flashrom::wp_toggle(¶m.cmd, false)?; |
| flashrom::write_file_with_layout(¶m.cmd, None, &rws)?; |
| if flashrom::verify(¶m.cmd, "/tmp/flashrom_tester_read.dat").is_ok() { |
| return Err(Error::new(ErrorKind::Other, "Original flash image should not match when overwritten top_quad with random data")); |
| } |
| |
| let rws_ = flashrom::ROMWriteSpecifics { |
| layout_file: Some("/tmp/layout.file"), |
| write_file: Some("/tmp/flashrom_tester_read.dat"), |
| name_file: Some(name), |
| }; |
| flashrom::write_file_with_layout(¶m.cmd, None, &rws_)?; |
| flashrom::verify(¶m.cmd, "/tmp/flashrom_tester_read.dat") |
| }; |
| let overwrite_top_quad_test = tester::TestCase { |
| name: "Overwrite top quad", |
| params: &default_test_params, |
| test_fn: overwrite_top_quad_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // ================================================ |
| // |
| let lock_top_quad_test_fn = |param: &tester::TestParams| { |
| let rom_sz: i64 = param.cmd.get_size()?; |
| let layout_sizes = utils::get_layout_sizes(rom_sz)?; |
| |
| let topq_sec = utils::layout_section(&layout_sizes, utils::LayoutNames::TopQuad); |
| test_section(¶m.cmd, topq_sec) |
| }; |
| let lock_top_quad_test = tester::TestCase { |
| name: "Lock top quad", |
| params: &default_test_params, |
| test_fn: lock_top_quad_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // ================================================ |
| // |
| let lock_top_half_test_fn = |param: &tester::TestParams| { |
| let rom_sz: i64 = param.cmd.get_size()?; |
| let layout_sizes = utils::get_layout_sizes(rom_sz)?; |
| |
| let toph_sec = utils::layout_section(&layout_sizes, utils::LayoutNames::TopHalf); |
| test_section(¶m.cmd, toph_sec) |
| }; |
| let lock_top_half_test = tester::TestCase { |
| name: "Lock top half", |
| params: &default_test_params, |
| test_fn: lock_top_half_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // ================================================ |
| // |
| let lock_bottom_half_test_fn = |param: &tester::TestParams| { |
| let rom_sz: i64 = param.cmd.get_size()?; |
| let layout_sizes = utils::get_layout_sizes(rom_sz)?; |
| |
| let both_sec = utils::layout_section(&layout_sizes, utils::LayoutNames::BottomHalf); |
| test_section(¶m.cmd, both_sec) |
| }; |
| let lock_bottom_half_test = tester::TestCase { |
| name: "Lock bottom half", |
| params: &default_test_params, |
| test_fn: lock_bottom_half_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // ================================================ |
| // |
| let lock_bottom_quad_test_fn = |param: &tester::TestParams| { |
| let rom_sz: i64 = param.cmd.get_size()?; |
| let layout_sizes = utils::get_layout_sizes(rom_sz)?; |
| |
| let botq_sec = utils::layout_section(&layout_sizes, utils::LayoutNames::BottomQuad); |
| test_section(¶m.cmd, botq_sec) |
| }; |
| let lock_bottom_quad_test = tester::TestCase { |
| name: "Lock bottom quad", |
| params: &default_test_params, |
| test_fn: lock_bottom_quad_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // ================================================ |
| // |
| let consistent_exit_test_fn = |param: &tester::TestParams| { |
| consistent_flash_checks(¶m.cmd) |
| }; |
| let consistent_exit_test = tester::TestCase { |
| name: "Flash image consistency check at end of tests", |
| params: &default_test_params, |
| test_fn: consistent_exit_test_fn, |
| conclusion: tester::TestConclusion::Pass, |
| }; |
| |
| // run generic tests: |
| // ================================================ |
| |
| // Register tests to run: |
| let mut tests = Vec::new(); |
| tests.push( &wp_test ); |
| tests.push( &read_test ); |
| tests.push( &erase_write_test ); |
| tests.push( &verify_fail_test ); |
| tests.push( &lock_test ); |
| tests.push( &overwrite_top_quad_test ); |
| tests.push( &lock_top_quad_test ); |
| tests.push( &lock_bottom_quad_test ); |
| tests.push( &lock_bottom_half_test ); |
| tests.push( &lock_top_half_test ); |
| tests.push( &consistent_exit_test ); |
| // ------------------------. |
| // Run all the tests and collate the findings: |
| let results = tester::run_all_tests(&tests); |
| |
| let os_rel = sys_info::os_release().unwrap_or("<Unknown OS>".to_string()); |
| let system_info = mosys::system_info().unwrap_or("<Unknown System>".to_string()); |
| let bios_info = mosys::bios_info().unwrap_or("<Unknown BIOS>".to_string()); |
| let meta_data = tester::ReportMetaData{ |
| chip_name: chip_name, |
| os_release: os_rel, |
| system_info: system_info, |
| bios_info: bios_info, |
| }; |
| tester::collate_all_test_runs(results, meta_data) |
| } |
| |
| fn consistent_flash_checks(cmd: &cmd::FlashromCmd) -> Result<(), std::io::Error> { |
| if flashrom::verify(&cmd, "/tmp/flashrom_tester_read.dat").is_ok() { |
| return Ok(()); |
| } |
| warn!("flash image in an inconsistent state! Attempting to restore.."); |
| flashrom::write(&cmd, "/tmp/flashrom_tester_read.dat")?; |
| flashrom::verify(&cmd, "/tmp/flashrom_tester_read.dat") |
| } |
| |
| fn test_section(cmd: &cmd::FlashromCmd, section: (&'static str, i64, i64)) -> Result<(), std::io::Error> { |
| let (name, start, len) = section; |
| |
| debug!("test_section() :: name = '{}' ..", name); |
| debug!("-------------------------------------------"); |
| |
| consistent_flash_checks(&cmd)?; |
| |
| let rws = flashrom::ROMWriteSpecifics { |
| layout_file: Some("/tmp/layout.file"), |
| write_file: Some("/tmp/random_content.bin"), |
| name_file: Some(name), |
| }; |
| |
| utils::toggle_hw_wp(true); // disconnect battery. |
| flashrom::wp_toggle(&cmd, false)?; |
| utils::toggle_hw_wp(false); // connect battery. |
| flashrom::wp_status(&cmd, false)?; |
| |
| flashrom::wp_range(&cmd, (start, len), true)?; |
| flashrom::wp_status(&cmd, false)?; |
| |
| if flashrom::write_file_with_layout(&cmd, None, &rws).is_ok() { |
| return Err(Error::new(ErrorKind::Other, "Section should be locked, should not have been overwritable with random data")); |
| } |
| |
| if flashrom::verify(&cmd, "/tmp/flashrom_tester_read.dat").is_err() { |
| return Err(Error::new(ErrorKind::Other, "Section didn't locked, has been overwritable with random data!")); |
| } |
| Ok(()) |
| } |
| |
| // ================================================ |
| // Specialization tests: |
| // ================================================ |
| |
| fn host(cmd: &cmd::FlashromCmd) -> tester::TestParams { |
| return tester::TestParams { |
| cmd: &cmd, |
| fc: types::FlashChip::HOST, |
| log_text: None, |
| pre_fn: None, |
| post_fn: None, |
| }; |
| } |
| |
| fn ec(cmd: &cmd::FlashromCmd) -> tester::TestParams { |
| panic!("Error: Unimplemented in this version, Please use 'host' parameter."); |
| } |
| |
| fn servov2(cmd: &cmd::FlashromCmd) -> tester::TestParams { |
| let pre_fn = |_: &tester::TestParams| { |
| if cmd::dut_ctrl_toggle_wp(false).is_err() { |
| error!("failed to dispatch dut_ctrl_toggle_wp()!"); |
| } |
| }; |
| let post_fn = |_: &tester::TestParams| { |
| if cmd::dut_ctrl_toggle_wp(true).is_err() { |
| error!("failed to dispatch dut_ctrl_toggle_wp()!"); |
| } |
| }; |
| return tester::TestParams { |
| cmd: &cmd, |
| fc: types::FlashChip::SERVOv2, |
| log_text: None, |
| pre_fn: Some(pre_fn), |
| post_fn: Some(post_fn), |
| }; |
| } |
| |
| fn dediprog(cmd: &cmd::FlashromCmd) -> tester::TestParams { |
| return tester::TestParams { |
| cmd: &cmd, |
| fc: types::FlashChip::DEDIPROG, |
| log_text: None, |
| pre_fn: None, |
| post_fn: None, |
| }; |
| } |