aboutsummaryrefslogtreecommitdiffstats
path: root/util/flashrom_tester/src/utils.rs
blob: d17480bcb41193a6f382088a0fcf5df41943afa8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pre { line-height: 125%; margin: 0; }
td.linenos pre { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
span.linenos { color: #000000; background-color: #f0f0f0; padding: 0 5px 0 5px; }
td.linenos pre.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding: 0 5px 0 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #ffffff; }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#!/bin/sh
[ $# -lt 4 -o -z "$1" -o -z "$2" -o -z "$3" -o -z "$4" ] && {
	echo "Usage: $0 <cross> <reference> <pic .a> <destination>"
	exit 1
}

cross="$1"; shift
ref="$1"; shift
pic="$1"; shift
dest="$1"; shift

SYMBOLS="$(${cross}nm "$ref" | grep -E '........ [TW] ' | awk '$3 {printf "-u%s ", $3}')"
set -x
${cross}gcc -nostdlib -nostartfiles -shared -Wl,--gc-sections -o "$dest" $SYMBOLS "$pic" "$@"
f='#n270'>270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
//
// 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 std::io::prelude::*;
use std::process::Command;

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LayoutNames {
    TopQuad,
    TopHalf,
    BottomHalf,
    BottomQuad,
}

#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct LayoutSizes {
    half_sz: i64,
    quad_sz: i64,
    rom_top: i64,
    bottom_half_top: i64,
    bottom_quad_top: i64,
    top_quad_bottom: i64,
}

pub fn get_layout_sizes(rom_sz: i64) -> Result<LayoutSizes, String> {
    if rom_sz <= 0 {
        return Err("invalid rom size provided".into());
    }
    if rom_sz & (rom_sz - 1) != 0 {
        return Err("invalid rom size, not a power of 2".into());
    }
    Ok(LayoutSizes {
        half_sz: rom_sz / 2,
        quad_sz: rom_sz / 4,
        rom_top: rom_sz - 1,
        bottom_half_top: (rom_sz / 2) - 1,
        bottom_quad_top: (rom_sz / 4) - 1,
        top_quad_bottom: (rom_sz / 4) * 3,
    })
}

pub fn layout_section(ls: &LayoutSizes, ln: LayoutNames) -> (&'static str, i64, i64) {
    match ln {
        LayoutNames::TopQuad => ("TOP_QUAD", ls.top_quad_bottom, ls.quad_sz),
        LayoutNames::TopHalf => ("TOP_HALF", ls.half_sz, ls.half_sz),
        LayoutNames::BottomHalf => ("BOTTOM_HALF", 0, ls.half_sz),
        LayoutNames::BottomQuad => ("BOTTOM_QUAD", 0, ls.quad_sz),
    }
}

pub fn construct_layout_file<F: Write>(mut target: F, ls: &LayoutSizes) -> std::io::Result<()> {
    writeln!(target, "000000:{:x} BOTTOM_QUAD", ls.bottom_quad_top)?;
    writeln!(target, "000000:{:x} BOTTOM_HALF", ls.bottom_half_top)?;
    writeln!(target, "{:x}:{:x} TOP_HALF", ls.half_sz, ls.rom_top)?;
    writeln!(target, "{:x}:{:x} TOP_QUAD", ls.top_quad_bottom, ls.rom_top)
}

pub fn toggle_hw_wp(dis: bool) -> Result<(), String> {
    // The easist way to toggle the harware write-protect is
    // to {dis}connect the battery (and/or open the WP screw).
    let s = if dis { "dis" } else { "" };
    info!("Prompt for hardware WP {}able", s);
    eprintln!(" > {}connect the battery (and/or open the WP screw)", s);
    pause();
    let wp = get_hardware_wp()?;
    if wp && dis {
        eprintln!("Hardware write protect is still ENABLED!");
        return toggle_hw_wp(dis);
    }
    if !wp && !dis {
        eprintln!("Hardware write protect is still DISABLED!");
        return toggle_hw_wp(dis);
    }
    Ok(())
}

pub fn ac_power_warning() {
    info!("*****************************");
    info!("AC power *must be* connected!");
    info!("*****************************");
    pause();
}

fn pause() {
    let mut stdout = std::io::stdout();
    // We want the cursor to stay at the end of the line, so we print without a newline
    // and flush manually.
    stdout.write(b"Press any key to continue...").unwrap();
    stdout.flush().unwrap();
    std::io::stdin().read(&mut [0]).unwrap();
}

pub fn get_hardware_wp() -> std::result::Result<bool, String> {
    let (_, wp) = parse_crosssystem(&collect_crosssystem()?)?;
    Ok(wp)
}

pub fn collect_crosssystem() -> Result<String, String> {
    let cmd = match Command::new("crossystem").output() {
        Ok(x) => x,
        Err(e) => return Err(format!("Failed to run crossystem: {}", e)),
    };

    if !cmd.status.success() {
        return Err(translate_command_error(&cmd).to_string());
    };

    Ok(String::from_utf8_lossy(&cmd.stdout).into_owned())
}

fn parse_crosssystem(s: &str) -> Result<(Vec<&str>, bool), &'static str> {
    // grep -v 'fwid +=' | grep -v 'hwid +='
    let sysinfo = s
        .split_terminator("\n")
        .filter(|s| !s.contains("fwid +=") && !s.contains("hwid +="));

    let state_line = match sysinfo.clone().filter(|s| s.starts_with("wpsw_cur")).next() {
        None => return Err("No wpsw_cur in system info"),
        Some(line) => line,
    };
    let wp_s_val = state_line
        .trim_start_matches("wpsw_cur")
        .trim_start_matches(' ')
        .trim_start_matches('=')
        .trim_start_matches(' ')
        .get(..1)
        .unwrap()
        .parse::<u32>();

    match wp_s_val {
        Ok(v) => {
            if v == 1 {
                return Ok((sysinfo.collect(), true));
            } else if v == 0 {
                return Ok((sysinfo.collect(), false));
            } else {
                return Err("Unknown state value");
            }
        }
        Err(_) => return Err("Cannot parse state value"),
    }
}

