xref: /aosp_15_r20/external/flashrom/util/ubertest/ubertest.sh (revision 0d6140be3aa665ecc836e8907834fcd3e3b018fc)
1#!/bin/sh
2#
3# Copyright (C) 2016 Google Inc.
4# Copyright (C) 2020 Facebook Inc.
5#
6# This program is free software; you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation; either version 2 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program; if not, write to the Free Software
18# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19
20EXIT_SUCCESS=0
21EXIT_FAILURE=1
22RC=$EXIT_SUCCESS
23FATAL=0
24NONFATAL=1
25
26#
27# Stuff obtained from command-line
28#
29
30# Generic options
31BACKUP_IMAGE=""
32OLD_FLASHROM=""
33NEW_FLASHROM=""
34NO_CLEAN=0
35SKIP_CONSISTENCY_CHECK=0
36UPLOAD_RESULTS=0
37
38# LOCAL_FLASHROM is required if both a secondary programmer *and* a remote host
39# are used since OLD_FLASHROM and NEW_FLASHROM will be called on the remote
40# host and we need a local copy of flashrom to control the secondary programmer.
41# By default this will be set to the result of `which flashrom`.
42LOCAL_FLASHROM=""
43
44# Primary/Secondary programmer options
45PRIMARY_OPTS=""
46SECONDARY_OPTS=""
47
48# Calls preflash_hook() and postflash_hook() before and after doing a command.
49CUSTOM_HOOKS_FILENAME=""
50
51# logfile to store the script's output
52SCRIPT_LOGFILE="flashrom-test_script_output.txt"
53
54# Test type
55TEST_TYPE_UNKNOWN=0
56TEST_TYPE_SINGLE=1
57TEST_TYPE_ENDURANCE=2
58TEST_TYPE=$TEST_TYPE_UNKNOWN
59
60# Region modes
61REGION_MODE_UNKNOWN=0
62REGION_MODE_CLOBBER=1
63REGION_MODE_DESCRIPTOR=2
64REGION_MODE_FLASHMAP=3
65REGION_MODE_LAYOUT=4
66REGION_MODE=$REGION_MODE_UNKNOWN
67DESCRIPTOR_REGION="BIOS"
68FLASHMAP_REGION="RW_SECTION_A"
69LAYOUT_FILE=""
70LAYOUT_REGION="RW"
71SMALL_REGION=0
72
73# Remote testing options
74SSH_PORT=""
75REMOTE_HOST=""
76REMOTE_PORT_OPTION=""
77REMOTE_ONLY=0
78REMOTE_PROGRAMMER_PARAMS=""
79SSH_CMD="ssh $REMOTE_PORT_OPTION root@${REMOTE_HOST} command -v"
80
81LOCAL=0
82REMOTE=1
83DO_REMOTE=0	# boolean to use for cmd() and tests
84
85# relative path from flashrom root directory
86TEST_SCRIPT_PATH="util/testing/test_v2.1.sh"
87
88# In case we need to run flashrom locally and we're not already root.
89SUDO_CMD=""
90if [ "$(id -u)" -ne "0" ]; then
91	SUDO_CMD="sudo"
92fi
93
94# 1KB
95K=1024
96
97show_help() {
98	printf "Usage:
99	${0} <options>
100
101General options:
102    -b, --backup-image <path>
103        Backup image to write unconditionally at end of testing.
104    -h, --help
105        Show this message.
106    -l, --layout-file <path>
107        Layout file (required if mode is \"layout\", resides locally).
108    -m, --mode <arg>
109        Region access mode (clobber, descriptor, flashmap, layout).
110    -n, --new <path>
111        Path to new version of flashrom.
112    -o, --old <path>
113        Path to old (stable) version of flashrom.
114    -p, --primary-programmer <parameters>
115        Primary programmer options.
116    -r, --remote-host <host>
117        Remote host to test primary programmer on.
118    -s, --secondary-programmer <parameters>
119        Secondary programmer options.
120    -t, --type <arg>
121        Test type (single, endurance).
122    -u, --upload-results
123        Upload results to flashrom.org.
124    -v, --voltage
125        Chip voltage in millivolts (usually 1800 or 3300).
126
127Long options:
128    --custom-hooks <filename>
129        Supply a script with custom hooks to run before and after commands.
130    --descriptor-region <name>
131        Specify region to use in descriptor mode (default: $DESCRIPTOR_REGION)
132    --flashmap-region <name>
133        Specify region to use in flashmap mode (default: $FLASHMAP_REGION)
134    --layout-region <name>
135        Specify region to use in layout mode (default: $LAYOUT_REGION)
136    --local-flashrom <path>
137        Path to local version of flashrom when using both a secondary programmer
138        and remote host (default: $($SUDO_CMD which flashrom))
139    --no-clean
140        Do not remove temporary files.
141    --skip-consistency-check
142        Skip the consistency check (two consecutive reads) at beginning.
143    --small-region
144        Omit tests that require large amounts of space (>16KB).
145Remote connectivity options:
146    --ssh-port <port>
147        Use a specific SSH port.
148
149See documentation for usage examples (TODO: Migrate https://goo.gl/3jNoL7
150to flashrom wiki).\n
151"
152}
153
154getopt -T
155if [ $? -ne 4 ]; then
156	printf "GNU-compatible getopt(1) required.\n"
157	exit $EXIT_FAILURE
158fi
159
160LONGOPTS="backup-image:,help,,new:,old:,remote-host:,upload-results:"
161LONGOPTS="${LONGOPTS},primary-programmer:,secondary-programmer:,local-flashrom:"
162LONGOPTS="${LONGOPTS},custom-hooks:,mode:,skip-consistency-check,small-region"
163LONGOPTS="${LONGOPTS},type:,voltage:"
164LONGOPTS="${LONGOPTS},layout-file:,descriptor-region:,flashmap-region:,layout-region:"
165LONGOPTS="${LONGOPTS},no-clean"
166LONGOPTS="${LONGOPTS},ssh-port:"
167
168ARGS=$(getopt -o b:hl:m:n:o:p:r:s:t:u -l "$LONGOPTS" -n "$0" -- "$@");
169if [ $? != 0 ] ; then printf "Terminating...\n" >&2 ; exit 1 ; fi
170eval set -- "$ARGS"
171while true ; do
172	case "$1" in
173		# Generic options
174		-b|--backup-image)
175			shift
176			BACKUP_IMAGE="$1"
177			;;
178		-h|--help)
179			show_help
180			exit $EXIT_SUCCESS
181			;;
182		-l|--layout-file)
183			shift
184			LAYOUT_FILE="$1"
185			;;
186		-m|--mode)
187			shift
188			if [ "$1" = "clobber" ]; then
189				REGION_MODE=$REGION_MODE_CLOBBER
190			elif [ "$1" = "descriptor" ]; then
191				REGION_MODE=$REGION_MODE_DESCRIPTOR
192			elif [ "$1" = "flashmap" ]; then
193				REGION_MODE=$REGION_MODE_FLASHMAP
194			elif [ "$1" = "layout" ]; then
195				REGION_MODE=$REGION_MODE_LAYOUT
196			else
197				printf "Unknown mode: $1\n"
198				exit $EXIT_FAILURE
199			fi
200			;;
201		-n|--new)
202			shift
203			NEW_FLASHROM="$1"
204			;;
205		-o|--old)
206			shift
207			OLD_FLASHROM="$1"
208			;;
209		-p|--primary_programmer)
210			shift
211			PRIMARY_OPTS="-p $1"
212			;;
213		-s|--secondary_programmer)
214			shift
215			SECONDARY_OPTS="-p $1"
216			;;
217		-t|--type)
218			shift
219			if [ "$1" = "single" ]; then
220				TEST_TYPE=$TEST_TYPE_SINGLE
221			elif [ "$1" = "endurance" ]; then
222				TEST_TYPE=$TEST_TYPE_ENDURANCE
223			else
224				printf "Unknown type: $1\n"
225				exit $EXIT_FAILURE
226			fi
227			;;
228		-r|--remote-host)
229			DO_REMOTE=1
230			shift
231			REMOTE_HOST="$1"
232			;;
233		-u|--upload-results)
234			UPLOAD_RESULTS=1
235			;;
236		-v|--voltage)
237			shift
238			VOLTAGE="$1"
239			;;
240
241		# Longopts only
242		--custom-hooks)
243			shift
244			CUSTOM_HOOKS_FILENAME="$1"
245			;;
246		--descriptor-region)
247			shift
248			DESCRIPTOR_REGION="$1"
249			;;
250		--flashmap-region)
251			shift
252			FLASHMAP_REGION="$1"
253			;;
254		--layout-region)
255			shift
256			LAYOUT_REGION="$1"
257			;;
258		--local-flashrom)
259			shift
260			LOCAL_FLASHROM="$1"
261			;;
262		--no-clean)
263			NO_CLEAN=1
264			;;
265		--skip-consistency-check)
266			SKIP_CONSISTENCY_CHECK=1
267			;;
268		--small-region)
269			SMALL_REGION=1
270			;;
271
272		# Remote testing options
273		--ssh-port)
274			shift
275			REMOTE_PORT_OPTION="-p $1"
276			;;
277
278		# error handling
279		--)
280			shift
281			if [ -n "$*" ]; then
282				printf "Non-option parameters detected: '$*'\n"
283				exit $EXIT_FAILURE
284			fi
285			break
286			;;
287		*)
288			printf "error processing options at '$1'\n"
289			exit $EXIT_FAILURE
290	esac
291	shift
292done
293
294# TODO: Implement this.
295if [ $UPLOAD_RESULTS -eq 1 ]; then
296	printf "TODO: Implement ability to upload results.\n"
297	exit $EXIT_FAILURE
298fi
299
300#
301# Source helper scripts
302#
303export REMOTE_HOST REMOTE_PORT_OPTION
304export LOCAL REMOTE FATAL NONFATAL EXIT_SUCCESS EXIT_FAILURE
305export CUSTOM_HOOKS_FILENAME SUDO_CMD VOLTAGE
306. "$(pwd)/util/testing/cmd.sh"
307
308# We will set up a logs directory within the tmpdirs to store
309# all output logs.
310LOGS="logs"
311
312# Setup temporary working directories:
313# LOCAL_TMPDIR:  Working directory on local host.
314# REMOTE_TMPDIR: Working directory on remote host.
315# TMPDIR:        The temporary directory in which we do most of the work. This is
316#                convenient for commands that depend on $DO_REMOTE.
317LOCAL_TMPDIR=$(mktemp -d --tmpdir flashrom_test.XXXXXXXX)
318if [ $? -ne 0 ] ; then
319	printf "Could not create temporary directory\n"
320	exit $EXIT_FAILURE
321fi
322mkdir "${LOCAL_TMPDIR}/${LOGS}"
323
324if [ $DO_REMOTE -eq 1 ]; then
325	REMOTE_TMPDIR=$(ssh root@${REMOTE_HOST} mktemp -d --tmpdir flashrom_test.XXXXXXXX)
326	if [ $? -ne 0 ] ; then
327		printf "Could not create temporary directory\n"
328		exit $EXIT_FAILURE
329	fi
330	scmd $REMOTE "mkdir ${REMOTE_TMPDIR}/${LOGS}"
331fi
332
333if [ $DO_REMOTE -eq 0 ]; then
334	TMPDIR="$LOCAL_TMPDIR"
335else
336	TMPDIR="$REMOTE_TMPDIR"
337fi
338
339#
340# Test command-line validity.
341#
342if [ $TEST_TYPE -eq $TEST_TYPE_UNKNOWN ]; then
343	printf "Must specify a test type (-t/--type).\n"
344	exit $EXIT_FAILURE
345elif [ $TEST_TYPE -eq $TEST_TYPE_SINGLE ]; then
346	if [ $REGION_MODE -eq $REGION_MODE_UNKNOWN ]; then
347		printf "Must specify a region access mode (-m/--mode).\n"
348		exit $EXIT_FAILURE
349	elif [ $REGION_MODE -eq $REGION_MODE_LAYOUT ]; then
350		if [ -z "$LAYOUT_FILE" ]; then
351			printf "Must specify a layout file when using layout mode.\n"
352			exit $EXIT_FAILURE
353		fi
354
355		scmd $DO_REMOTE "stat $LAYOUT_FILE"
356		if [ $? -ne 0 ]; then
357			if [ $DO_REMOTE -eq 1 ]; then
358				tmp=" on remote host $REMOTE_HOST."
359			else
360				tmp=" on local host."
361			fi
362			printf "Layout file $LAYOUT_FILE not found${tmp}\n"
363			exit $EXIT_FAILURE
364		fi
365
366		if [ $DO_REMOTE -eq 1 ]; then
367			scp root@"${REMOTE_HOST}:$LAYOUT_FILE" "${LOCAL_TMPDIR}/" 2>&1 >/dev/null
368		else
369			cp "$LAYOUT_FILE" "${LOCAL_TMPDIR}/"
370		fi
371	fi
372fi
373
374if [ -n "$VOLTAGE" ]; then
375	echo "$VOLTAGE" | grep -q '[^0-9]'
376	if [ $? -ne 1 ]; then
377		printf "Voltage must be an integer with units of millivolts."
378		exit $EXIT_FAILURE
379	fi
380fi
381
382if [ $DO_REMOTE -eq 1 ]; then
383	# Test connection to remote host
384	test_cmd $DO_REMOTE "ls /" $NONFATAL
385	if [ $? -ne 0 ]; then
386		printf "Could not connect to remote host ${REMOTE_HOST}\n"
387		exit $EXIT_FAILURE
388	fi
389fi
390
391# Remote host and secondary programmer are in use, so either the user must
392# specify a local version of flashrom to control the secondary programmer
393# or it must be found in the default path.
394if [ $DO_REMOTE -eq 1 ] && [ -n "$SECONDARY_OPTS" ]; then
395	if [ -z "$LOCAL_FLASHROM" ]; then
396		LOCAL_FLASHROM="$($SUDO_CMD which flashrom)"
397	fi
398
399	if [ ! -e "$LOCAL_FLASHROM" ]; then
400		printf "$LOCAL_FLASHROM does not exist\n"
401		exit $EXIT_FAILURE
402	fi
403fi
404
405#
406# Dependencies
407#
408
409# cmp is used to compare files
410test_cmd $DO_REMOTE "cmp" $FATAL
411
412if [ ! -e "/dev/urandom" ]; then
413	printf "This script uses /dev/urandom\n"
414	exit $EXIT_FAILURE
415fi
416
417if [ ! -e "/dev/zero" ]; then
418	printf "This script uses /dev/zero\n"
419	exit $EXIT_FAILURE
420fi
421
422#
423# Setup.
424#
425
426# Naive path check
427if [ ! -e "$TEST_SCRIPT_PATH" ] ; then
428	printf "Script must be run from root of flashrom directory\n"
429	exit $EXIT_FAILURE
430fi
431
432if [ -z "$OLD_FLASHROM" ]; then
433	if [ $DO_REMOTE -eq 1 ]; then
434		OLD_FLASHROM="$(ssh root@${REMOTE_HOST} which flashrom)"
435	else
436		OLD_FLASHROM="$($SUDO_CMD which flashrom)"
437	fi
438fi
439test_cmd $DO_REMOTE "$OLD_FLASHROM --help" $NONFATAL
440if [ $? -ne 0 ]; then
441	printf "Old flashrom binary is not usable.\n"
442	exit $EXIT_FAILURE
443fi
444
445# print $1 and store it in the script log file
446print_and_log()
447{
448	printf "$1" | tee -a "${LOCAL_TMPDIR}/${LOGS}/${SCRIPT_LOGFILE}"
449}
450
451# Copy files from local tmpdir to remote host tmpdir
452copy_to_remote()
453{
454	for F in $@; do
455		scp "${LOCAL_TMPDIR}/${F}" root@"${REMOTE_HOST}:${REMOTE_TMPDIR}" 2>&1 >/dev/null
456	done
457}
458
459# Copy files from remote host tmpdir to local tmpdir
460copy_from_remote()
461{
462	for F in $@; do
463		scp root@"${REMOTE_HOST}:${REMOTE_TMPDIR}/${F}" "${LOCAL_TMPDIR}/" 2>&1 >/dev/null
464	done
465}
466
467# A wrapper for scmd calls to flashrom when we want to log the output
468# $1: 0 ($LOCAL) to run command locally,
469#     1 ($REMOTE) to run remotely if remote host defined
470# $2: arguments to be passed into scmd
471# $3: context of the flashrom call (to be used in the logfile)
472flashrom_log_scmd()
473{
474	local logfile="flashrom-${3}.txt"
475	local rc
476
477	if [ $1 -eq $REMOTE ]; then
478		tmpdir=$REMOTE_TMPDIR
479	else
480		tmpdir=$LOCAL_TMPDIR
481	fi
482
483	scmd $1 "$2 -o ${tmpdir}/${LOGS}/${logfile}"; rc=$?
484	# if the call was successful, we don't want to save the log (only save failure logs)
485	if [ $rc -eq $EXIT_SUCCESS ]; then
486		scmd $1 "rm -f ${tmpdir}/${LOGS}/${logfile}"
487	else
488		# if the log was stored remotely, we want to copy it over to local tmpdir
489		if [ $1 -eq $REMOTE ]; then
490			scp root@"${REMOTE_HOST}:${REMOTE_TMPDIR}/${LOGS}/${logfile}" "${LOCAL_TMPDIR}/${LOGS}" 2>&1 >/dev/null
491		fi
492	fi
493
494	return $rc
495}
496
497# Read current image as backup in case one hasn't already been specified.
498if [ -z "$BACKUP_IMAGE" ]; then
499	backup_file="backup.bin"
500	if [ $DO_REMOTE -eq 1 ]; then
501		BACKUP_IMAGE="${REMOTE_TMPDIR}/${backup_file}"
502	else
503		BACKUP_IMAGE="${LOCAL_TMPDIR}/${backup_file}"
504	fi
505
506	print_and_log "Reading backup image..."
507	flashrom_log_scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -r $BACKUP_IMAGE" "read_backup"
508
509	if [ $? -ne 0 ]; then
510		print_and_log "Failed to read backup image, aborting.\n"
511		exit $EXIT_FAILURE
512	fi
513
514	if [ $DO_REMOTE -eq 1 ]; then
515		copy_from_remote "$backup_file"
516	fi
517else
518	if [ $DO_REMOTE -eq 1 ]; then
519		scmd $DO_REMOTE "cp $BACKUP_IMAGE $REMOTE_TMPDIR"
520		copy_from_remote "$(basename $BACKUP_IMAGE)"
521	fi
522fi
523
524# The copy of flashrom to test. If unset, we'll assume the user wants to test
525# a newly built flashrom binary in the current directory.
526if [ -z "$NEW_FLASHROM" ] ; then
527	if [ -x "flashrom" ]; then
528		NEW_FLASHROM="flashrom"
529	else
530		print_and_log "Must supply new flashrom version to test\n"
531		exit $EXIT_FAILURE
532	fi
533fi
534
535print_and_log "Stable flashrom binary: ${OLD_FLASHROM}\n"
536print_and_log "New flashrom binary to test: ${NEW_FLASHROM}\n"
537print_and_log "Local temporary files will be stored in ${LOCAL_TMPDIR}\n"
538if [ $DO_REMOTE -eq 1 ]; then
539	print_and_log "Remote temporary files will be stored in ${REMOTE_HOST}:${REMOTE_TMPDIR}\n"
540	print_and_log "Backup image: ${REMOTE_HOST}:${BACKUP_IMAGE}\n"
541	print_and_log "Backup image also stored at: ${LOCAL_TMPDIR}/$(basename ${BACKUP_IMAGE})\n"
542else
543	print_and_log "Backup image: ${BACKUP_IMAGE}\n"
544fi
545
546#
547# Now the fun begins.
548#
549cmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS --flash-size" "${LOCAL_TMPDIR}/old_chip_size.txt.orig"
550cmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS --flash-size" "${LOCAL_TMPDIR}/new_chip_size.txt.orig"
551# get rid of banner and other superfluous output from flashrom
552tail -n 1 "${LOCAL_TMPDIR}/old_chip_size.txt.orig" > "${LOCAL_TMPDIR}/old_chip_size.txt"
553tail -n 1 "${LOCAL_TMPDIR}/new_chip_size.txt.orig" > "${LOCAL_TMPDIR}/new_chip_size.txt"
554tmp=$(cat ${LOCAL_TMPDIR}/old_chip_size.txt)
555CHIP_SIZE=$(cat ${LOCAL_TMPDIR}/new_chip_size.txt)
556CHIP_SIZE_KB=$(($CHIP_SIZE / $K))
557CHIP_SIZE_HALF=$(($CHIP_SIZE / 2))
558if [ $CHIP_SIZE -ne $tmp ]; then
559	print_and_log "New flashrom and old flashrom disagree on chip size. Aborting.\n"
560	exit $EXIT_FAILURE
561else
562	print_and_log "Chip size: $CHIP_SIZE_KB KiB\n"
563fi
564
565# Upload results
566#do_upload()
567#{
568#	# TODO: implement this
569#}
570
571# Remove temporary files
572do_cleanup()
573{
574	if [ $NO_CLEAN -eq 1 ]; then
575		print_and_log "Skipping cleanup.\n"
576		return $EXIT_SUCCESS
577	fi
578
579	rm -rf "$LOCAL_TMPDIR"
580	if [ -n "$REMOTE_HOST" ]; then
581		ssh root@${REMOTE_HOST} rm -rf "$REMOTE_TMPDIR"
582	fi
583
584	return $EXIT_SUCCESS
585}
586
587# $1: Message to display to user.
588test_fail()
589{
590	print_and_log "$1\n"
591	printf "Skipping cleanup (logs saved).\n"
592	exit $EXIT_FAILURE
593}
594
595write_backup_image()
596{
597	print_and_log "Writing backup image.\n"
598	flashrom_log_scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -w $BACKUP_IMAGE" "write_backup"
599	if [ $? -ne 0 ]; then
600		test_fail "Failed to write backup image.\n"
601	fi
602}
603
604# Read a region twice and compare results
605# $1: address of region (in bytes)
606# $2: length of region (in bytes)
607double_read_test()
608{
609	local cmp1="${TMPDIR}/read_test1.bin"
610	local cmp2="${TMPDIR}/read_test2.bin"
611	local layout="double_read_test_layout.txt"
612	local len=$(($2 / $K))
613
614	print_and_log "Doing double read test, size: $len KiB\n"
615	# FIXME: Figure out how to do printf remotely...
616	printf "%06x:%06x region\n" $1 $(($1 + $2 - 1)) > "${LOCAL_TMPDIR}/${layout}"
617	if [ $DO_REMOTE -eq 1 ]; then copy_to_remote "$layout" ; fi
618
619	flashrom_log_scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -l ${TMPDIR}/${layout} -i region -r ${cmp1}" "double_read_1"
620	# FIXME: second (or maybe third?) read should be done using secondary programmer, if applicable.
621	flashrom_log_scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -l ${TMPDIR}/${layout} -i region -r ${cmp2}" "double_read_2"
622	scmd $DO_REMOTE "cmp $cmp1 $cmp2"
623	if [ $? -ne 0 ]; then
624		test_fail "Double-read test failed, aborting."
625	fi
626}
627
628PARTIAL_WRITE_TEST_REGION_SIZE=0
629PARTIAL_WRITE_TEST_ALIGN_SIZE_KB=0
630# FIXME: Hack due to lack of region-sized file handling.
631PARTIAL_WRITE_TEST_REGION_BASE_OFFSET=-1
632
633# helper function to reduce repetitiveness of partial_write_test
634partial_write_test_helper()
635{
636	local test_name="$1"
637	local pattern_offset_kb=$2
638	local pattern_size_kb=$3
639	local hex="$4"
640	local oct=""
641	local base_offset_kb=$(($PARTIAL_WRITE_TEST_REGION_BASE_OFFSET / $K))
642
643	oct="\\$(printf "%03o" $hex)"
644	cp "${LOCAL_TMPDIR}/random_4k_test.bin" "${LOCAL_TMPDIR}/${test_name}.bin"
645	dd if=/dev/zero bs=1k count=${pattern_size_kb} 2>/dev/null | tr "\000" "$oct" > "${LOCAL_TMPDIR}/${hex}_${pattern_size_kb}k.bin"
646
647	while [ $(($(($pattern_offset_kb + $pattern_size_kb)) * $K)) -lt $PARTIAL_WRITE_TEST_REGION_SIZE ]; do
648		dd if="${LOCAL_TMPDIR}/${hex}_${pattern_size_kb}k.bin" of="${LOCAL_TMPDIR}/${test_name}.bin" bs=1k count=$pattern_size_kb seek=$(($base_offset_kb + $pattern_offset_kb)) conv=notrunc 2>/dev/null
649		pattern_offset_kb=$(($pattern_offset_kb + $PARTIAL_WRITE_TEST_ALIGN_SIZE_KB))
650	done
651}
652
653# Regional partial write test. Given a region name, this will write patterns
654# of bytes designed to test corner cases.
655#
656# We assume that eraseable block size can be either 4KB or 64KB and
657# must test for both. For simplicity, we'll assume the region size is
658# at least 256KB.
659#
660# $1: Region name
661partial_write_test()
662{
663	local opts="--noverify-all"
664	local secondary_opts=""		# for secondary programmer
665	local region_name="$1"
666	local filename=""
667	local test_num=0
668	local prev_test_num=0
669
670	# FIXME: Hack due to lack of region-sized file handling.
671	if [ $((PARTIAL_WRITE_TEST_REGION_SIZE)) -eq 0 ]; then
672		print_and_log "Size of $region_name unknown\n"
673		return $EXIT_FAILURE
674	fi
675	if [ $((PARTIAL_WRITE_TEST_REGION_BASE_OFFSET)) -lt 0 ]; then
676		print_and_log "Offset of $region_name is unknown\n"
677		return $EXIT_FAILURE
678	fi
679
680	if [ $TEST_TYPE -eq $TEST_TYPE_SINGLE ]; then
681		if [ $REGION_MODE -eq $REGION_MODE_LAYOUT ]; then
682			opts="$opts -l $LAYOUT_FILE"
683			secondary_opts="$opts"
684		elif [ $REGION_MODE -eq $REGION_MODE_CLOBBER ]; then
685			printf "000000:%06x RW\n" $(($CHIP_SIZE - 1)) > "${LOCAL_TMPDIR}/clobber_mode_layout.txt"
686			if [ $DO_REMOTE -eq 1 ]; then
687				copy_to_remote "clobber_mode_layout.txt"
688			fi
689			secondary_opts="$opts -l ${LOCAL_TMPDIR}/clobber_mode_layout.txt"
690			opts="$opts -l ${TMPDIR}/clobber_mode_layout.txt"
691		fi
692	fi
693
694	if [ $SMALL_REGION -eq 1 ]; then
695		PARTIAL_WRITE_TEST_ALIGN_SIZE_KB=16
696	else
697		PARTIAL_WRITE_TEST_ALIGN_SIZE_KB=256
698	fi
699
700	# FIXME: Add sanity checks.
701
702	print_and_log "Doing region-based partial write test on region \"$region_name\"\n"
703	flashrom_log_scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -i ${region_name} -r ${TMPDIR}/${region_name}.bin" "read_region_${region_name}"
704	if [ $DO_REMOTE -eq 1 ]; then
705		copy_from_remote "${region_name}.bin"
706	fi
707
708	if [ $(($PARTIAL_WRITE_TEST_REGION_SIZE % $(($PARTIAL_WRITE_TEST_ALIGN_SIZE_KB)))) -ne 0 ]; then
709		print_and_log "Region $region_name is not aligned to $PARTIAL_WRITE_TEST_ALIGN_SIZE_KB\n"
710		return $EXIT_FAILURE
711	fi
712
713	# Test procedure:
714	# Clobber region with random content first. Then do writes using the
715	# following sequences for each 128KB:
716	# 0-2K		: 0x00 (\000)	Partial 4KB sector, lower half
717	# 2K-6K		: 0x11 (\021)	Crossover 4KB sector boundary
718	# 6K-8K		: 0x22 (\042)	Partial 4KB sector, upper half
719	# 8K-16K	: 0x33 (\063)	Full 4KB sectors
720	#
721	# Repeat the above sequence for 64KB-aligned sizes
722	# 0-32K		: 0x44 (\104)	Partial 64KB block, lower half
723	# 32K-96K	: 0x55 (\125)	Crossover 64KB block boundary
724	# 96K-128K	: 0x66 (\146)	Partial 64KB block, upper half
725	# 128K-256K	: 0x77 (\167)	Full 64KB blocks
726
727	test_num=0
728	dd if=/dev/zero of="${LOCAL_TMPDIR}/random_4k_test.bin" bs=1k count=$CHIP_SIZE_KB 2>/dev/null
729	dd if=/dev/urandom of="${LOCAL_TMPDIR}/random_4k_test.bin" bs=4k count=$(($PARTIAL_WRITE_TEST_REGION_SIZE / $((4 * $K)))) seek=$(($((PARTIAL_WRITE_TEST_REGION_BASE_OFFSET)) / $((4 * $K)))) conv=notrunc 2>/dev/null
730
731	# 0-2K		: 0x00 (\000)	Partial 4KB sector, lower half
732	partial_write_test_helper "4k_test_${test_num}" 0 2 "0x00"
733	prev_test_num=$test_num
734	test_num=$(($test_num + 1))
735
736	# 2K-6K		: 0x11 (\021)	Crossover 4KB sector boundary
737	partial_write_test_helper "4k_test_${test_num}" 2 4 "0x11"
738	test_num=$(($test_num + 1))
739
740	# 6K-8K		: 0x22 (\042)	Partial 4KB sector, upper half
741	partial_write_test_helper "4k_test_${test_num}" 6 2 "0x22"
742	test_num=$(($test_num + 1))
743
744	# 8K-16K	: 0x33 (\063)	Full 4KB sectors
745	partial_write_test_helper "4k_test_${test_num}" 8 8 "0x33"
746
747	for F in ${LOCAL_TMPDIR}/random_4k_test.bin ${LOCAL_TMPDIR}/4k_test_*.bin ; do
748		filename=$(basename $F)
749		if [ $DO_REMOTE -eq 1 ]; then
750			copy_to_remote $filename
751		fi
752
753		flashrom_log_scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -i ${region_name} -w ${TMPDIR}/${filename}" "write_${filename}"
754		if [ $? -ne 0 ]; then
755			test_fail "Failed to write $filename to $region_name"
756		fi
757
758		flashrom_log_scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -i ${region_name} -v ${TMPDIR}/${filename}" "verify_${filename}"
759		if [ $? -ne 0 ]; then
760			test_fail "Failed to verify write of $filename to $region_name"
761		fi
762
763		if [ -n "$SECONDARY_OPTS" ]; then
764			flashrom_log_scmd $LOCAL "$LOCAL_FLASHROM $SECONDARY_OPTS $secondary_opts -i ${region_name} -v ${LOCAL_TMPDIR}/${filename}" "verify_secondary_${filename}"
765			if [ $? -ne 0 ]; then
766				test_fail "Failed to verify write of $filename to $region_name using secondary programmer"
767			fi
768		fi
769
770		print_and_log "\tWrote $filename to $region_name region successfully.\n"
771	done
772
773	if [ $SMALL_REGION -eq 1 ]; then
774		return $EXIT_SUCCESS
775	fi
776
777	#
778	# Second half: Tests for 64KB chunks
779	#
780	test_num=0
781#	dd if=/dev/urandom of="${LOCAL_TMPDIR}/random_64k_test.bin" bs=128k count=$(($PARTIAL_WRITE_TEST_REGION_SIZE / $((128*$K)))) 2>/dev/null
782	dd if=/dev/zero of="${LOCAL_TMPDIR}/random_64k_test.bin" bs=1k count=$CHIP_SIZE_KB 2>/dev/null
783	dd if=/dev/urandom of="${LOCAL_TMPDIR}/random_64k_test.bin" bs=128k count=$(($PARTIAL_WRITE_TEST_REGION_SIZE / $((128 * $K)))) seek=$(($((PARTIAL_WRITE_TEST_REGION_BASE_OFFSET)) / $((128 * $K)))) conv=notrunc 2>/dev/null
784
785	# 0-32K		: 0x44 (\104)	Partial 64KB block, lower half
786	partial_write_test_helper "64k_test_${test_num}"  0 32 "0x44"
787	prev_test_num=$test_num
788	test_num=$(($test_num + 1))
789
790	# 32K-96K	: 0x55 (\125)	Crossover 64KB block boundary
791	partial_write_test_helper "64k_test_${test_num}" 32 64 "0x55"
792	test_num=$(($test_num + 1))
793
794	# 96K-128K	: 0x66 (\146)	Partial 64KB block, upper half
795	partial_write_test_helper "64k_test_${test_num}"  96 32 "0x66"
796	test_num=$(($test_num + 1))
797
798	# 128K-256K	: 0x77 (\167)	Full 64KB blocks
799	partial_write_test_helper "64k_test_${test_num}"  128 128 "0x77"
800
801	for F in ${LOCAL_TMPDIR}/random_64k_test.bin ${LOCAL_TMPDIR}/64k_test_*.bin ; do
802		filename=$(basename $F)
803		if [ $DO_REMOTE -eq 1 ]; then
804			copy_to_remote $filename
805		fi
806
807		flashrom_log_scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS $opts -i ${region_name} -w ${TMPDIR}/${filename}" "write_${filename}"
808		if [ $? -ne 0 ]; then
809			test_fail "Failed to write $filename to $region_name"
810		fi
811
812		flashrom_log_scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS $opts -i ${region_name} -v ${TMPDIR}/${filename}" "verify_${filename}"
813		if [ $? -ne 0 ]; then
814			test_fail "Failed to verify write of $filename to $region_name"
815		fi
816
817		if [ -n "$SECONDARY_OPTS" ]; then
818			flashrom_log_scmd $LOCAL "$LOCAL_FLASHROM $SECONDARY_OPTS $secondary_opts -i ${region_name} -v ${LOCAL_TMPDIR}/${filename}" "verify_secondary_${filename}"
819			if [ $? -ne 0 ]; then
820				test_fail "Failed to verify write of $filename to $region_name using secondary programmer"
821			fi
822		fi
823
824		print_and_log "\tWrote $filename to $region_name region successfully.\n"
825	done
826
827	return $EXIT_SUCCESS
828}
829
830# Before anything else, check to see if Flashrom can successfully probe
831# for and find the flash chips. If not, we will abort.
832flashrom_log_scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS" "verify_probe"
833if [ $? -ne 0 ]; then
834	test_fail "Failed to find flash chips while probing, aborting."
835fi
836
837# Read ROM twice to test for consistency.
838if [ $SKIP_CONSISTENCY_CHECK -eq 0 ]; then
839	double_read_test 0 $CHIP_SIZE
840fi
841
842if [ $TEST_TYPE -eq $TEST_TYPE_SINGLE ]; then
843	if [ $REGION_MODE -eq $REGION_MODE_CLOBBER ]; then
844		random_file="${TMPDIR}/random_${CHIP_SIZE_KB}K.bin"
845		cmp_file="${TMPDIR}/cmp.bin"
846
847		scmd $DO_REMOTE "dd if=/dev/urandom of=${random_file} bs=1k count=${CHIP_SIZE_KB}"
848		flashrom_log_scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -w $random_file" "clobber_write"
849		flashrom_log_scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -r $cmp_file" "clobber_verify"
850		scmd $DO_REMOTE "cmp $random_file $cmp_file"
851		if [ $? -ne 0 ]; then
852			write_backup_image
853			test_fail "Failed to clobber entire ROM."
854		fi
855	elif [ $REGION_MODE -eq $REGION_MODE_DESCRIPTOR ]; then
856		# FIXME: Need region-sized file handling or some way to get region info
857		print_and_log "Currently broken due to lack of region-sized file handling."
858		exit $EXIT_FAILURE
859	elif [ $REGION_MODE -eq $REGION_MODE_FLASHMAP ]; then
860		# FIXME: Need region-sized file handling or some way to get region info
861		print_and_log "Currently broken due to lack of region-sized file handling."
862		exit $EXIT_FAILURE
863	elif [ $REGION_MODE -eq $REGION_MODE_LAYOUT ]; then
864		rw_layout=""
865		addr=""
866		end=""
867		size=""
868		size_kb=""
869
870		# Look for a region name with any amount of leading whitespace
871		# and no trailing whitespace or characters.
872		rw_layout=$(grep "\s${LAYOUT_REGION}$" "${LOCAL_TMPDIR}/$(basename $LAYOUT_FILE)" | head -n 1)
873		if [ -z "$rw_layout" ]; then
874			print_and_log "No region matching \"${LAYOUT_REGION}\" found layout file \"${LAYOUT_FILE}\"\n"
875			test_fail ""
876		fi
877
878		addr="0x$(echo "$rw_layout" | cut -d ' ' -f -1 | awk -F ':' '{ print $1 }')"
879		end="0x$(echo "$rw_layout" | cut -d ' ' -f -1 | awk -F ':' '{ print $2 }')"
880		size="$(($end - $addr + 1))"
881		size_kb="$(($size / $K))"
882
883		# FIXME: Hack to make this work without region-sized file handling.
884		PARTIAL_WRITE_TEST_REGION_BASE_OFFSET=$addr
885		PARTIAL_WRITE_TEST_REGION_SIZE=$size
886
887		print_and_log "\"$LAYOUT_REGION\" region address: ${addr}, size: $size_kb KiB\n"
888		partial_write_test "$LAYOUT_REGION"
889		if [ $? -ne 0 ]; then
890			print_and_log "Layout mode test failed\n"
891			RC=$EXIT_FAILURE
892		fi
893	fi
894elif [ $TEST_TYPE -eq $TEST_TYPE_ENDURANCE ]; then
895	iteration=1
896	terminate=0
897	random_file="${TMPDIR}/random_${CHIP_SIZE_KB}K.bin"
898	cmp_file="${TMPDIR}/cmp.bin"
899	# TODO: We can measure how long the tests take on average, throughput, etc.
900	# i.e. { time $NEW_FLASHROM $PRIMARY_OPTS -w $random_file ; } 2>&1 | grep user | cut -f2
901
902	# For this test we want to run clobber mode until failure
903	while [ $terminate -eq 0 ]
904	do
905		print_and_log "Running iteration #${iteration}\n"
906
907		scmd $DO_REMOTE "dd if=/dev/urandom of=${random_file} bs=1k count=${CHIP_SIZE_KB}"
908		flashrom_log_scmd $DO_REMOTE "$NEW_FLASHROM $PRIMARY_OPTS -w $random_file" "endurance_write_${iteration}"
909		flashrom_log_scmd $DO_REMOTE "$OLD_FLASHROM $PRIMARY_OPTS -r $cmp_file" "endurance_verify_${iteration}"
910		scmd $DO_REMOTE "cmp $random_file $cmp_file"
911		if [ $? -ne 0 ]; then
912			terminate=1
913		fi
914		scmd $DO_REMOTE "rm -f $cmp_file $random_file"
915
916		iteration=$(($iteration + 1))
917	done
918
919	# TODO: Determine what to return for the endurance test exit status
920	# i.e. what constitutes a test pass and what constitutes a test fail?
921	print_and_log "Failed on iteration $iteration\n"
922	# TODO - Print performance metrics?
923fi
924
925# restore and cleanup
926write_backup_image
927
928if [ $RC -eq 0 ]; then
929	print_and_log "Test status: PASS\n"
930else
931	print_and_log "Test status: FAIL\n"
932fi
933do_cleanup
934
935exit $RC
936