summaryrefslogtreecommitdiffstats
path: root/util/flashrom_tester/flashrom/src
diff options
context:
space:
mode:
Diffstat (limited to 'util/flashrom_tester/flashrom/src')
-rw-r--r--util/flashrom_tester/flashrom/src/cmd.rs590
-rw-r--r--util/flashrom_tester/flashrom/src/flashromlib.rs184
-rw-r--r--util/flashrom_tester/flashrom/src/lib.rs165
3 files changed, 939 insertions, 0 deletions
diff --git a/util/flashrom_tester/flashrom/src/cmd.rs b/util/flashrom_tester/flashrom/src/cmd.rs
new file mode 100644
index 000000000..1f13a8ede
--- /dev/null
+++ b/util/flashrom_tester/flashrom/src/cmd.rs
@@ -0,0 +1,590 @@
+//
+// 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.
+//
+
+use crate::{FlashChip, FlashromError};
+
+use std::{
+ ffi::{OsStr, OsString},
+ path::Path,
+ process::Command,
+};
+
+#[derive(Default)]
+pub struct FlashromOpt<'a> {
+ pub wp_opt: WPOpt,
+ pub io_opt: Option<IOOpt<'a>>,
+
+ pub flash_name: bool, // --flash-name
+ pub verbose: bool, // -V
+}
+
+#[derive(Default)]
+pub struct WPOpt {
+ pub range: Option<(i64, i64)>, // --wp-range x0 x1
+ pub status: bool, // --wp-status
+ pub list: bool, // --wp-list
+ pub enable: bool, // --wp-enable
+ pub disable: bool, // --wp-disable
+}
+
+pub enum OperationArgs<'a> {
+ /// The file is the whole chip.
+ EntireChip(&'a Path),
+ /// File is the size of the full chip, limited to a single named region.
+ ///
+ /// The required path is the file to use, and the optional path is a layout file
+ /// specifying how to locate regions (if unspecified, flashrom will attempt
+ /// to discover the layout itself).
+ FullFileRegion(&'a str, &'a Path, Option<&'a Path>),
+ /// File is the size of the single named region only.
+ ///
+ /// The required path is the file to use, and the optional path is a layout file
+ /// specifying how to locate regions (if unspecified, flashrom will attempt
+ /// to discover the layout itself).
+ RegionFileRegion(&'a str, &'a Path, Option<&'a Path>), // The file contains only the region
+}
+
+pub enum IOOpt<'a> {
+ Read(OperationArgs<'a>), // -r <file>
+ Write(OperationArgs<'a>), // -w <file>
+ Verify(OperationArgs<'a>), // -v <file>
+ Erase, // -E
+}
+
+#[derive(PartialEq, Eq, Debug)]
+pub struct FlashromCmd {
+ pub path: String,
+ pub fc: FlashChip,
+}
+
+/// Attempt to determine the Flash size given stdout from `flashrom --flash-size`
+fn flashrom_extract_size(stdout: &str) -> Result<i64, FlashromError> {
+ // Search for the last line of output that contains only digits, assuming
+ // that's the actual size. flashrom sadly tends to write additional messages
+ // to stdout.
+ match stdout
+ .lines()
+ .filter(|line| line.chars().all(|c| c.is_ascii_digit()))
+ .last()
+ .map(str::parse::<i64>)
+ {
+ None => Err("Found no purely-numeric lines in flashrom output".into()),
+ Some(Err(e)) => {
+ Err(format!("Failed to parse flashrom size output as integer: {}", e).into())
+ }
+ Some(Ok(sz)) => Ok(sz),
+ }
+}
+
+impl FlashromCmd {
+ fn dispatch(
+ &self,
+ fropt: FlashromOpt,
+ debug_name: &str,
+ ) -> Result<(String, String), FlashromError> {
+ let params = flashrom_decode_opts(fropt);
+ flashrom_dispatch(self.path.as_str(), &params, self.fc, debug_name)
+ }
+}
+
+impl crate::Flashrom for FlashromCmd {
+ fn get_size(&self) -> Result<i64, FlashromError> {
+ let (stdout, _) =
+ flashrom_dispatch(self.path.as_str(), &["--flash-size"], self.fc, "get_size")?;
+ flashrom_extract_size(&stdout)
+ }
+
+ fn name(&self) -> Result<(String, String), FlashromError> {
+ let opts = FlashromOpt {
+ flash_name: true,
+ ..Default::default()
+ };
+
+ let (stdout, _) = self.dispatch(opts, "name")?;
+ match extract_flash_name(&stdout) {
+ None => Err("Didn't find chip vendor/name in flashrom output".into()),
+ Some((vendor, name)) => Ok((vendor.into(), name.into())),
+ }
+ }
+
+ fn write_from_file_region(
+ &self,
+ path: &Path,
+ region: &str,
+ layout: &Path,
+ ) -> Result<bool, FlashromError> {
+ let opts = FlashromOpt {
+ io_opt: Some(IOOpt::Write(OperationArgs::FullFileRegion(
+ region,
+ path,
+ Some(layout),
+ ))),
+ ..Default::default()
+ };
+
+ self.dispatch(opts, "write_file_with_layout")?;
+ Ok(true)
+ }
+
+ fn wp_range(&self, range: (i64, i64), en: bool) -> Result<bool, FlashromError> {
+ let opts = FlashromOpt {
+ wp_opt: WPOpt {
+ enable: en,
+ disable: !en,
+ range: Some(range),
+ ..Default::default()
+ },
+ ..Default::default()
+ };
+
+ self.dispatch(opts, "wp_range")?;
+ Ok(true)
+ }
+
+ fn wp_list(&self) -> Result<String, FlashromError> {
+ let opts = FlashromOpt {
+ wp_opt: WPOpt {
+ list: true,
+ ..Default::default()
+ },
+ ..Default::default()
+ };
+
+ let (stdout, _) = self.dispatch(opts, "wp_list")?;
+ if stdout.is_empty() {
+ return Err(
+ "wp_list isn't supported on platforms using the Linux kernel SPI driver wp_list"
+ .into(),
+ );
+ }
+ Ok(stdout)
+ }
+
+ fn wp_status(&self, en: bool) -> Result<bool, FlashromError> {
+ let status = if en { "en" } else { "dis" };
+ info!("See if chip write protect is {}abled", status);
+
+ let opts = FlashromOpt {
+ wp_opt: WPOpt {
+ status: true,
+ ..Default::default()
+ },
+ ..Default::default()
+ };
+
+ let (stdout, _) = self.dispatch(opts, "wp_status")?;
+ let s = std::format!("write protect is {}abled", status);
+ Ok(stdout.contains(&s))
+ }
+
+ fn wp_toggle(&self, en: bool) -> Result<bool, FlashromError> {
+ let range = if en {
+ let rom_sz: i64 = self.get_size()?;
+ (0, rom_sz) // (start, len)
+ } else {
+ (0, 0)
+ };
+ self.wp_range(range, en)?;
+ let status = if en { "en" } else { "dis" };
+ match self.wp_status(true) {
+ Ok(_ret) => {
+ info!("Successfully {}abled write-protect", status);
+ Ok(true)
+ }
+ Err(e) => Err(format!("Cannot {}able write-protect: {}", status, e).into()),
+ }
+ }
+
+ fn read_into_file(&self, path: &Path) -> Result<(), FlashromError> {
+ let opts = FlashromOpt {
+ io_opt: Some(IOOpt::Read(OperationArgs::EntireChip(path))),
+ ..Default::default()
+ };
+
+ self.dispatch(opts, "read_into_file")?;
+ Ok(())
+ }
+
+ fn read_region_into_file(&self, path: &Path, region: &str) -> Result<(), FlashromError> {
+ let opts = FlashromOpt {
+ io_opt: Some(IOOpt::Read(OperationArgs::RegionFileRegion(
+ region, path, None,
+ ))),
+ ..Default::default()
+ };
+
+ self.dispatch(opts, "read_region_into_file")?;
+ Ok(())
+ }
+
+ fn write_from_file(&self, path: &Path) -> Result<(), FlashromError> {
+ let opts = FlashromOpt {
+ io_opt: Some(IOOpt::Write(OperationArgs::EntireChip(path))),
+ ..Default::default()
+ };
+
+ self.dispatch(opts, "write_from_file")?;
+ Ok(())
+ }
+
+ fn verify_from_file(&self, path: &Path) -> Result<(), FlashromError> {
+ let opts = FlashromOpt {
+ io_opt: Some(IOOpt::Verify(OperationArgs::EntireChip(path))),
+ ..Default::default()
+ };
+
+ self.dispatch(opts, "verify_from_file")?;
+ Ok(())
+ }
+
+ fn verify_region_from_file(&self, path: &Path, region: &str) -> Result<(), FlashromError> {
+ let opts = FlashromOpt {
+ io_opt: Some(IOOpt::Verify(OperationArgs::RegionFileRegion(
+ region, path, None,
+ ))),
+ ..Default::default()
+ };
+
+ self.dispatch(opts, "verify_region_from_file")?;
+ Ok(())
+ }
+
+ fn erase(&self) -> Result<(), FlashromError> {
+ let opts = FlashromOpt {
+ io_opt: Some(IOOpt::Erase),
+ ..Default::default()
+ };
+
+ self.dispatch(opts, "erase")?;
+ Ok(())
+ }
+
+ fn can_control_hw_wp(&self) -> bool {
+ self.fc.can_control_hw_wp()
+ }
+}
+
+fn flashrom_decode_opts(opts: FlashromOpt) -> Vec<OsString> {
+ let mut params = Vec::<OsString>::new();
+
+ // ------------ WARNING !!! ------------
+ // each param must NOT contain spaces!
+ // -------------------------------------
+
+ // wp_opt
+ if opts.wp_opt.range.is_some() {
+ let (x0, x1) = opts.wp_opt.range.unwrap();
+ params.push("--wp-range".into());
+ params.push(hex_range_string(x0, x1).into());
+ }
+ if opts.wp_opt.status {
+ params.push("--wp-status".into());
+ } else if opts.wp_opt.list {
+ params.push("--wp-list".into());
+ } else if opts.wp_opt.enable {
+ params.push("--wp-enable".into());
+ } else if opts.wp_opt.disable {
+ params.push("--wp-disable".into());
+ }
+
+ // io_opt
+ fn add_operation_args(opts: OperationArgs, params: &mut Vec<OsString>) {
+ let (file, region, layout) = match opts {
+ OperationArgs::EntireChip(file) => (Some(file), None, None),
+ OperationArgs::FullFileRegion(region, file, layout) => {
+ (Some(file), Some(region.to_string()), layout)
+ }
+ OperationArgs::RegionFileRegion(region, file, layout) => (
+ None,
+ Some(format!("{region}:{}", file.to_string_lossy())),
+ layout,
+ ),
+ };
+ if let Some(file) = file {
+ params.push(file.into())
+ }
+ if let Some(region) = region {
+ params.push("--include".into());
+ params.push(region.into())
+ }
+ if let Some(layout) = layout {
+ params.push("--layout".into());
+ params.push(layout.into())
+ }
+ }
+ if let Some(io) = opts.io_opt {
+ match io {
+ IOOpt::Read(args) => {
+ params.push("-r".into());
+ add_operation_args(args, &mut params);
+ }
+ IOOpt::Write(args) => {
+ params.push("-w".into());
+ add_operation_args(args, &mut params);
+ }
+ IOOpt::Verify(args) => {
+ params.push("-v".into());
+ add_operation_args(args, &mut params);
+ }
+ IOOpt::Erase => params.push("-E".into()),
+ }
+ }
+
+ // misc_opt
+ if opts.flash_name {
+ params.push("--flash-name".into());
+ }
+ if opts.verbose {
+ params.push("-V".into());
+ }
+
+ params
+}
+
+fn flashrom_dispatch<S: AsRef<OsStr>>(
+ path: &str,
+ params: &[S],
+ fc: FlashChip,
+ debug_name: &str,
+) -> Result<(String, String), FlashromError> {
+ // from man page:
+ // ' -p, --programmer <name>[:parameter[,parameter[,parameter]]] '
+ let mut args: Vec<&OsStr> = vec![OsStr::new("-p"), OsStr::new(FlashChip::to(fc))];
+ args.extend(params.iter().map(S::as_ref));
+
+ info!("flashrom_dispatch() running: {} {:?}", path, args);
+
+ let output = match Command::new(path).args(&args).output() {
+ Ok(x) => x,
+ Err(e) => return Err(format!("Failed to run flashrom: {}", e).into()),
+ };
+
+ let stdout = String::from_utf8_lossy(output.stdout.as_slice());
+ let stderr = String::from_utf8_lossy(output.stderr.as_slice());
+ debug!("{}()'stdout: {}.", debug_name, stdout);
+ debug!("{}()'stderr: {}.", debug_name, stderr);
+
+ if !output.status.success() {
+ // There is two cases on failure;
+ // i. ) A bad exit code,
+ // ii.) A SIG killed us.
+ match output.status.code() {
+ Some(code) => {
+ return Err(format!("{}\nExited with error code: {}", stderr, code).into());
+ }
+ None => return Err("Process terminated by a signal".into()),
+ }
+ }
+
+ Ok((stdout.into(), stderr.into()))
+}
+
+fn hex_range_string(s: i64, l: i64) -> String {
+ format!("{:#08X},{:#08X}", s, l)
+}
+
+/// Get a flash vendor and name from the first matching line of flashrom output.
+///
+/// The target line looks like 'vendor="foo" name="bar"', as output by flashrom --flash-name.
+/// This is usually the last line of output.
+fn extract_flash_name(stdout: &str) -> Option<(&str, &str)> {
+ for line in stdout.lines() {
+ if !line.starts_with("vendor=\"") {
+ continue;
+ }
+
+ let tail = line.trim_start_matches("vendor=\"");
+ let mut split = tail.splitn(2, "\" name=\"");
+ let vendor = split.next();
+ let name = split.next().map(|s| s.trim_end_matches('"'));
+
+ match (vendor, name) {
+ (Some(v), Some(n)) => return Some((v, n)),
+ _ => continue,
+ }
+ }
+ None
+}
+
+#[cfg(test)]
+mod tests {
+ use std::path::Path;
+
+ use super::flashrom_decode_opts;
+ use super::{FlashromOpt, IOOpt, WPOpt};
+
+ #[test]
+ fn decode_wp_opt() {
+ fn test_wp_opt(wpo: WPOpt, expected: &[&str]) {
+ assert_eq!(
+ flashrom_decode_opts(FlashromOpt {
+ wp_opt: wpo,
+ ..Default::default()
+ }),
+ expected
+ );
+ }
+
+ test_wp_opt(Default::default(), &[]);
+ test_wp_opt(
+ WPOpt {
+ range: Some((0, 1234)),
+ status: true,
+ ..Default::default()
+ },
+ &["--wp-range", "0x000000,0x0004D2", "--wp-status"],
+ );
+ test_wp_opt(
+ WPOpt {
+ list: true,
+ ..Default::default()
+ },
+ &["--wp-list"],
+ );
+ test_wp_opt(
+ WPOpt {
+ enable: true,
+ ..Default::default()
+ },
+ &["--wp-enable"],
+ );
+ test_wp_opt(
+ WPOpt {
+ disable: true,
+ ..Default::default()
+ },
+ &["--wp-disable"],
+ );
+ }
+
+ #[test]
+ fn decode_io_opt() {
+ fn test_io_opt(opts: IOOpt, expected: &[&str]) {
+ assert_eq!(
+ flashrom_decode_opts(FlashromOpt {
+ io_opt: Some(opts),
+ ..Default::default()
+ }),
+ expected
+ );
+ }
+
+ test_io_opt(
+ IOOpt::Read(crate::cmd::OperationArgs::EntireChip(Path::new("foo.bin"))),
+ &["-r", "foo.bin"],
+ );
+ test_io_opt(
+ IOOpt::Write(crate::cmd::OperationArgs::EntireChip(Path::new("bar.bin"))),
+ &["-w", "bar.bin"],
+ );
+ test_io_opt(
+ IOOpt::Verify(crate::cmd::OperationArgs::EntireChip(Path::new("baz.bin"))),
+ &["-v", "baz.bin"],
+ );
+ test_io_opt(IOOpt::Erase, &["-E"]);
+ test_io_opt(
+ IOOpt::Read(crate::cmd::OperationArgs::FullFileRegion(
+ "RO",
+ Path::new("foo.bin"),
+ Some(Path::new("baz.bin")),
+ )),
+ &["-r", "foo.bin", "--include", "RO", "--layout", "baz.bin"],
+ );
+
+ test_io_opt(
+ IOOpt::Read(crate::cmd::OperationArgs::RegionFileRegion(
+ "foo",
+ Path::new("bar.bin"),
+ None,
+ )),
+ &["-r", "--include", "foo:bar.bin"],
+ )
+ }
+
+ #[test]
+ fn decode_misc() {
+ //use Default::default;
+
+ assert_eq!(
+ flashrom_decode_opts(FlashromOpt {
+ flash_name: true,
+ verbose: true,
+ ..Default::default()
+ }),
+ &["--flash-name", "-V"]
+ );
+ }
+
+ #[test]
+ fn flashrom_extract_size() {
+ use super::flashrom_extract_size;
+
+ assert_eq!(
+ flashrom_extract_size(
+ "coreboot table found at 0x7cc13000.\n\
+ Found chipset \"Intel Braswell\". Enabling flash write... OK.\n\
+ 8388608\n"
+ ),
+ Ok(8388608)
+ );
+
+ assert_eq!(
+ flashrom_extract_size("There was a catastrophic error."),
+ Err("Found no purely-numeric lines in flashrom output".into())
+ );
+ }
+
+ #[test]
+ fn extract_flash_name() {
+ use super::extract_flash_name;
+
+ assert_eq!(
+ extract_flash_name(
+ "coreboot table found at 0x7cc13000\n\
+ Found chipset \"Intel Braswell\". Enabling flash write... OK.\n\
+ vendor=\"Winbond\" name=\"W25Q64DW\"\n"
+ ),
+ Some(("Winbond", "W25Q64DW"))
+ );
+
+ assert_eq!(
+ extract_flash_name(
+ "vendor name is TEST\n\
+ Something failed!"
+ ),
+ None
+ )
+ }
+}
diff --git a/util/flashrom_tester/flashrom/src/flashromlib.rs b/util/flashrom_tester/flashrom/src/flashromlib.rs
new file mode 100644
index 000000000..5e1747b1b
--- /dev/null
+++ b/util/flashrom_tester/flashrom/src/flashromlib.rs
@@ -0,0 +1,184 @@
+// Copyright 2022, 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.
+//
+
+use libflashrom::{Chip, Programmer};
+
+use std::{cell::RefCell, convert::TryFrom, fs, path::Path};
+
+use crate::{FlashChip, FlashromError};
+
+#[derive(Debug)]
+pub struct FlashromLib {
+ // RefCell required here to keep Flashrom trait immutable.
+ // Cant make Flashrom methods mut because WriteProtectState
+ // and TestEnv both keep a reference.
+ pub flashrom: RefCell<Chip>,
+ pub fc: FlashChip,
+}
+
+impl FlashromLib {
+ pub fn new(fc: FlashChip, log_level: libflashrom::flashrom_log_level) -> FlashromLib {
+ libflashrom::set_log_level(Some(log_level));
+ let (programmer, options) = FlashChip::to_split(fc);
+ let flashrom = Chip::new(Programmer::new(programmer, options).unwrap(), None).unwrap();
+ FlashromLib {
+ flashrom: RefCell::new(flashrom),
+ fc,
+ }
+ }
+}
+
+impl crate::Flashrom for FlashromLib {
+ fn get_size(&self) -> Result<i64, FlashromError> {
+ Ok(self.flashrom.borrow().get_size() as i64)
+ }
+
+ fn name(&self) -> Result<(String, String), FlashromError> {
+ Ok(("not".to_string(), "implemented".to_string()))
+ }
+
+ fn wp_range(&self, range: (i64, i64), wp_enable: bool) -> Result<bool, FlashromError> {
+ let mut cfg = libflashrom::WriteProtectCfg::new()?;
+ let start = usize::try_from(range.0).unwrap();
+ let len = usize::try_from(range.1).unwrap();
+ cfg.set_range::<std::ops::Range<usize>>(start..(start + len));
+ cfg.set_mode(if wp_enable {
+ libflashrom::flashrom_wp_mode::FLASHROM_WP_MODE_HARDWARE
+ } else {
+ libflashrom::flashrom_wp_mode::FLASHROM_WP_MODE_DISABLED
+ });
+ self.flashrom.borrow_mut().set_wp(&cfg)?;
+ Ok(true)
+ }
+
+ fn wp_list(&self) -> Result<String, FlashromError> {
+ let ranges = self.flashrom.borrow_mut().get_wp_ranges()?;
+ Ok(format!("{:?}", ranges))
+ }
+
+ fn wp_status(&self, en: bool) -> Result<bool, FlashromError> {
+ let ret = self
+ .flashrom
+ .borrow_mut()
+ .get_wp()
+ .map_err(|e| format!("{:?}", e))?
+ .get_mode();
+ if en {
+ Ok(ret != libflashrom::flashrom_wp_mode::FLASHROM_WP_MODE_DISABLED)
+ } else {
+ Ok(ret == libflashrom::flashrom_wp_mode::FLASHROM_WP_MODE_DISABLED)
+ }
+ }
+
+ fn wp_toggle(&self, en: bool) -> Result<bool, FlashromError> {
+ let range = if en { (0, self.get_size()?) } else { (0, 0) };
+ self.wp_range(range, en)
+ }
+
+ fn read_into_file(&self, path: &Path) -> Result<(), FlashromError> {
+ let buf = self.flashrom.borrow_mut().image_read(None)?;
+ fs::write(path, buf).map_err(|error| error.to_string())?;
+ Ok(())
+ }
+
+ fn read_region_into_file(&self, path: &Path, region: &str) -> Result<(), FlashromError> {
+ let mut layout = self.flashrom.borrow_mut().layout_read_fmap_from_rom()?;
+ layout.include_region(region)?;
+ let range = layout.get_region_range(region)?;
+ let buf = self.flashrom.borrow_mut().image_read(None)?;
+ fs::write(path, &buf[range]).map_err(|error| error.to_string())?;
+ Ok(())
+ }
+
+ fn write_from_file(&self, path: &Path) -> Result<(), FlashromError> {
+ let mut buf = fs::read(path).map_err(|error| error.to_string())?;
+ self.flashrom.borrow_mut().image_write(&mut buf, None)?;
+ Ok(())
+ }
+
+ fn write_from_file_region(
+ &self,
+ path: &Path,
+ region: &str,
+ layout: &Path,
+ ) -> Result<bool, FlashromError> {
+ let buf = fs::read(layout).map_err(|error| error.to_string())?;
+ let buf = String::from_utf8(buf).unwrap();
+ let mut layout: libflashrom::Layout = buf
+ .parse()
+ .map_err(|e: Box<dyn std::error::Error>| e.to_string())?;
+ layout.include_region(region)?;
+ let mut buf = fs::read(path).map_err(|error| error.to_string())?;
+ self.flashrom
+ .borrow_mut()
+ .image_write(&mut buf, Some(layout))?;
+ Ok(true)
+ }
+
+ fn verify_from_file(&self, path: &Path) -> Result<(), FlashromError> {
+ let buf = fs::read(path).map_err(|error| error.to_string())?;
+ self.flashrom.borrow_mut().image_verify(&buf, None)?;
+ Ok(())
+ }
+
+ fn verify_region_from_file(&self, path: &Path, region: &str) -> Result<(), FlashromError> {
+ let mut layout = self.flashrom.borrow_mut().layout_read_fmap_from_rom()?;
+ layout.include_region(region)?;
+ let range = layout.get_region_range(region)?;
+ let region_data = fs::read(path).map_err(|error| error.to_string())?;
+ if region_data.len() != range.len() {
+ return Err(format!(
+ "verify region range ({}) does not match provided file size ({})",
+ range.len(),
+ region_data.len()
+ )
+ .into());
+ }
+ let mut buf = vec![0; self.get_size()? as usize];
+ buf[range].copy_from_slice(&region_data);
+ self.flashrom
+ .borrow_mut()
+ .image_verify(&buf, Some(layout))?;
+ Ok(())
+ }
+
+ fn erase(&self) -> Result<(), FlashromError> {
+ self.flashrom.borrow_mut().erase()?;
+ Ok(())
+ }
+
+ fn can_control_hw_wp(&self) -> bool {
+ self.fc.can_control_hw_wp()
+ }
+}
diff --git a/util/flashrom_tester/flashrom/src/lib.rs b/util/flashrom_tester/flashrom/src/lib.rs
new file mode 100644
index 000000000..41393e841
--- /dev/null
+++ b/util/flashrom_tester/flashrom/src/lib.rs
@@ -0,0 +1,165 @@
+//
+// 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.
+//
+
+#[macro_use]
+extern crate log;
+
+mod cmd;
+mod flashromlib;
+
+use std::{error, fmt, path::Path};
+
+pub use cmd::FlashromCmd;
+pub use flashromlib::FlashromLib;
+
+pub use libflashrom::{
+ flashrom_log_level, FLASHROM_MSG_DEBUG, FLASHROM_MSG_DEBUG2, FLASHROM_MSG_ERROR,
+ FLASHROM_MSG_INFO, FLASHROM_MSG_SPEW, FLASHROM_MSG_WARN,
+};
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum FlashChip {
+ HOST,
+}
+
+impl FlashChip {
+ pub fn from(s: &str) -> Result<FlashChip, &str> {
+ match s {
+ "host" => Ok(FlashChip::HOST),
+ _ => Err("cannot convert str to enum"),
+ }
+ }
+ pub fn to(fc: FlashChip) -> &'static str {
+ match fc {
+ FlashChip::HOST => "host",
+ }
+ }
+
+ /// Return the programmer string and optional programmer options
+ pub fn to_split(fc: FlashChip) -> (&'static str, Option<&'static str>) {
+ let programmer = FlashChip::to(fc);
+ let mut bits = programmer.splitn(2, ':');
+ (bits.next().unwrap(), bits.next())
+ }
+
+ /// Return whether the hardware write protect signal can be controlled.
+ ///
+ /// Servo and dediprog adapters are assumed to always have hardware write protect
+ /// disabled.
+ pub fn can_control_hw_wp(&self) -> bool {
+ match self {
+ FlashChip::HOST => true,
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct FlashromError {
+ msg: String,
+}
+
+impl fmt::Display for FlashromError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.msg)
+ }
+}
+
+impl error::Error for FlashromError {}
+
+impl<T> From<T> for FlashromError
+where
+ T: Into<String>,
+{
+ fn from(msg: T) -> Self {
+ FlashromError { msg: msg.into() }
+ }
+}
+
+pub trait Flashrom {
+ /// Returns the size of the flash in bytes.
+ fn get_size(&self) -> Result<i64, FlashromError>;
+
+ /// Returns the vendor name and the flash name.
+ fn name(&self) -> Result<(String, String), FlashromError>;
+
+ /// Set write protect status and range.
+ fn wp_range(&self, range: (i64, i64), wp_enable: bool) -> Result<bool, FlashromError>;
+
+ /// Read the write protect regions for the flash.
+ fn wp_list(&self) -> Result<String, FlashromError>;
+
+ /// Return true if the flash write protect status matches `en`.
+ fn wp_status(&self, en: bool) -> Result<bool, FlashromError>;
+
+ /// Set write protect status.
+ /// If en=true sets wp_range to the whole chip (0,getsize()).
+ /// If en=false sets wp_range to (0,0).
+ /// This is due to the MTD driver, which requires wp enable to use a range
+ /// length != 0 and wp disable to have the range 0,0.
+ fn wp_toggle(&self, en: bool) -> Result<bool, FlashromError>;
+
+ /// Read the whole flash to the file specified by `path`.
+ fn read_into_file(&self, path: &Path) -> Result<(), FlashromError>;
+
+ /// Read only a region of the flash into the file specified by `path`. Note
+ /// the first byte written to the file is the first byte from the region.
+ fn read_region_into_file(&self, path: &Path, region: &str) -> Result<(), FlashromError>;
+
+ /// Write the whole flash to the file specified by `path`.
+ fn write_from_file(&self, path: &Path) -> Result<(), FlashromError>;
+
+ /// Write only a region of the flash.
+ /// `path` is a file of the size of the whole flash.
+ /// The `region` name corresponds to a region name in the `layout` file, not the flash.
+ fn write_from_file_region(
+ &self,
+ path: &Path,
+ region: &str,
+ layout: &Path,
+ ) -> Result<bool, FlashromError>;
+
+ /// Verify the whole flash against the file specified by `path`.
+ fn verify_from_file(&self, path: &Path) -> Result<(), FlashromError>;
+
+ /// Verify only the region against the file specified by `path`.
+ /// Note the first byte in the file is matched against the first byte of the region.
+ fn verify_region_from_file(&self, path: &Path, region: &str) -> Result<(), FlashromError>;
+
+ /// Erase the whole flash.
+ fn erase(&self) -> Result<(), FlashromError>;
+
+ /// Return true if the hardware write protect of this flash can be controlled.
+ fn can_control_hw_wp(&self) -> bool;
+}