pub fn translate_command_error(output: &std::process::Output) -> std::io::Error {
    use std::io::{Error, ErrorKind};
    // There is two cases on failure;
    //  i. ) A bad exit code,
    //  ii.) A SIG killed us.
    match output.status.code() {
        Some(code) => {
            let e = format!(
                "{}\nExited with error code: {}",
                String::from_utf8_lossy(&output.stderr),
                code
            );
            Error::new(ErrorKind::Other, e)
        }
        None => Error::new(
            ErrorKind::Other,
            "Process terminated by a signal".to_string(),
        ),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn construct_layout_file() {
        use super::{construct_layout_file, get_layout_sizes};

        let mut buf = Vec::new();
        construct_layout_file(
            &mut buf,
            &get_layout_sizes(0x10000).expect("64k is a valid chip size"),
        )
        .expect("no I/O errors expected");

        assert_eq!(
            &buf[..],
            &b"000000:3fff BOTTOM_QUAD\n\
               000000:7fff BOTTOM_HALF\n\
               8000:ffff TOP_HALF\n\
               c000:ffff TOP_QUAD\n"[..]
        );
    }

    #[test]
    fn get_layout_sizes() {
        use super::get_layout_sizes;

        assert_eq!(
            get_layout_sizes(-128).err(),
            Some("invalid rom size provided".into())
        );

        assert_eq!(
            get_layout_sizes(3 << 20).err(),
            Some("invalid rom size, not a power of 2".into())
        );

        assert_eq!(
            get_layout_sizes(64 << 10).unwrap(),
            LayoutSizes {
                half_sz: 0x8000,
                quad_sz: 0x4000,
                rom_top: 0xFFFF,
                bottom_half_top: 0x7FFF,
                bottom_quad_top: 0x3FFF,
                top_quad_bottom: 0xC000,
            }
        );
    }

    #[test]
    fn parse_crosssystem() {
        use super::parse_crosssystem;

        assert_eq!(
            parse_crosssystem("This is not the tool you are looking for").err(),
            Some("No wpsw_cur in system info")
        );

        assert_eq!(
            parse_crosssystem("wpsw_cur = ERROR").err(),
            Some("Cannot parse state value")
        );

        assert_eq!(
            parse_crosssystem("wpsw_cur = 3").err(),
            Some("Unknown state value")
        );

        assert_eq!(
            parse_crosssystem("wpsw_cur = 0"),
            Ok((vec!["wpsw_cur = 0"], false))
        );

        assert_eq!(
            parse_crosssystem("wpsw_cur = 1"),
            Ok((vec!["wpsw_cur = 1"], true))
        );

        assert_eq!(
            parse_crosssystem("wpsw_cur=1"),
            Ok((vec!["wpsw_cur=1"], true))
        );

        assert_eq!(
            parse_crosssystem(
                "fwid += 123wpsw_cur\n\
                 hwid += aaaaa\n\
                 wpsw_boot                  = 0                      # [RO/int]\n\
                 wpsw_cur                   = 1                      # [RO/int]\n"
            ),
            Ok((
                vec![
                    "wpsw_boot                  = 0                      # [RO/int]",
                    "wpsw_cur                   = 1                      # [RO/int]"
                ],
                true
            ))
        );
    }
}