From 353aa26a174d9beb1ec886f7f752473f94023ed8 Mon Sep 17 00:00:00 2001
From: David Hendricks <
dhendrix@chromium.org>
Date: Sun, 19 Jun 2016 12:53:22 -0700
Subject: [PATCH] WIP: New test script for flashrom
** work in progress **
Shiny new testing capabilities for Flashrom:
- Region awareness
- Remote testing
- Primary and secondary programmer support
- Autotest-friendly
Long-term goals:
- Performance measurement
- Endurance testing
BUG=chromium:621715
BRANCH=none
TEST=this *is* the test
Change-Id: Ic643cf2edaf7d8772c63ce8119363d882d1b116d
Signed-off-by: David Hendricks <
dhendrix@chromium.org>
---
tests/tests_v2/cmd.sh | 109 ++++++
tests/tests_v2/test_v2.sh | 909 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 1018 insertions(+)
create mode 100644 tests/tests_v2/cmd.sh
create mode 100644 tests/tests_v2/test_v2.sh
diff --git a/tests/tests_v2/cmd.sh b/tests/tests_v2/cmd.sh
new file mode 100644
index 0000000..443ee2c
--- /dev/null
+++ b/tests/tests_v2/cmd.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+#
+# This file is part of the flashrom project. It is derived from
+# board_status.sh in coreboot.
+#
+# Copyright (C) 2016 Google Inc.
+# Copyright (C) 2014 Sage Electronic Engineering, LLC.
+
+# test a command
+#
+# $1: 0 ($LOCAL) to run command locally,
+# 1 ($REMOTE) to run remotely if remote host defined
+# $2: command to test
+# $3: 0 ($FATAL) Exit with an error if the command fails
+# 1 ($NONFATAL) Don't exit on command test failure
+test_cmd()
+{
+ local rc
+ local cmd__="$(echo $2 | cut -d ' ' -f -1)"
+ local args="$(echo $2 | cut -d ' ' -f 2-)"
+
+ if [ -e "$cmd__" ]; then
+ return
+ fi
+
+ if [ "$1" -eq "$REMOTE" ] && [ -n "$REMOTE_HOST" ]; then
+ ssh $REMOTE_PORT_OPTION root@${REMOTE_HOST} command -v "$cmd__" $args > /dev/null 2>&1
+ rc=$?
+ else
+ command -v "$cmd__" $args >/dev/null 2>&1
+ rc=$?
+ fi
+
+ if [ $rc -eq 0 ]; then
+ return 0
+ fi
+
+ if [ "$3" = "1" ]; then
+ return 1
+ fi
+
+ echo "$2 not found"
+ exit $EXIT_FAILURE
+}
+
+# Same args as cmd() but with the addition of $4 which determines if the
+# command should be totally silenced or not.
+_cmd()
+{
+ local silent=$4
+
+ if [ -n "$3" ]; then
+ pipe_location="${3}"
+ else
+ pipe_location="/dev/null"
+ fi
+
+ if [ $1 -eq $REMOTE ] && [ -n "$REMOTE_HOST" ]; then
+ if [ $silent -eq 0 ]; then
+ ssh $REMOTE_PORT_OPTION "root@${REMOTE_HOST}" "$cmd $args" > "$pipe_location" 2>/dev/null
+ else
+ ssh $REMOTE_PORT_OPTION "root@${REMOTE_HOST}" "$cmd $args" > /dev/null 2>&1
+ fi
+ else
+ if [ $silent -eq 0 ]; then
+ $2 > "$pipe_location" 2>/dev/null
+ else
+ $2 > /dev/null 2>&1
+ fi
+ fi
+
+ return $?
+}
+
+# run a command
+#
+# $1: 0 ($LOCAL) to run command locally,
+# 1 ($REMOTE) to run remotely if remote host defined
+# $2: command
+# $3: filename to direct output of command into
+cmd()
+{
+ _cmd $1 "$2" "$3" 0
+
+ if [ $? -eq 0 ]; then
+ return
+ fi
+
+ echo "Failed to run \"$2\", aborting"
+ rm -f "$3" # don't leave an empty file
+ exit $EXIT_FAILURE
+}
+
+# run a command silently
+#
+# $1: 0 ($LOCAL) to run command locally,
+# 1 ($REMOTE) to run remotely if remote host defined
+# $2: command
+scmd()
+{
+ _cmd $1 "$2" "" 1
+
+ if [ $? -eq 0 ]; then
+ return
+ fi
+
+ echo "Failed to run \"$2\", aborting"
+ exit $EXIT_FAILURE
+}
diff --git a/tests/tests_v2/test_v2.sh b/tests/tests_v2/test_v2.sh
new file mode 100644
index 0000000..5a8cb79
--- /dev/null
+++ b/tests/tests_v2/test_v2.sh
@@ -0,0 +1,909 @@
+#!/bin/sh
+#
+# Copyright (C) 2016 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+RC=$EXIT_SUCCESS
+FATAL=0
+NONFATAL=1
+
+# For old flashrom version, if -o is not specified
+DEFAULT_FLASHROM_PATH="/usr/local/sbin/flashrom"
+
+#
+# Stuff obtained from command-line
+#
+
+# Generic options
+BACKUP_IMAGE=""
+OLD_FLASHROM=""
+NEW_FLASHROM=""
+NO_CLEAN=0
+SKIP_CONSISTENCY_CHECK=0
+UPLOAD_RESULTS=0
+
+# Primary/Secondary programmer options
+PRIMARY_OPTS=""
+SECONDARY_OPTS=""
+
+# Region modes
+REGION_MODE_UNKNOWN=0
+REGION_MODE_CLOBBER=1
+REGION_MODE_DESCRIPTOR=2
+REGION_MODE_FLASHMAP=3
+REGION_MODE_LAYOUT=4
+REGION_MODE=$REGION_MODE_UNKNOWN
+DESCRIPTOR_REGION="bios"
+FLASHMAP_REGION="RW_SECTION_A"
+LAYOUT_FILE=""
+LAYOUT_REGION="RW"
+SMALL_REGION=0
+
+# Remote testing options
+SSH_PORT=""
+REMOTE_HOST=""
+REMOTE_PORT_OPTION=""
+REMOTE_ONLY=0
+REMOTE_PROGRAMMER_PARAMS=""
+SSH_CMD="ssh $REMOTE_PORT_OPTION root@${REMOTE_HOST} command -v"
+
+LOCAL=0
+REMOTE=1
+DO_REMOTE=$LOCAL # boolean to use for cmd() and tests
+
+# For suppressing flashrom output
+SILENT=">/dev/null 2>&1"
+
+# 1KB
+K="1024"
+
+show_help() {
+ echo "Usage:
+ ${0} <options>
+
+General options:
+ -b, --backup-image
+ Backup image to write unconditionally at end of testing.
+ -h, --help
+ Show this message.
+ -l, --layout-file
+ Layout file (required if mode is \"layout\", resides locally).
+ -m, --mode <arg>
+ Region access mode (clobber, descriptor, flashmap, layout).
+ -n, --new <path>
+ Path to new version of flashrom.
+ -o, --old <path>
+ Path to old version of flashrom.
+ -p, --primary-programmer <parameters>
+ Primary programmer options.
+ -r, --remote-host <host>
+ Remote host to test primary programmer on.
+ -s, --secondary-programmer <parameters>
+ Secondary programmer options.
+ -u, --upload-results
+ Upload results to
flashrom.org.
+
+Long options:
+ --descriptor-region
+ Specify region to use in descriptor mode (default: $DESCRIPTOR_REGION)
+ --fmap-region
+ Specify region to use in flashmap mode (default: $FLASHMAP_REGION)
+ --layout-region
+ Specify region to use in layout mode (default: $LAYOUT_REGION)
+ --no-clean
+ Do not remove temporary files.
+ --skip-consistency-check
+ Skip the consistency check (two consecutive reads) at beginning.
+ --small-region
+ Omit tests that require large amounts of space (>16KB).
+
+Remote connectivity options:
+ --ssh-port <port>
+ Use a specific SSH port.
+
+This script is intended to cover two possible test scenarios:
+1. Test flashrom using primary programmer only (default).
+2. Test flashrom using primary programmer and verify result using
+ a secondary programmer, for example, an external USB programmer.
+
+If a secondary programmer is provided, certain steps will be verified
+using the secondary programmer. The secondary programmer must be
+connected to the machine which is running this script.
+
+If a remote host is provided, this script will SSH into the remote
+host and run tests using the primary programmer interface parameters
+provided.
+
+See documentation for usage examples (TODO: write documentation)
+"
+}
+
+getopt -T
+if [ $? -ne 4 ]; then
+ echo "GNU-compatible getopt(1) required."
+ exit $EXIT_FAILURE
+fi
+
+LONGOPTS="backup-image:,help,,new:,old:,remote-host:,upload-results:"
+LONGOPTS="${LONGOPTS},primary-programmer:,secondary-programmer:"
+LONGOPTS="${LONGOPTS},skip-consistency-check"
+LONGOPTS="${LONGOPTS},mode:,clobber,descriptor,flashmap,layout,small-region"
+LONGOPTS="${LONGOPTS},layout-file:,desctriptor-region:,flashmap-region:,layout-region:"
+LONGOPTS="${LONGOPTS},no-clean"
+LONGOPTS="${LONGOPTS},ssh-port:"
+
+ARGS=$(getopt -o b:hl:m:n:o:p:r:s:u -l "$LONGOPTS" -n "$0" -- "$@");
+if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
+eval set -- "$ARGS"
+while true ; do
+ case "$1" in
+ # Generic options
+ -b|--backup-image)
+ shift
+ BACKUP_IMAGE="$1"
+ ;;
+ -h|--help)
+ show_help
+ exit $EXIT_SUCCESS
+ ;;
+ -l|--layout-file)
+ shift
+ LAYOUT_FILE="$1"
+ ;;
+ -m|--mode)
+ shift
+ if [ "$1" == "clobber" ]; then
+ REGION_MODE=$REGION_MODE_CLOBBER
+ elif [ "$1" == "descriptor" ]; then
+ REGION_MODE=$REGION_MODE_DESCRIPTOR
+ elif [ "$1" == "flashmap" ]; then
+ REGION_MODE=$REGION_MODE_FLASHMAP
+ elif [ "$1" == "layout" ]; then
+ REGION_MODE=$REGION_MODE_LAYOUT
+ else
+ echo "Unknown mode: $1"
+ exit $EXIT_FAILURE
+ fi
+ ;;
+ -n|--new)
+ shift
+ NEW_FLASHROM="$1"
+ ;;
+ -o|--old)
+ shift
+ OLD_FLASHROM="$1"
+ ;;
+ -p|--primary_programmer)
+ shift
+ PRIMARY_OPTS="-p $1"
+ ;;
+ -s|--secondary_programmer)
+ shift
+ SECONDARY_OPTS="-p $1"
+ ;;
+ -r|--remote-host)
+ DO_REMOTE=1
+ shift
+ REMOTE_HOST="$1"
+ ;;
+ -u|--upload-results)
+ UPLOAD_RESULTS=1
+ ;;
+
+ # Longopts only
+ --descriptor-region)
+ shift
+ DESCRIPTOR_REGION="$1"
+ ;;
+ --flashmap-region)
+ shift
+ FLASHMAP_REGION="$1"
+ ;;
+ --layout-region)
+ shift
+ LAYOUT_REGION="$1"
+ ;;
+ --no-clean)
+ NO_CLEAN=1
+ ;;
+ --skip-consistency-check)
+ SKIP_CONSISTENCY_CHECK=1
+ ;;
+
+ # Remote testing options
+ --ssh-port)
+ shift
+ REMOTE_PORT_OPTION="-p $1"
+ ;;
+
+ # error handling
+ --)
+ shift
+ if [ -n "$*" ]; then
+ echo "Non-option parameters detected: '$*'"
+ exit $EXIT_FAILURE
+ fi
+ break
+ ;;
+ *)
+ echo "error processing options at '$1'"
+ exit $EXIT_FAILURE
+ esac
+ shift
+done
+
+# TODO: Implement this.
+if [ $UPLOAD_RESULTS -eq 1 ]; then
+ echo "TODO: Implement ability to upload results."
+ exit $EXIT_FAILURE
+fi
+
+#
+# Test command-line validity.
+#
+if [ $REGION_MODE -eq $REGION_MODE_UNKNOWN ]; then
+ echo "Must specify a region access mode (-m/--mode)."
+ exit $EXIT_FAILURE
+elif [ $REGION_MODE -eq $REGION_MODE_LAYOUT ]; then
+ if [ ! -e "$LAYOUT_FILE" ]; then
+ echo "Must specify a layout file when using layout mode."
+ exit $EXIT_FAILURE
+ fi
+fi
+
+#
+# Source helper scripts
+#
+export REMOTE_HOST REMOTE_PORT_OPTION
+export LOCAL REMOTE FATAL NONFATAL EXIT_SUCCESS EXIT_FAILURE
+. "$(pwd)/tests/tests_v2/cmd.sh"
+
+if [ $DO_REMOTE -eq 1 ]; then
+ # Test connection to remote host
+ test_cmd $DO_REMOTE "ls /" $NONFATAL
+ if [ $? -ne 0 ]; then
+ echo "Could not connect to remote host $REMOTE_HOST"
+ exit $EXIT_FAILURE
+ fi
+fi
+
+#
+# Dependencies
+#
+
+# cmp is used to compare files
+test_cmd $DO_REMOTE "cmp" $FATAL
+
+if [ ! -e "/dev/urandom" ]; then
+ echo "This script uses /dev/urandom"
+ exit $EXIT_FAILURE
+fi
+
+if [ ! -e "/dev/zero" ]; then
+ echo "This script uses /dev/zero"
+ exit $EXIT_FAILURE
+fi
+
+#
+# Setup.
+#
+grep -rH 'projectname = .*flashrom' .git/config >/dev/null 2>&1
+if [ $? -ne 0 ]; then
+ echo "Script must be run from root of flashrom directory"
+ exit $EXIT_FAILURE
+fi
+
+if [ -z "$OLD_FLASHROM" ]; then
+ OLD_FLASHROM="$DEFAULT_FLASHROM_PATH"
+fi
+test_cmd $DO_REMOTE "$OLD_FLASHROM --help" $NONFATAL
+if [ $? -ne 0 ]; then
+ echo "Old flashrom binary is not usable."
+ exit $EXIT_FAILURE
+fi
+
+#
+# Setup temporary working directories:
+# LOCAL_TMPDIR: Working directory on local host.
+# REMOTE_TMPDIR: Working directory on remote host.
+# TMPDIR: The temporary directy in which we do most of the work. This is
+# convenient for commands that depend on $DO_REMOTE.
+LOCAL_TMPDIR=$(mktemp -d --tmpdir flashrom_test.XXXXXXXX)
+if [ $? -ne 0 ] ; then
+ echo "Could not create temporary directory"
+ exit $EXIT_FAILURE
+fi
+
+if [ $DO_REMOTE -eq 1 ]; then
+ REMOTE_TMPDIR=$(ssh root@${REMOTE_HOST} mktemp -d --tmpdir flashrom_test.XXXXXXXX)
+ if [ $? -ne 0 ] ; then
+ echo "Could not create temporary directory"
+ exit $EXIT_FAILURE
+ fi
+fi
+
+if [ $DO_REMOTE -eq 0 ]; then
+ TMPDIR="$LOCAL_TMPDIR"
+else
+ TMPDIR="$REMOTE_TMPDIR"
+fi
+
+# Copy files from local tmpdir to remote host tmpdir
+copy_to_remote()
+{
+ for F in $@; do
+ # FIXME: quoting for REMOTE_TMPDIR?
+ scp "${LOCAL_TMPDIR}/$F" root@${REMOTE_HOST}:${REMOTE_TMPDIR} 2>&1 >/dev/null
+ done
+}
+
+# Copy files from remote host tmpdir to local tmpdir
+copy_from_remote()
+{
+ for F in $@; do
+ # FIXME: quoting for REMOTE_TMPDIR?
+ scp root@${REMOTE_HOST}:${REMOTE_TMPDIR}/$F "${LOCAL_TMPDIR}/" 2>/dev/null
+ done
+}
+
+# Read current image as backup in case one hasn't already been specified.
+if [ -z "$BACKUP_IMAGE" ]; then
+ local backup_file="backup.bin"
+ if [ $DO_REMOTE -eq 1 ]; then
+ BACKUP_IMAGE="${REMOTE_TMPDIR}/${backup_file}"
+ else
+ BACKUP_IMAGE="${LOCAL_TMPDIR}/${backup_file}"
+ fi
+
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -r $BACKUP_IMAGE"
+ if [ $? -ne 0 ]; then
+ echo "Failed to read backup image, aborting."
+ exit $EXIT_FAILURE
+ fi
+
+ if [ $DO_REMOTE -eq 1 ]; then
+ copy_from_remote "${backup_file}"
+ fi
+fi
+
+# The copy of flashrom to test. If unset, we'll assume the user wants to test
+# a newly built flashrom binary in the current directory.
+if [ -z "$NEW_FLASHROM" ] ; then
+ if [ -x "flashrom" ]; then
+ NEW_FLASHROM="flashrom"
+ else
+ echo "Must supply new flashrom version to test"
+ exit $EXIT_FAILURE
+ fi
+fi
+
+echo "Stable flashrom binary: $OLD_FLASHROM"
+echo "New flashrom binary to test: $NEW_FLASHROM"
+echo "Local temporary files will be stored in $LOCAL_TMPDIR"
+if [ $DO_REMOTE -eq 1 ]; then
+ echo "Remote temporary files will be stored in ${REMOTE_HOST}:${REMOTE_TMPDIR}"
+fi
+echo "Backup image: $BACKUP_IMAGE"
+
+#
+# Now the fun begins.
+#
+cmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS --get-size" "${LOCAL_TMPDIR}/chip_size.txt"
+tmp=$(cat ${LOCAL_TMPDIR}/chip_size.txt)
+cmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS --get-size" "${LOCAL_TMPDIR}/chip_size.txt"
+CHIP_SIZE=$(cat ${LOCAL_TMPDIR}/chip_size.txt)
+CHIP_SIZE_KB=$(($CHIP_SIZE / 1024))
+if [ $CHIP_SIZE -ne $tmp ]; then
+ echo "New flashrom and old flashrom disagree on chip size. Aborting."
+ exit $EXIT_FAILURE
+else
+ echo "Chip size: ${CHIP_SIZE_KB} KiB"
+fi
+
+# Upload results
+#do_upload()
+#{
+# # TODO: implement this
+#}
+
+# Remove temporary files
+do_cleanup()
+{
+ if [ $NO_CLEAN -eq 1 ]; then
+ echo "Skipping cleanup."
+ return $EXIT_SUCCESS
+ fi
+
+ rm -rf "$LOCAL_TMPDIR"
+ if [ -n "$REMOTE_HOST" ]; then
+ ssh root@${REMOTE_HOST} rm -rf "${REMOTE_TMPDIR}"
+ fi
+
+ return $EXIT_SUCCESS
+}
+
+# $1: Message to display to user.
+test_fail()
+{
+ echo "$1"
+ do_cleanup
+ exit $EXIT_FAILURE
+}
+
+write_backup_image()
+{
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -w $BACKUP_IMAGE"
+}
+
+# Read a region twice and compare results
+# $1: address of region (in bytes)
+# $2: length of region (in bytes)
+double_read_test()
+{
+ local cmp1="${TMPDIR}/cmp1.bin"
+ local cmp2="${TMPDIR}/cmp2.bin"
+ local layout="double_read_test_layout.txt"
+
+ printf "Doing double read test, size: %u KiB\n" $(($2 / $K))
+ # FIXME: Figure out how to do printf remotely...
+ printf "%06x:%06x region\n" $1 $(($1 + $2 - 1)) > "${LOCAL_TMPDIR}/${layout}"
+ if [ $DO_REMOTE -eq 1 ]; then copy_to_remote "$layout" ; fi
+
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -r -l ${TMPDIR}/${layout} --ignore-fmap -i region:$cmp1"
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -r -l ${TMPDIR}/${layout} --ignore-fmap -i region:$cmp2"
+ scmd $DO_REMOTE "cmp $cmp1 $cmp2"
+ if [ $? -ne 0 ]; then
+ test_fail "Double-read test failed, aborting."
+ fi
+}
+
+# Simple partial write test. Given a region:
+# - Write a sub-region within a single eraseable block.
+# - Write a sub-region that crosses over into the next eraseable block.
+#
+# We assume that eraseable block size can be either 4KB or 64KB and
+# must test for both. This means the given region must be at least 128KB.
+#
+# $1: region addr
+# $2: region size (in bytes)
+simple_partial_write_test()
+{
+ local addr="$1"
+ local size="$2"
+ local layout="simple_partial_write_test_layout.txt"
+ local random_2k_subregion="simple_partial_write_test_random_2k_subregion.bin"
+ local random_2k_crossover="simple_partial_write_test_random_2k_crossover.bin"
+ local random_32k_subregion="simple_partial_write_test_random_32k_subregion.bin"
+ local random_32k_crossover="simple_partial_write_test_random_32k_crossover.bin"
+ local whole_region="simple_partial_write_test_whole_region.bin"
+ local opts="--ignore-fmap -l ${TMPDIR}/${layout}"
+
+# FIXME: integer/string conversion
+# if [ $size -lt 131072 ]; then
+# test_fail "Region is not big enough for partial write test."
+# fi
+#
+# if [ $(($addr + $size)) -gt $CHIP_SIZE ]; then
+# test_fail "Region exceeds chip size."
+# fi
+
+ # FIXME: For now, assume the given region is aligned to 64KB.
+
+ printf "Doing partial write test\n"
+
+ # Create a layout file for regions we're interested in.
+ # FIXME: figure out how to do this using cmd
+ printf "%06x:%06x 4k_region\n" $addr $(($addr + 4*$K - 1)) >> "${LOCAL_TMPDIR}/${layout}"
+ printf "%06x:%06x 2k_subregion\n" $(($addr + $K)) $(($addr + 4*$K - $K - 1)) >> "${LOCAL_TMPDIR}/${layout}"
+ printf "%06x:%06x 2k_crossover\n" $(($addr + 4*$K - $K)) $(($addr + 4*$K + $K - 1)) >> "${LOCAL_TMPDIR}/${layout}"
+
+ printf "%06x:%06x 64k_region\n" $addr $(($addr + 64*$K - 1)) >> "${LOCAL_TMPDIR}/${layout}"
+ printf "%06x:%06x 32k_subregion\n" $(($addr + 16*$K)) $(($addr + 64*$K - 16*$K - 1)) >> "${LOCAL_TMPDIR}/${layout}"
+ printf "%06x:%06x 32k_crossover\n" $(($addr + 64*$K - 16*$K)) $(($addr + 64*$K + 16*$K - 1)) >> "${LOCAL_TMPDIR}/${layout}"
+
+ printf "%06x:%06x whole" $addr $(($addr + $size - 1)) >> "${LOCAL_TMPDIR}/${layout}"
+
+ dd if=/dev/urandom of=${LOCAL_TMPDIR}/${random_2k_subregion} bs=1k count=2 2>/dev/null
+ dd if=/dev/urandom of=${LOCAL_TMPDIR}/${random_2k_crossover} bs=1k count=2 2>/dev/null
+ dd if=/dev/urandom of=${LOCAL_TMPDIR}/${random_32k_subregion} bs=1k count=32 2>/dev/null
+ dd if=/dev/urandom of=${LOCAL_TMPDIR}/${random_32k_crossover} bs=1k count=32 2>/dev/null
+
+ # Example for creating a file filled with 0xaa bytes:
+ # dd if=/dev/zero bs=1k count=2 | tr "\000" "\252" > "aa_2K.bin"
+ # Example to create file with 0xff bytes:
+ # dd if=/dev/zero bs=1k count=2 | tr "\000" "\377" > "ff_2k.bin"
+
+ if [ $DO_REMOTE -eq 1 ]; then
+ copy_to_remote "${layout}"
+ copy_to_remote "${random_2k_subregion}" "${random_2k_crossover}"
+ copy_to_remote "${random_32k_subregion}" "${random_32k_crossover}"
+ fi
+
+ # Create backup of region. This will be modified along with the ROM content,
+ # read back, and compared.
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -r -i whole:${TMPDIR}/${whole_region}"
+
+ scmd $DO_REMOTE "dd if=${TMPDIR}/${random_2k_subregion} of=${TMPDIR}/${whole_region} bs=1 seek=$K conv=notrunc"
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -w --fast-verify -i 2k_subregion:${TMPDIR}/${random_2k_subregion}"
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -r -i 2k_subregion:${TMPDIR}/cmp.bin"
+ scmd $DO_REMOTE "cmp ${TMPDIR}/${random_2k_subregion} ${TMPDIR}/cmp.bin"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify partial write to 2k subregion."
+ else
+ printf "\t2k subregion test passed\n"
+ fi
+
+ scmd $DO_REMOTE "dd if=${TMPDIR}/${random_2k_crossover} of=${TMPDIR}/${whole_region} bs=1 seek=$((4*$K - $K)) conv=notrunc"
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -w --fast-verify -i 2k_crossover:${TMPDIR}/${random_2k_crossover}"
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -r -i 2k_crossover:${TMPDIR}/cmp.bin"
+ scmd $DO_REMOTE "cmp ${TMPDIR}/${random_2k_crossover} ${TMPDIR}/cmp.bin"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify partial write to 2k crossover region."
+ else
+ printf "\t2k crossover region test passed\n"
+ fi
+
+ if [ $SMALL_REGION -eq 0 ]; then
+ scmd $DO_REMOTE "dd if=${TMPDIR}/${random_32k_subregion} of=${TMPDIR}/${whole_region} bs=1 seek=$((16*$K)) conv=notrunc"
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -w --fast-verify -i 32k_subregion:${TMPDIR}/${random_32k_subregion}"
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -r -i 32k_subregion:${TMPDIR}/cmp.bin"
+ scmd $DO_REMOTE "cmp ${TMPDIR}/${random_32k_subregion} ${TMPDIR}/cmp.bin"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify partial write to 32k subregion."
+ else
+ printf "\t32k subregion test passed\n"
+ fi
+
+ scmd $DO_REMOTE "dd if=${TMPDIR}/${random_32k_crossover} of=${TMPDIR}/${whole_region} bs=1 seek=$((64*$K - 16*$K)) conv=notrunc"
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -w --fast-verify -i 32k_crossover:${TMPDIR}/${random_32k_crossover}"
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -r -i 32k_crossover:${TMPDIR}/cmp.bin"
+ scmd $DO_REMOTE "cmp ${TMPDIR}/${random_32k_crossover} ${TMPDIR}/cmp.bin"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify partial write to 32k crossover region."
+ else
+ printf "\t32k crossover region test passed\n"
+ fi
+ fi
+
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -r -i whole:${TMPDIR}/cmp.bin"
+ scmd $DO_REMOTE "cmp ${TMPDIR}/${whole_region} ${TMPDIR}/cmp.bin"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify correctness of final image."
+ fi
+
+ if [ -n "$SECONDARY_ARGS" ]; then
+ if [ $DO_REMOTE -eq 1 ]; then
+ copy_from_remote "${whole_region}"
+ fi
+
+ scmd $LOCAL "$OLD_FLASHROM $SECONDARY_OPTS $opts -r -i whole:${LOCAL_TMPDIR}/cmp.bin"
+ scmd $LOCAL "cmp ${LOCAL_TMPDIR}/${whole_region} ${LOCAL_TMPDIR}/cmp.bin"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify correctness of final image."
+ fi
+ fi
+
+ return $EXIT_SUCCESS
+}
+
+# Regional partial write test. Given a region name, this will write patterns
+# of bytes designed to test corner cases.
+#
+# We assume that eraseable block size can be either 4KB or 64KB and
+# must test for both. For simplicity, we'll assume the region size is
+# at least 256KB.
+#
+# $1: Region name
+region_partial_write_test()
+{
+ local opts="--fast-verify"
+ local layout="region_partial_write_test_layout.txt"
+ local region_size=0
+ local align_size_kb=0
+ local region_name="$1"
+ local filename=""
+ local offset_kb=0
+ local size_kb=0
+ local test_num=0
+ local prev_test_num=0
+
+ # FIXME: Sanity checking:
+ # - Region must be aligned to 64KB
+ # - Region must be at least 128KB in size.
+
+ if [ $REGION_MODE -ne $REGION_MODE_FLASHMAP ]; then
+ opts="$opts --ignore-fmap"
+ fi
+
+ if [ $SMALL_REGION -eq 1 ]; then
+ align_size_kb=16
+ else
+ align_size_kb=256
+ fi
+
+ echo "Doing region-based partial write test on region \"$region_name\""
+ filename="${TMPDIR}/${region_name}.bin"
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -r -i ${region_name}:${filename}"
+ copy_from_remote "${region_name}.bin"
+ region_size=$(stat --format=%s ${LOCAL_TMPDIR}/${region_name}.bin)
+
+ if [ $region_size -lt $(($align_size_kb * $K)) ]; then
+ echo "Region $region_name is too small"
+ return $EXIT_FAILURE
+ fi
+
+ if [ $(($region_size % $(($align_size_kb * $K)))) -ne 0 ]; then
+ echo "Region $region_name is not aligned to ${align_size}KB"
+ return $EXIT_FAILURE
+ fi
+
+ # Test procedure:
+ # Clobber region with random content first. Then do writes using the
+ # following sequences for each 128KB:
+ # 0-2K : 0x00 (\000) Partial 4KB sector, lower half
+ # 2K-6K : 0x11 (\021) Crossover 4KB sector boundary
+ # 6K-8K : 0x22 (\042) Partial 4KB sector, upper half
+ # 8K-16K : 0x33 (\063) Full 4KB sectors
+ #
+ # Repeat the above sequence for 64KB-aligned sizes
+ # 0-32K : 0x44 (\104) Partial 64KB block, lower half
+ # 32K-96K : 0x55 (\125) Crossover 64KB block boundary
+ # 96K-128K : 0x66 (\146) Partial 64KB block, upper half
+ # 128K-256K : 0x77 (\167) Full 64KB blocks
+
+ test_num=0
+ dd if=/dev/urandom of="${LOCAL_TMPDIR}/random_4k_test.bin" bs=4k count=$(($region_size / $((4 * $K)))) 2>/dev/null
+
+ # 0-2K : 0x00 (\000) Partial 4KB sector, lower half
+ offset_kb=0
+ size_kb=2
+ hex="0x00"
+ oct="\\$(printf "%03o" $hex)"
+ cp "${LOCAL_TMPDIR}/random_4k_test.bin" "${LOCAL_TMPDIR}/4k_test_${test_num}.bin"
+ while [ $(($offset_kb * $K)) -lt $region_size ]; do
+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"
+ 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
+ offset_kb=$(($offset_kb + $align_size_kb))
+ done
+ prev_test_num=$test_num
+ test_num=$(($test_num + 1))
+
+ # 2K-6K : 0x11 (\021) Crossover 4KB sector boundary
+ offset_kb=2
+ size_kb=4
+ hex="0x11"
+ oct="\\$(printf "%03o" $hex)"
+ cp "${LOCAL_TMPDIR}/4k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/4k_test_${test_num}.bin"
+ while [ $(($offset_kb * $K)) -lt $region_size ]; do
+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"
+ 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
+ offset_kb=$(($offset_kb + $align_size_kb))
+ done
+ test_num=$(($test_num + 1))
+
+ # 6K-8K : 0x22 (\042) Partial 4KB sector, upper half
+ offset_kb=2
+ size_kb=4
+ hex="0x11"
+ oct="\\$(printf "%03o" $hex)"
+ cp "${LOCAL_TMPDIR}/4k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/4k_test_${test_num}.bin"
+ while [ $(($offset_kb * $K)) -lt $region_size ]; do
+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"
+ 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
+ offset_kb=$(($offset_kb + $align_size_kb))
+ done
+ test_num=$(($test_num + 1))
+
+ # 8K-16K : 0x33 (\063) Full 4KB sectors
+ offset_kb=8
+ size_kb=8
+ hex="0x22"
+ oct="\\$(printf "%03o" $hex)"
+ cp "${LOCAL_TMPDIR}/4k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/4k_test_${test_num}.bin"
+ while [ $(($offset_kb * $K)) -lt $region_size ]; do
+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"
+ 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
+ offset_kb=$(($offset_kb + $align_size_kb))
+ done
+ test_num=$(($test_num + 1))
+
+ for F in ${LOCAL_TMPDIR}/random_4k_test.bin ${LOCAL_TMPDIR}/4k_test_*.bin ; do
+ filename=$(basename $F)
+ if [ $DO_REMOTE -eq 1 ]; then
+ copy_to_remote $filename
+ fi
+
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -w -i ${region_name}:${TMPDIR}/${filename}"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to write $filename to $region_name"
+ fi
+
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -v -i ${region_name}:${TMPDIR}/${filename}"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify write of $filename to $region_name"
+ fi
+
+ if [ -n "$SECONDARY_OPTS" ]; then
+ scmd $LOCAL "$OLD_FLASHROM $SECONDARY_OPTS $opts -v -i ${region_name}:${TMPDIR}/${filename}"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify write of $filename to $region_name using secondary programmer"
+ fi
+ fi
+
+ printf "\tWrote $filename to $region_name region successfully.\n"
+ done
+
+ if [ $SMALL_REGION -eq 1 ]; then
+ return $EXIT_SUCCESS
+ fi
+
+ #
+ # Second half: Tests for 64KB chunks
+ #
+ test_num=0
+ dd if=/dev/urandom of="${LOCAL_TMPDIR}/random_64k_test.bin" bs=128k count=$(($region_size / $((128*$K)))) 2>/dev/null
+
+ # 0-32K : 0x44 (\104) Partial 64KB block, lower half
+ offset_kb=0
+ size_kb=32
+ hex="0x44"
+ oct="\\$(printf "%03o" $hex)"
+ cp "${LOCAL_TMPDIR}/random_64k_test.bin" "${LOCAL_TMPDIR}/64k_test_${test_num}.bin"
+ while [ $(($offset_kb * $K)) -lt $region_size ]; do
+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"
+ 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
+ offset_kb=$(($offset_kb + $align_size_kb))
+ done
+ prev_test_num=$test_num
+ test_num=$(($test_num + 1))
+
+ # 32K-96K : 0x55 (\125) Crossover 64KB block boundary
+ offset_kb=32
+ size_kb=64
+ hex="0x55"
+ oct="\\$(printf "%03o" $hex)"
+ cp "${LOCAL_TMPDIR}/64k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/64k_test_${test_num}.bin"
+ while [ $(($offset_kb * $K)) -lt $region_size ]; do
+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"
+ 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
+ offset_kb=$(($offset_kb + $align_size_kb))
+ done
+ test_num=$(($test_num + 1))
+
+ # 96K-128K : 0x66 (\146) Partial 64KB block, upper half
+ offset_kb=96
+ size_kb=32
+ hex="0x66"
+ oct="\\$(printf "%03o" $hex)"
+ cp "${LOCAL_TMPDIR}/64k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/64k_test_${test_num}.bin"
+ while [ $(($offset_kb * $K)) -lt $region_size ]; do
+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"
+ 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
+ offset_kb=$(($offset_kb + $align_size_kb))
+ done
+ test_num=$(($test_num + 1))
+
+ # 128K-256K : 0x77 (\167) Full 64KB blocks
+ offset_kb=128
+ size_kb=128
+ hex="0x77"
+ oct="\\$(printf "%03o" $hex)"
+ cp "${LOCAL_TMPDIR}/64k_test_${prev_test_num}.bin" "${LOCAL_TMPDIR}/64k_test_${test_num}.bin"
+ while [ $(($offset_kb * $K)) -lt $region_size ]; do
+ dd if=/dev/zero bs=1k count=$size_kb 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${size_kb}k.bin"
+ 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
+ offset_kb=$(($offset_kb + $align_size_kb))
+ done
+
+ for F in ${LOCAL_TMPDIR}/random_64k_test.bin ${LOCAL_TMPDIR}/64k_test_*.bin ; do
+ filename=$(basename $F)
+ if [ $DO_REMOTE -eq 1 ]; then
+ copy_to_remote $filename
+ fi
+
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -w -i ${region_name}:${TMPDIR}/${filename}"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to write $filename to $region_name"
+ fi
+
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -v -i ${region_name}:${TMPDIR}/${filename}"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify write of $filename to $region_name"
+ fi
+
+ if [ -n "$SECONDARY_OPTS" ]; then
+ scmd $LOCAL "$OLD_FLASHROM $SECONDARY_OPTS $opts -v -i ${region_name}:${TMPDIR}/${filename}"
+ if [ $? -ne 0 ]; then
+ test_fail "Failed to verify write of $filename to $region_name using secondary programmer"
+ fi
+ fi
+
+ printf "\tWrote $filename to $region_name region successfully.\n"
+ done
+
+ return $EXIT_SUCCESS
+}
+
+# Do a consistency check for sanity before any other test.
+if [ $SKIP_CONSISTENCY_CHECK -eq 0 ]; then
+ double_read_test 0 $CHIP_SIZE
+fi
+
+if [ $REGION_MODE -eq $REGION_MODE_CLOBBER ]; then
+ random_file="${TMPDIR}/random_${CHIP_SIZE_KB}K.bin"
+ cmp_file="${TMPDIR}/cmp.bin"
+
+ scmd $DO_REMOTE "dd if=/dev/urandom of=$random_file bs=1k count=${CHIP_SIZE_KB}"
+ scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -w $random_file"
+ scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -r $cmp_file"
+ scmd $DO_REMOTE "cmp $random_file $cmp_file"
+ if [ $? -ne 0 ]; then
+ write_backup_image
+ test_fail "Failed to clobber entire ROM."
+ fi
+ scmd $DO_REMOTE "rm -f $cmp_file $random_file"
+
+ simple_partial_write_test "0" "$CHIP_SIZE"
+elif [ $REGION_MODE -eq $REGION_MODE_DESCRIPTOR ]; then
+ # This depends on descriptor regions being translated into internal
+ # layout representation automatically so we can target them using -i.
+ echo "TODO: Descriptor mode"
+ exit $EXIT_FAILURE
+elif [ $REGION_MODE -eq $REGION_MODE_FLASHMAP ]; then
+ region_partial_write_test "$FLASHMAP_REGION" 0
+ if [ $? -ne 0 ]; then
+ echo "Flashmap mode test failed"
+ RC=$EXIT_FAILURE
+ fi
+elif [ $REGION_MODE -eq $REGION_MODE_LAYOUT ]; then
+ rw_layout=""
+ addr=""
+ end=""
+ size=""
+
+ # Look for a region named "RW" with any amount of leading whitespace
+ # and no trailing whitespace or characters.
+ rw_layout=$(grep "\s${LAYOUT_REGION}$" $LAYOUT_FILE | head -n 1)
+ if [ -z "$rw_layout" ]; then
+ printf "No region matching \"${LAYOUT_REGION}\" found layout file \"%s\"\n" "$LAYOUT_FILE"
+ exit $EXIT_FAILURE
+ fi
+
+ addr="0x$(echo "$rw_layout" | cut -d ' ' -f -1 | awk -F ':' '{ print $1 }')"
+ end="0x$(echo "$rw_layout" | cut -d ' ' -f -1 | awk -F ':' '{ print $2 }')"
+ size="$(($end - $addr + 1))"
+
+ printf "RW region address: ${addr}, size: %u KiB\n" $(($size / $K))
+ simple_partial_write_test "$addr" "$size"
+ if [ $? -ne 0 ]; then
+ echo "Flashmap mode test failed"
+ RC=$EXIT_FAILURE
+ fi
+fi
+
+# restore and cleanup
+write_backup_image
+do_cleanup
+
+if [ $RC -eq 0 ]; then
+ echo "Test status: PASS"
+else
+ echo "Test status: FAIL"
+fi
+
+exit $RC
--
2.6.0