<div dir="ltr">I had a bit more time to work on this today and managed to fix several bugs and simplify the code - now all modes use a single algorithm for testing. I probably won't do another major revision for a few days, but fortunately it seems much more usable now. I also updated the documentation at <a href="https://goo.gl/3jNoL7" style="font-size:12.8px" target="_blank">https://goo.gl/3jNoL7</a>.<br><br>I also tried to get the test working with upstream, but there are a few features that need to be added first:<br>- Add -i <region>[:<file>] support (<a href="http://patchwork.coreboot.org/patch/4076/" target="_blank">http://patchwork.coreboot.org/patch/4076/</a>)<br>- Partial reads need to be supported.<br>- -r/-w/-v shouldn't require a positional argument (<a href="https://gerrit.chromium.org/gerrit/60515" target="_blank">https://gerrit.chromium.org/gerrit/60515</a>) since we want to do purely partial reads/writes in this case.<div>- Command-line options from chromium: --get-size to obtain chip size (I want to rename this --chip-size at some point), --ignore-fmap (which can be a noop), and --fast-verify (needed for Intel systems with read-locked regions, but good to have in any case).</div><div><br></div><div>So for now, those who want to try this script can do so using the <a href="http://chromium.org" target="_blank">chromium.org</a> fork: <a href="https://chromium.googlesource.com/chromiumos/third_party/flashrom/" target="_blank">https://chromium.googlesource.com/chromiumos/third_party/flashrom/</a><br><br>Here is patch revision 20 from <a href="https://chromium-review.googlesource.com/#/c/353912/" target="_blank">https://chromium-review.googlesource.com/#/c/353912/</a>:<br><br>From 8af85a07966b9486ea78c381c948aeb44bdcca7c Mon Sep 17 00:00:00 2001<br>From: David Hendricks <<a href="mailto:dhendrix@chromium.org" target="_blank">dhendrix@chromium.org</a>><br>Date: Sun, 19 Jun 2016 12:53:22 -0700<br>Subject: [PATCH] WIP: New test script for flashrom<br><br>** work in progress **<br><br>Shiny new testing capabilities for Flashrom:<br>- Region awareness<br>- Remote testing<br>- Primary and secondary programmer support<br>- Autotest-friendly<br><br>Long-term goals:<br>- Performance measurement<br>- Endurance testing<br><br>BUG=chromium:621715<br>BRANCH=none<br>TEST=this *is* the test<br><br>Change-Id: Ic643cf2edaf7d8772c63ce8119363d882d1b116d<br>Signed-off-by: David Hendricks <<a href="mailto:dhendrix@chromium.org" target="_blank">dhendrix@chromium.org</a>><br>---<br> tests/tests_v2/cmd.sh     | 109 +++++++<br> tests/tests_v2/test_v2.sh | 798 ++++++++++++++++++++++++++++++++++++++++++++++<br> 2 files changed, 907 insertions(+)<br> create mode 100644 tests/tests_v2/cmd.sh<br> create mode 100644 tests/tests_v2/test_v2.sh<br><br>diff --git a/tests/tests_v2/cmd.sh b/tests/tests_v2/cmd.sh<br>new file mode 100644<br>index 0000000..1cfd219<br>--- /dev/null<br>+++ b/tests/tests_v2/cmd.sh<br>@@ -0,0 +1,109 @@<br>+#!/bin/sh<br>+#<br>+# This file is part of the flashrom project. It is derived from<br>+# board_status.sh in coreboot.<br>+#<br>+# Copyright (C) 2016 Google Inc.<br>+# Copyright (C) 2014 Sage Electronic Engineering, LLC.<br>+<br>+# test a command<br>+#<br>+# $1: 0 ($LOCAL) to run command locally,<br>+#     1 ($REMOTE) to run remotely if remote host defined<br>+# $2: command to test<br>+# $3: 0 ($FATAL) Exit with an error if the command fails<br>+#     1 ($NONFATAL) Don't exit on command test failure<br>+test_cmd()<br>+{<br>+ local rc<br>+ local cmd__="$(echo $2 | cut -d ' ' -f -1)"<br>+ local args="$(echo $2 | cut -d ' ' -f 2-)"<br>+<br>+ if [ -e "$cmd__" ]; then<br>+ return<br>+ fi<br>+<br>+ if [ "$1" -eq "$REMOTE" ] && [ -n "$REMOTE_HOST" ]; then<br>+ ssh $REMOTE_PORT_OPTION root@${REMOTE_HOST} command -v "$cmd__" $args > /dev/null 2>&1<br>+ rc=$?<br>+ else<br>+ command -v "$cmd__" $args >/dev/null 2>&1<br>+ rc=$?<br>+ fi<br>+<br>+ if [ $rc -eq 0 ]; then<br>+ return 0<br>+ fi<br>+<br>+ if [ "$3" = "1" ]; then<br>+ return 1<br>+ fi<br>+<br>+ echo "$2 not found"<br>+ exit $EXIT_FAILURE<br>+}<br>+<br>+# Same args as cmd() but with the addition of $4 which determines if the<br>+# command should be totally silenced or not.<br>+_cmd()<br>+{<br>+ local silent=$4<br>+<br>+ if [ -n "$3" ]; then<br>+ pipe_location="${3}"<br>+ else<br>+ pipe_location="/dev/null"<br>+ fi<br>+<br>+ if [ $1 -eq $REMOTE ] && [ -n "$REMOTE_HOST" ]; then<br>+ if [ $silent -eq 0 ]; then<br>+ ssh $REMOTE_PORT_OPTION "root@${REMOTE_HOST}" "$2" > "$pipe_location" 2>/dev/null<br>+ else<br>+ ssh $REMOTE_PORT_OPTION "root@${REMOTE_HOST}" "$2" > /dev/null 2>&1<br>+ fi<br>+ else<br>+ if [ $silent -eq 0 ]; then<br>+ $2 > "$pipe_location" 2>/dev/null<br>+ else<br>+ $2 > /dev/null 2>&1<br>+ fi<br>+ fi<br>+<br>+ return $?<br>+}<br>+<br>+# run a command<br>+#<br>+# $1: 0 ($LOCAL) to run command locally,<br>+#     1 ($REMOTE) to run remotely if remote host defined<br>+# $2: command<br>+# $3: filename to direct output of command into<br>+cmd()<br>+{<br>+ _cmd $1 "$2" "$3" 0<br>+<br>+ if [ $? -eq 0 ]; then<br>+ return<br>+ fi<br>+<br>+ echo "Failed to run \"$2\", aborting"<br>+ rm -f "$3" # don't leave an empty file<br>+ return $EXIT_FAILURE<br>+}<br>+<br>+# run a command silently<br>+#<br>+# $1: 0 ($LOCAL) to run command locally,<br>+#     1 ($REMOTE) to run remotely if remote host defined<br>+# $2: command<br>+scmd()<br>+{<br>+ _cmd $1 "$2" "" 1<br>+<br>+ if [ $? -eq 0 ]; then<br>+ return<br>+ fi<br>+<br>+ echo "Failed to run \"$2\", aborting"<br>+ return $EXIT_FAILURE<br>+}<br>diff --git a/tests/tests_v2/test_v2.sh b/tests/tests_v2/test_v2.sh<br>new file mode 100644<br>index 0000000..cb9bace<br>--- /dev/null<br>+++ b/tests/tests_v2/test_v2.sh<br>@@ -0,0 +1,798 @@<br>+#!/bin/sh<br>+#<br>+# Copyright (C) 2016 Google Inc.<br>+#<br>+# This program is free software; you can redistribute it and/or modify<br>+# it under the terms of the GNU General Public License as published by<br>+# the Free Software Foundation; either version 2 of the License, or<br>+# (at your option) any later version.<br>+#<br>+# This program is distributed in the hope that it will be useful,<br>+# but WITHOUT ANY WARRANTY; without even the implied warranty of<br>+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the<br>+# GNU General Public License for more details.<br>+#<br>+# You should have received a copy of the GNU General Public License<br>+# along with this program; if not, write to the Free Software<br>+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA<br>+<br>+EXIT_SUCCESS=0<br>+EXIT_FAILURE=1<br>+RC=$EXIT_SUCCESS<br>+FATAL=0<br>+NONFATAL=1<br>+<br>+# For old flashrom version, if -o is not specified<br>+DEFAULT_FLASHROM_PATH="/usr/local/sbin/flashrom"<br>+<br>+#<br>+# Stuff obtained from command-line<br>+#<br>+<br>+# Generic options<br>+BACKUP_IMAGE=""<br>+OLD_FLASHROM=""<br>+NEW_FLASHROM=""<br>+NO_CLEAN=0<br>+SKIP_CONSISTENCY_CHECK=0<br>+UPLOAD_RESULTS=0<br>+<br>+# Primary/Secondary programmer options<br>+PRIMARY_OPTS=""<br>+SECONDARY_OPTS=""<br>+<br>+# Region modes<br>+REGION_MODE_UNKNOWN=0<br>+REGION_MODE_CLOBBER=1<br>+REGION_MODE_DESCRIPTOR=2<br>+REGION_MODE_FLASHMAP=3<br>+REGION_MODE_LAYOUT=4<br>+REGION_MODE=$REGION_MODE_UNKNOWN<br>+DESCRIPTOR_REGION="BIOS"<br>+FLASHMAP_REGION="RW_SECTION_A"<br>+LAYOUT_FILE=""<br>+LAYOUT_REGION="RW"<br>+SMALL_REGION=0<br>+<br>+# Remote testing options<br>+SSH_PORT=""<br>+REMOTE_HOST=""<br>+REMOTE_PORT_OPTION=""<br>+REMOTE_ONLY=0<br>+REMOTE_PROGRAMMER_PARAMS=""<br>+SSH_CMD="ssh $REMOTE_PORT_OPTION root@${REMOTE_HOST} command -v"<br>+<br>+LOCAL=0<br>+REMOTE=1<br>+DO_REMOTE=$LOCAL # boolean to use for cmd() and tests<br>+<br>+# 1KB<br>+K="1024"<br>+<br>+show_help() {<br>+ echo "Usage:<br>+ ${0} <options><br>+<br>+General options:<br>+    -b, --backup-image <path><br>+        Backup image to write unconditionally at end of testing.<br>+    -h, --help<br>+        Show this message.<br>+    -l, --layout-file <path><br>+        Layout file (required if mode is \"layout\", resides locally).<br>+    -m, --mode <arg><br>+        Region access mode (clobber, descriptor, flashmap, layout).<br>+    -n, --new <path><br>+        Path to new version of flashrom.<br>+    -o, --old <path><br>+        Path to old (stable) version of flashrom.<br>+    -p, --primary-programmer <parameters><br>+        Primary programmer options.<br>+    -r, --remote-host <host><br>+        Remote host to test primary programmer on.<br>+    -s, --secondary-programmer <parameters><br>+        Secondary programmer options.<br>+    -u, --upload-results<br>+        Upload results to <a href="http://flashrom.org" target="_blank">flashrom.org</a>.<br>+<br>+Long options:<br>+    --descriptor-region <name><br>+        Specify region to use in descriptor mode (default: $DESCRIPTOR_REGION)<br>+    --fmap-region <name><br>+        Specify region to use in flashmap mode (default: $FLASHMAP_REGION)<br>+    --layout-region <name><br>+        Specify region to use in layout mode (default: $LAYOUT_REGION)<br>+    --no-clean<br>+    Do not remove temporary files.<br>+    --skip-consistency-check<br>+        Skip the consistency check (two consecutive reads) at beginning.<br>+    --small-region<br>+        Omit tests that require large amounts of space (>16KB).<br>+<br>+Remote connectivity options:<br>+    --ssh-port <port><br>+        Use a specific SSH port.<br>+<br>+See documentation for usage examples (TODO: Migrate <a href="https://goo.gl/3jNoL7" target="_blank">https://goo.gl/3jNoL7</a><br>+to flashrom wiki).<br>+"<br>+}<br>+<br>+getopt -T<br>+if [ $? -ne 4 ]; then<br>+ echo "GNU-compatible getopt(1) required."<br>+ exit $EXIT_FAILURE<br>+fi<br>+<br>+LONGOPTS="backup-image:,help,,new:,old:,remote-host:,upload-results:"<br>+LONGOPTS="${LONGOPTS},primary-programmer:,secondary-programmer:"<br>+LONGOPTS="${LONGOPTS},skip-consistency-check"<br>+LONGOPTS="${LONGOPTS},mode:,clobber,descriptor,flashmap,layout,small-region"<br>+LONGOPTS="${LONGOPTS},layout-file:,desctriptor-region:,flashmap-region:,layout-region:"<br>+LONGOPTS="${LONGOPTS},no-clean"<br>+LONGOPTS="${LONGOPTS},ssh-port:"<br>+<br>+ARGS=$(getopt -o b:hl:m:n:o:p:r:s:u -l "$LONGOPTS" -n "$0" -- "$@");<br>+if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi<br>+eval set -- "$ARGS"<br>+while true ; do<br>+ case "$1" in<br>+ # Generic options<br>+ -b|--backup-image)<br>+ shift<br>+ BACKUP_IMAGE="$1"<br>+ ;;<br>+ -h|--help)<br>+ show_help<br>+ exit $EXIT_SUCCESS<br>+ ;;<br>+ -l|--layout-file)<br>+ shift<br>+ LAYOUT_FILE="$1"<br>+ ;;<br>+ -m|--mode)<br>+ shift<br>+ if [ "$1" == "clobber" ]; then<br>+ REGION_MODE=$REGION_MODE_CLOBBER<br>+ elif [ "$1" == "descriptor" ]; then<br>+ REGION_MODE=$REGION_MODE_DESCRIPTOR<br>+ elif [ "$1" == "flashmap" ]; then<br>+ REGION_MODE=$REGION_MODE_FLASHMAP<br>+ elif [ "$1" == "layout" ]; then<br>+ REGION_MODE=$REGION_MODE_LAYOUT<br>+ else<br>+ echo "Unknown mode: $1"<br>+ exit $EXIT_FAILURE<br>+ fi<br>+ ;;<br>+ -n|--new)<br>+ shift<br>+ NEW_FLASHROM="$1"<br>+ ;;<br>+ -o|--old)<br>+ shift<br>+ OLD_FLASHROM="$1"<br>+ ;;<br>+ -p|--primary_programmer)<br>+ shift<br>+ PRIMARY_OPTS="-p $1"<br>+ ;;<br>+ -s|--secondary_programmer)<br>+ shift<br>+ SECONDARY_OPTS="-p $1"<br>+ ;;<br>+ -r|--remote-host)<br>+ DO_REMOTE=1<br>+ shift<br>+ REMOTE_HOST="$1"<br>+ ;;<br>+ -u|--upload-results)<br>+ UPLOAD_RESULTS=1<br>+ ;;<br>+<br>+ # Longopts only<br>+ --descriptor-region)<br>+ shift<br>+ DESCRIPTOR_REGION="$1"<br>+ ;;<br>+ --flashmap-region)<br>+ shift<br>+ FLASHMAP_REGION="$1"<br>+ ;;<br>+ --layout-region)<br>+ shift<br>+ LAYOUT_REGION="$1"<br>+ ;;<br>+ --no-clean)<br>+ NO_CLEAN=1<br>+ ;;<br>+ --skip-consistency-check)<br>+ SKIP_CONSISTENCY_CHECK=1<br>+ ;;<br>+<br>+ # Remote testing options<br>+ --ssh-port)<br>+ shift<br>+ REMOTE_PORT_OPTION="-p $1"<br>+ ;;<br>+<br>+ # error handling<br>+ --)<br>+ shift<br>+ if [ -n "$*" ]; then<br>+ echo "Non-option parameters detected: '$*'"<br>+ exit $EXIT_FAILURE<br>+ fi<br>+ break<br>+ ;;<br>+ *)<br>+ echo "error processing options at '$1'"<br>+ exit $EXIT_FAILURE<br>+ esac<br>+ shift<br>+done<br>+<br>+# TODO: Implement this.<br>+if [ $UPLOAD_RESULTS -eq 1 ]; then<br>+ echo "TODO: Implement ability to upload results."<br>+ exit $EXIT_FAILURE<br>+fi<br>+<br>+#<br>+# Source helper scripts<br>+#<br>+export REMOTE_HOST REMOTE_PORT_OPTION<br>+export LOCAL REMOTE FATAL NONFATAL EXIT_SUCCESS EXIT_FAILURE<br>+. "$(pwd)/tests/tests_v2/cmd.sh"<br>+<br>+#<br>+# Test command-line validity.<br>+#<br>+if [ $REGION_MODE -eq $REGION_MODE_UNKNOWN ]; then<br>+ echo "Must specify a region access mode (-m/--mode)."<br>+ exit $EXIT_FAILURE<br>+elif [ $REGION_MODE -eq $REGION_MODE_LAYOUT ]; then<br>+ if [ -z "$LAYOUT_FILE" ]; then<br>+ echo "Must specify a layout file when using layout mode."<br>+ exit $EXIT_FAILURE<br>+ fi<br>+<br>+ scmd $DO_REMOTE "stat $LAYOUT_FILE"<br>+ if [ $? -ne 0 ]; then<br>+ if [ $DO_REMOTE -eq 1 ]; then<br>+ tmp=" on remote host $REMOTE_HOST."<br>+ else<br>+ tmp=" on local host."<br>+ fi<br>+ echo "Layout file $LAYOUT_FILE not found${TMP}"<br>+ exit $EXIT_FAILURE<br>+ fi<br>+<br>+ if [ $DO_REMOTE -eq 1 ]; then<br>+ scp root@"${REMOTE_HOST}:$LAYOUT_FILE" "${LOCAL_TMPDIR}/" 2>&1 >/dev/null<br>+ fi<br>+fi<br>+<br>+if [ $DO_REMOTE -eq 1 ]; then<br>+ # Test connection to remote host<br>+ test_cmd $DO_REMOTE "ls /" $NONFATAL<br>+ if [ $? -ne 0 ]; then<br>+ echo "Could not connect to remote host $REMOTE_HOST"<br>+ exit $EXIT_FAILURE<br>+ fi<br>+fi<br>+<br>+#<br>+# Dependencies<br>+#<br>+<br>+# cmp is used to compare files<br>+test_cmd $DO_REMOTE "cmp" $FATAL<br>+<br>+if [ ! -e "/dev/urandom" ]; then<br>+ echo "This script uses /dev/urandom"<br>+ exit $EXIT_FAILURE<br>+fi<br>+<br>+if [ ! -e "/dev/zero" ]; then<br>+ echo "This script uses /dev/zero"<br>+ exit $EXIT_FAILURE<br>+fi<br>+<br>+#<br>+# Setup.<br>+#<br>+grep -rH 'projectname = .*flashrom' .git/config >/dev/null 2>&1<br>+if [ $? -ne 0 ]; then<br>+ echo "Script must be run from root of flashrom directory"<br>+ exit $EXIT_FAILURE<br>+fi<br>+<br>+if [ -z "$OLD_FLASHROM" ]; then<br>+ OLD_FLASHROM="$DEFAULT_FLASHROM_PATH"<br>+fi<br>+test_cmd $DO_REMOTE "$OLD_FLASHROM --help" $NONFATAL<br>+if [ $? -ne 0 ]; then<br>+ echo "Old flashrom binary is not usable."<br>+ exit $EXIT_FAILURE<br>+fi<br>+<br>+# Setup temporary working directories:<br>+# LOCAL_TMPDIR:  Working directory on local host.<br>+# REMOTE_TMPDIR: Working directory on remote host.<br>+# TMPDIR:        The temporary directy in which we do most of the work. This is<br>+#                convenient for commands that depend on $DO_REMOTE.<br>+LOCAL_TMPDIR=$(mktemp -d --tmpdir flashrom_test.XXXXXXXX)<br>+if [ $? -ne 0 ] ; then<br>+ echo "Could not create temporary directory"<br>+ exit $EXIT_FAILURE<br>+fi<br>+<br>+if [ $DO_REMOTE -eq 1 ]; then<br>+ REMOTE_TMPDIR=$(ssh root@${REMOTE_HOST} mktemp -d --tmpdir flashrom_test.XXXXXXXX)<br>+ if [ $? -ne 0 ] ; then<br>+ echo "Could not create temporary directory"<br>+ exit $EXIT_FAILURE<br>+ fi<br>+fi<br>+<br>+if [ $DO_REMOTE -eq 0 ]; then<br>+ TMPDIR="$LOCAL_TMPDIR"<br>+else<br>+ TMPDIR="$REMOTE_TMPDIR"<br>+fi<br>+<br>+# Copy files from local tmpdir to remote host tmpdir<br>+copy_to_remote()<br>+{<br>+ for F in $@; do<br>+ scp "${LOCAL_TMPDIR}/$F" root@"${REMOTE_HOST}:${REMOTE_TMPDIR}" 2>&1 >/dev/null<br>+ done<br>+}<br>+<br>+# Copy files from remote host tmpdir to local tmpdir<br>+copy_from_remote()<br>+{<br>+ for F in $@; do<br>+ scp root@"${REMOTE_HOST}:${REMOTE_TMPDIR}/$F" "${LOCAL_TMPDIR}/" 2>&1 >/dev/null<br>+ done<br>+}<br>+<br>+# Read current image as backup in case one hasn't already been specified.<br>+if [ -z "$BACKUP_IMAGE" ]; then<br>+ backup_file="backup.bin"<br>+ if [ $DO_REMOTE -eq 1 ]; then<br>+ BACKUP_IMAGE="${REMOTE_TMPDIR}/${backup_file}"<br>+ else<br>+ BACKUP_IMAGE="${LOCAL_TMPDIR}/${backup_file}"<br>+ fi<br>+<br>+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -r $BACKUP_IMAGE"<br>+ if [ $? -ne 0 ]; then<br>+ echo "Failed to read backup image, aborting."<br>+ exit $EXIT_FAILURE<br>+ fi<br>+<br>+ if [ $DO_REMOTE -eq 1 ]; then<br>+ copy_from_remote "${backup_file}"<br>+ fi<br>+else<br>+ if [ $DO_REMOTE -eq 1 ]; then<br>+ scmd $DO_REMOTE "cp $BACKUP_IMAGE ${REMOTE_TMPDIR}"<br>+ copy_from_remote "$(basename $BACKUP_IMAGE)"<br>+ fi<br>+fi<br>+<br>+# The copy of flashrom to test. If unset, we'll assume the user wants to test<br>+# a newly built flashrom binary in the current directory.<br>+if [ -z "$NEW_FLASHROM" ] ; then<br>+ if [ -x "flashrom" ]; then<br>+ NEW_FLASHROM="flashrom"<br>+ else<br>+ echo "Must supply new flashrom version to test"<br>+ exit $EXIT_FAILURE<br>+ fi<br>+fi<br>+<br>+echo "Stable flashrom binary: $OLD_FLASHROM"<br>+echo "New flashrom binary to test: $NEW_FLASHROM"<br>+echo "Local temporary files will be stored in $LOCAL_TMPDIR"<br>+if [ $DO_REMOTE -eq 1 ]; then<br>+ echo "Remote temporary files will be stored in ${REMOTE_HOST}:${REMOTE_TMPDIR}"<br>+ echo "Backup image: ${REMOTE_HOST}:${BACKUP_IMAGE}"<br>+ echo "Backup image also stored at: ${LOCAL_TMPDIR}/$(basename ${BACKUP_IMAGE})"<br>+else<br>+ echo "Backup image: ${BACKUP_IMAGE}"<br>+fi<br>+<br>+#<br>+# Now the fun begins.<br>+#<br>+cmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS --get-size" "${LOCAL_TMPDIR}/chip_size.txt"<br>+tmp=$(cat ${LOCAL_TMPDIR}/chip_size.txt)<br>+cmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS --get-size" "${LOCAL_TMPDIR}/chip_size.txt"<br>+CHIP_SIZE=$(cat ${LOCAL_TMPDIR}/chip_size.txt)<br>+CHIP_SIZE_KB=$(($CHIP_SIZE / $K))<br>+if [ $CHIP_SIZE -ne $tmp ]; then<br>+ echo "New flashrom and old flashrom disagree on chip size. Aborting."<br>+ exit $EXIT_FAILURE<br>+else<br>+ echo "Chip size: ${CHIP_SIZE_KB} KiB"<br>+fi<br>+<br>+# Upload results<br>+#do_upload()<br>+#{<br>+# # TODO: implement this<br>+#}<br>+<br>+# Remove temporary files<br>+do_cleanup()<br>+{<br>+ if [ $NO_CLEAN -eq 1 ]; then<br>+ echo "Skipping cleanup."<br>+ return $EXIT_SUCCESS<br>+ fi<br>+<br>+ rm -rf "$LOCAL_TMPDIR"<br>+ if [ -n "$REMOTE_HOST" ]; then<br>+ ssh root@${REMOTE_HOST} rm -rf "${REMOTE_TMPDIR}"<br>+ fi<br>+<br>+ return $EXIT_SUCCESS<br>+}<br>+<br>+# $1: Message to display to user.<br>+test_fail()<br>+{<br>+ echo "$1"<br>+ do_cleanup<br>+ exit $EXIT_FAILURE<br>+}<br>+<br>+write_backup_image()<br>+{<br>+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -w $BACKUP_IMAGE"<br>+}<br>+<br>+# Read a region twice and compare results<br>+# $1: address of region (in bytes)<br>+# $2: length of region (in bytes)<br>+double_read_test()<br>+{<br>+ local cmp1="${TMPDIR}/cmp1.bin"<br>+ local cmp2="${TMPDIR}/cmp2.bin"<br>+ local layout="double_read_test_layout.txt"<br>+<br>+ printf "Doing double read test, size: %u KiB\n" $(($2 / $K))<br>+ # FIXME: Figure out how to do printf remotely...<br>+ printf "%06x:%06x region\n" $1 $(($1 + $2 - 1)) > "${LOCAL_TMPDIR}/${layout}"<br>+ if [ $DO_REMOTE -eq 1 ]; then copy_to_remote "$layout" ; fi<br>+<br>+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -r -l ${TMPDIR}/${layout} --ignore-fmap -i region:$cmp1"<br>+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -r -l ${TMPDIR}/${layout} --ignore-fmap -i region:$cmp2"<br>+ scmd $DO_REMOTE "cmp $cmp1 $cmp2"<br>+ if [ $? -ne 0 ]; then<br>+ test_fail "Double-read test failed, aborting."<br>+ fi<br>+}<br>+<br>+# Regional partial write test. Given a region name, this will write patterns<br>+# of bytes designed to test corner cases.<br>+#<br>+# We assume that eraseable block size can be either 4KB or 64KB and<br>+# must test for both. For simplicity, we'll assume the region size is<br>+# at least 256KB.<br>+#<br>+# $1: Region name<br>+partial_write_test()<br>+{<br>+ local opts="--fast-verify"<br>+ local layout="partial_write_test_layout.txt"<br>+ local region_size=0<br>+ local align_size_kb=0<br>+ local region_name="$1"<br>+ local filename=""<br>+ local offset_kb=0<br>+ local size_kb=0<br>+ local test_num=0<br>+ local prev_test_num=0<br>+<br>+ if [ $REGION_MODE -ne $REGION_MODE_FLASHMAP ]; then<br>+ opts="$opts --ignore-fmap"<br>+ fi<br>+<br>+ if [ $REGION_MODE -eq $REGION_MODE_LAYOUT ]; then<br>+ opts="$opts -l $LAYOUT_FILE"<br>+ elif [ $REGION_MODE -eq $REGION_MODE_CLOBBER ]; then<br>+ printf "000000:%06x RW\n" $(($CHIP_SIZE - 1)) > "${LOCAL_TMPDIR}/clobber_mode_layout.txt"<br>+ if [ $DO_REMOTE -eq 1 ]; then<br>+ copy_to_remote "clobber_mode_layout.txt"<br>+ fi<br>+ opts="$opts -l ${TMPDIR}/clobber_mode_layout.txt"<br>+ fi<br>+<br>+ if [ $SMALL_REGION -eq 1 ]; then<br>+ align_size_kb=16<br>+ else<br>+ align_size_kb=256<br>+ fi<br>+<br>+ # FIXME: Add sanity checks.<br>+<br>+ echo "Doing region-based partial write test on region \"$region_name\""<br>+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -r -i ${region_name}:${TMPDIR}/${region_name}.bin"<br>+ if [ $DO_REMOTE -eq 1 ]; then<br>+ copy_from_remote "${region_name}.bin"<br>+ fi<br>+<br>+ region_size=$(stat --format=%s ${LOCAL_TMPDIR}/${region_name}.bin)<br>+ if [ $region_size -lt $(($align_size_kb * $K)) ]; then<br>+ echo "Region $region_name is too small"<br>+ return $EXIT_FAILURE<br>+ fi<br>+<br>+ if [ $(($region_size % $(($align_size_kb * $K)))) -ne 0 ]; then<br>+ echo "Region $region_name is not aligned to ${align_size}KB"<br>+ return $EXIT_FAILURE<br>+ fi<br>+<br>+ # Test procedure:<br>+ # Clobber region with random content first. Then do writes using the<br>+ # following sequences for each 128KB:<br>+ # 0-2K : 0x00 (\000) Partial 4KB sector, lower half<br>+ # 2K-6K : 0x11 (\021) Crossover 4KB sector boundary<br>+ # 6K-8K : 0x22 (\042) Partial 4KB sector, upper half<br>+ # 8K-16K : 0x33 (\063) Full 4KB sectors<br>+ #<br>+ # Repeat the above sequence for 64KB-aligned sizes<br>+ # 0-32K : 0x44 (\104) Partial 64KB block, lower half<br>+ # 32K-96K : 0x55 (\125) Crossover 64KB block boundary<br>+ # 96K-128K : 0x66 (\146) Partial 64KB block, upper half<br>+ # 128K-256K : 0x77 (\167) Full 64KB blocks<br>+<br>+ test_num=0<br>+ dd if=/dev/urandom of="${LOCAL_TMPDIR}/random_4k_test.bin" bs=4k count=$(($region_size / $((4 * $K)))) 2>/dev/null<br>+<br>+ # 0-2K : 0x00 (\000) Partial 4KB sector, lower half<br>+ offset_kb=0<br>+ size_kb=2<br>+ hex="0x00"<br>+ oct="\\$(printf "%03o" $hex)"<br>+ cp "${LOCAL_TMPDIR}/random_4k_test.bin" "${LOCAL_TMPDIR}/4k_test_${test_num}.bin"<br>+ while [ $(($offset_kb * $K)) -lt $region_size ]; do<br>+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"<br>+ dd if="${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin" of="${LOCAL_TMPDIR}/4k_test_${test_num}.bin" bs=1k count=${size_kb} seek=${offset_kb} conv=notrunc 2>/dev/null<br>+ offset_kb=$(($offset_kb + $align_size_kb))<br>+ done<br>+ prev_test_num=$test_num<br>+ test_num=$(($test_num + 1))<br>+<br>+ # 2K-6K : 0x11 (\021) Crossover 4KB sector boundary<br>+ offset_kb=2<br>+ size_kb=4<br>+ hex="0x11"<br>+ oct="\\$(printf "%03o" $hex)"<br>+ cp "${LOCAL_TMPDIR}/4k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/4k_test_${test_num}.bin"<br>+ while [ $(($offset_kb * $K)) -lt $region_size ]; do<br>+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"<br>+ dd if="${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin" of="${LOCAL_TMPDIR}/4k_test_${test_num}.bin" bs=1k count=${size_kb} seek=${offset_kb} conv=notrunc 2>/dev/null<br>+ offset_kb=$(($offset_kb + $align_size_kb))<br>+ done<br>+ test_num=$(($test_num + 1))<br>+<br>+ # 6K-8K : 0x22 (\042) Partial 4KB sector, upper half<br>+ offset_kb=2<br>+ size_kb=4<br>+ hex="0x11"<br>+ oct="\\$(printf "%03o" $hex)"<br>+ cp "${LOCAL_TMPDIR}/4k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/4k_test_${test_num}.bin"<br>+ while [ $(($offset_kb * $K)) -lt $region_size ]; do<br>+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"<br>+ dd if="${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin" of="${LOCAL_TMPDIR}/4k_test_${test_num}.bin" bs=1k count=${size_kb} seek=${offset_kb} conv=notrunc 2>/dev/null<br>+ offset_kb=$(($offset_kb + $align_size_kb))<br>+ done<br>+ test_num=$(($test_num + 1))<br>+<br>+ # 8K-16K : 0x33 (\063) Full 4KB sectors<br>+ offset_kb=8<br>+ size_kb=8<br>+ hex="0x22"<br>+ oct="\\$(printf "%03o" $hex)"<br>+ cp "${LOCAL_TMPDIR}/4k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/4k_test_${test_num}.bin"<br>+ while [ $(($offset_kb * $K)) -lt $region_size ]; do<br>+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"<br>+ dd if="${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin" of="${LOCAL_TMPDIR}/4k_test_${test_num}.bin" bs=1k count=${size_kb} seek=${offset_kb} conv=notrunc 2>/dev/null<br>+ offset_kb=$(($offset_kb + $align_size_kb))<br>+ done<br>+ test_num=$(($test_num + 1))<br>+<br>+ for F in ${LOCAL_TMPDIR}/random_4k_test.bin ${LOCAL_TMPDIR}/4k_test_*.bin ; do<br>+ filename=$(basename $F)<br>+ if [ $DO_REMOTE -eq 1 ]; then<br>+ copy_to_remote $filename<br>+ fi<br>+<br>+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -w -i ${region_name}:${TMPDIR}/${filename}"<br>+ if [ $? -ne 0 ]; then<br>+ test_fail "Failed to write $filename to $region_name"<br>+ fi<br>+<br>+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -v -i ${region_name}:${TMPDIR}/${filename}"<br>+ if [ $? -ne 0 ]; then<br>+ test_fail "Failed to verify write of $filename to $region_name"<br>+ fi<br>+<br>+ if [ -n "$SECONDARY_OPTS" ]; then<br>+ scmd $LOCAL "$OLD_FLASHROM $SECONDARY_OPTS $opts -v -i ${region_name}:${TMPDIR}/${filename}"<br>+ if [ $? -ne 0 ]; then<br>+ test_fail "Failed to verify write of $filename to $region_name using secondary programmer"<br>+ fi<br>+ fi<br>+<br>+ printf "\tWrote $filename to $region_name region successfully.\n"<br>+ done<br>+<br>+ if [ $SMALL_REGION -eq 1 ]; then<br>+ return $EXIT_SUCCESS<br>+ fi<br>+<br>+ #<br>+ # Second half: Tests for 64KB chunks<br>+ #<br>+ test_num=0<br>+ dd if=/dev/urandom of="${LOCAL_TMPDIR}/random_64k_test.bin" bs=128k count=$(($region_size / $((128*$K)))) 2>/dev/null<br>+<br>+ # 0-32K : 0x44 (\104) Partial 64KB block, lower half<br>+ offset_kb=0<br>+ size_kb=32<br>+ hex="0x44"<br>+ oct="\\$(printf "%03o" $hex)"<br>+ cp "${LOCAL_TMPDIR}/random_64k_test.bin" "${LOCAL_TMPDIR}/64k_test_${test_num}.bin"<br>+ while [ $(($offset_kb * $K)) -lt $region_size ]; do<br>+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"<br>+ dd if="${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin" of="${LOCAL_TMPDIR}/64k_test_${test_num}.bin" bs=1k count=${size_kb} seek=${offset_kb} conv=notrunc 2>/dev/null<br>+ offset_kb=$(($offset_kb + $align_size_kb))<br>+ done<br>+ prev_test_num=$test_num<br>+ test_num=$(($test_num + 1))<br>+<br>+ # 32K-96K : 0x55 (\125) Crossover 64KB block boundary<br>+ offset_kb=32<br>+ size_kb=64<br>+ hex="0x55"<br>+ oct="\\$(printf "%03o" $hex)"<br>+ cp "${LOCAL_TMPDIR}/64k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/64k_test_${test_num}.bin"<br>+ while [ $(($offset_kb * $K)) -lt $region_size ]; do<br>+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"<br>+ dd if="${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin" of="${LOCAL_TMPDIR}/64k_test_${test_num}.bin" bs=1k count=${size_kb} seek=${offset_kb} conv=notrunc 2>/dev/null<br>+ offset_kb=$(($offset_kb + $align_size_kb))<br>+ done<br>+ test_num=$(($test_num + 1))<br>+<br>+ # 96K-128K : 0x66 (\146) Partial 64KB block, upper half<br>+ offset_kb=96<br>+ size_kb=32<br>+ hex="0x66"<br>+ oct="\\$(printf "%03o" $hex)"<br>+ cp "${LOCAL_TMPDIR}/64k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/64k_test_${test_num}.bin"<br>+ while [ $(($offset_kb * $K)) -lt $region_size ]; do<br>+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"<br>+ dd if="${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin" of="${LOCAL_TMPDIR}/64k_test_${test_num}.bin" bs=1k count=${size_kb} seek=${offset_kb} conv=notrunc 2>/dev/null<br>+ offset_kb=$(($offset_kb + $align_size_kb))<br>+ done<br>+ test_num=$(($test_num + 1))<br>+<br>+ # 128K-256K : 0x77 (\167) Full 64KB blocks<br>+ offset_kb=128<br>+ size_kb=128<br>+ hex="0x77"<br>+ oct="\\$(printf "%03o" $hex)"<br>+ cp "${LOCAL_TMPDIR}/64k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/64k_test_${test_num}.bin"<br>+ while [ $(($offset_kb * $K)) -lt $region_size ]; do<br>+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"<br>+ dd if="${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin" of="${LOCAL_TMPDIR}/64k_test_${test_num}.bin" bs=1k count=${size_kb} seek=${offset_kb} conv=notrunc 2>/dev/null<br>+ offset_kb=$(($offset_kb + $align_size_kb))<br>+ done<br>+<br>+ for F in ${LOCAL_TMPDIR}/random_64k_test.bin ${LOCAL_TMPDIR}/64k_test_*.bin ; do<br>+ filename=$(basename $F)<br>+ if [ $DO_REMOTE -eq 1 ]; then<br>+ copy_to_remote $filename<br>+ fi<br>+<br>+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -w -i ${region_name}:${TMPDIR}/${filename}"<br>+ if [ $? -ne 0 ]; then<br>+ test_fail "Failed to write $filename to $region_name"<br>+ fi<br>+<br>+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -v -i ${region_name}:${TMPDIR}/${filename}"<br>+ if [ $? -ne 0 ]; then<br>+ test_fail "Failed to verify write of $filename to $region_name"<br>+ fi<br>+<br>+ if [ -n "$SECONDARY_OPTS" ]; then<br>+ scmd $LOCAL "$OLD_FLASHROM $SECONDARY_OPTS $opts -v -i ${region_name}:${TMPDIR}/${filename}"<br>+ if [ $? -ne 0 ]; then<br>+ test_fail "Failed to verify write of $filename to $region_name using secondary programmer"<br>+ fi<br>+ fi<br>+<br>+ printf "\tWrote $filename to $region_name region successfully.\n"<br>+ done<br>+<br>+ return $EXIT_SUCCESS<br>+}<br>+<br>+# Do a consistency check for sanity before any other test.<br>+if [ $SKIP_CONSISTENCY_CHECK -eq 0 ]; then<br>+ double_read_test 0 $CHIP_SIZE<br>+fi<br>+<br>+if [ $REGION_MODE -eq $REGION_MODE_CLOBBER ]; then<br>+ random_file="${TMPDIR}/random_${CHIP_SIZE_KB}K.bin"<br>+ cmp_file="${TMPDIR}/cmp.bin"<br>+<br>+ scmd $DO_REMOTE "dd if=/dev/urandom of=$random_file bs=1k count=${CHIP_SIZE_KB}"<br>+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -w $random_file"<br>+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -r $cmp_file"<br>+ scmd $DO_REMOTE "cmp $random_file $cmp_file"<br>+ if [ $? -ne 0 ]; then<br>+ write_backup_image<br>+ test_fail "Failed to clobber entire ROM."<br>+ fi<br>+ scmd $DO_REMOTE "rm -f $cmp_file $random_file"<br>+<br>+ partial_write_test "RW"<br>+ if [ $? -ne 0 ]; then<br>+ echo "Layout mode test failed"<br>+ RC=$EXIT_FAILURE<br>+ fi<br>+elif [ $REGION_MODE -eq $REGION_MODE_DESCRIPTOR ]; then<br>+ # FIXME: This depends on descriptor regions being translated into internal<br>+ # layout representation automatically so we can target them using -i.<br>+ echo "TODO: Descriptor mode"<br>+ exit $EXIT_FAILURE<br>+elif [ $REGION_MODE -eq $REGION_MODE_FLASHMAP ]; then<br>+ partial_write_test "$FLASHMAP_REGION" 0<br>+ if [ $? -ne 0 ]; then<br>+ echo "Flashmap mode test failed"<br>+ RC=$EXIT_FAILURE<br>+ fi<br>+elif [ $REGION_MODE -eq $REGION_MODE_LAYOUT ]; then<br>+ rw_layout=""<br>+ addr=""<br>+ end=""<br>+ size=""<br>+<br>+ # Look for a region named "RW" with any amount of leading whitespace<br>+ # and no trailing whitespace or characters.<br>+ rw_layout=$(grep "\s${LAYOUT_REGION}$" $LAYOUT_FILE | head -n 1)<br>+ if [ -z "$rw_layout" ]; then<br>+ printf "No region matching \"${LAYOUT_REGION}\" found layout file \"%s\"\n" "$LAYOUT_FILE"<br>+ test_fail ""<br>+ fi<br>+<br>+ addr="0x$(echo "$rw_layout" | cut -d ' ' -f -1 | awk -F ':' '{ print $1 }')"<br>+ end="0x$(echo "$rw_layout" | cut -d ' ' -f -1 | awk -F ':' '{ print $2 }')"<br>+ size="$(($end - $addr + 1))"<br>+<br>+ printf "\"$LAYOUT_REGION\" region address: ${addr}, size: %u KiB\n" $(($size / $K))<br>+ partial_write_test "$LAYOUT_REGION"<br>+ if [ $? -ne 0 ]; then<br>+ echo "Layout mode test failed"<br>+ RC=$EXIT_FAILURE<br>+ fi<br>+fi<br>+<br>+# restore and cleanup<br>+write_backup_image<br>+do_cleanup<br>+<br>+if [ $RC -eq 0 ]; then<br>+ echo "Test status: PASS"<br>+else<br>+ echo "Test status: FAIL"<br>+fi<br>+<br>+exit $RC<br>-- <br>2.6.0</div></div>