• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env sh
2#
3#
4# See README and https://www.coreboot.org/Board_Status for instructions.
5
6EXIT_SUCCESS=0
7EXIT_FAILURE=1
8
9# Stuff from command-line switches
10COREBOOT_IMAGE="build/coreboot.rom"
11REMOTE_HOST=""
12REMOTE_PORT_OPTION=""
13CLOBBER_OUTPUT=0
14UPLOAD_RESULTS=0
15SERIAL_PORT_SPEED=115200
16
17# Used to specify whether a command should always be run locally or
18# if command should be run remoteley when a remote host is specified.
19LOCAL=0
20REMOTE=1
21FATAL=0
22NONFATAL=1
23
24# Used if cbmem is not in default $PATH, e.g. not installed or when using `sudo`
25CBMEM_PATH=""
26
27# Used if nvramtool is not in default $PATH, e.g. not installed or when using `sudo`
28NVRAMTOOL_PATH=""
29
30case $(uname) in
31	FreeBSD)
32		if [ ! -x /usr/local/bin/gmake ]; then
33			echo "Please install gmake, or build and install devel/gmake from ports."
34			exit $EXIT_FAILURE
35		else
36			MAKE='gmake'
37		fi
38		;;
39	*)
40		MAKE='make'
41		;;
42esac
43
44# test a command
45#
46# $1: 0 ($LOCAL) to run command locally,
47#     1 ($REMOTE) to run remotely if remote host defined
48# $2: command to test
49# $3: 0 ($FATAL) Exit with an error if the command fails
50#     1 ($NONFATAL) Don't exit on command test failure
51test_cmd()
52{
53	local rc
54
55	if [ -e "$2" ]; then
56		return
57	fi
58
59	if [ "$1" -eq "$REMOTE" ] && [ -n "$REMOTE_HOST" ]; then
60		ssh $REMOTE_PORT_OPTION root@${REMOTE_HOST} command -v "$2" > /dev/null
61		rc=$?
62	else
63		command -v "$2" >/dev/null
64		rc=$?
65	fi
66
67	if [ $rc -eq 0 ]; then
68		return 0
69	fi
70
71	if [ "$3" = "1" ]; then
72		return 1
73	fi
74
75	echo "$2 not found"
76	exit $EXIT_FAILURE
77}
78
79_cmd()
80{
81	if [ -e "$2" ]; then
82		return $EXIT_FAILURE
83	fi
84
85	if [ -n "$3" ]; then
86		pipe_location="${3}"
87	else
88		pipe_location="/dev/null"
89	fi
90
91	if [ "$1" -eq "$REMOTE" ] && [ -n "$REMOTE_HOST" ]; then
92		ssh $REMOTE_PORT_OPTION "root@${REMOTE_HOST}" "$2" > "$pipe_location" 2>&1
93	else
94		$2 > "$pipe_location" 2>&1
95	fi
96
97	return $?
98}
99
100# run a command
101#
102# $1: 0 ($LOCAL) to run command locally,
103#     1 ($REMOTE) to run remotely if remote host defined
104# $2: command
105# $3: filename to direct output of command into
106cmd()
107{
108	_cmd $1 "$2" "$3"
109
110	if [ $? -eq 0 ]; then
111		return
112	fi
113
114	echo "Failed to run \"$2\", aborting"
115	rm -f "$3"	# don't leave an empty file
116	exit $EXIT_FAILURE
117}
118
119# run a command where failure is considered to be non-fatal
120#
121# $1: 0 ($LOCAL) to run command locally,
122#     1 ($REMOTE) to run remotely if remote host defined
123# $2: command
124# $3: filename to direct output of command into
125cmd_nonfatal()
126{
127	_cmd $1 "$2" "$3"
128
129	if [ $? -eq 0 ]; then
130		return
131	fi
132
133	echo "Failed to run \"$2\", ignoring"
134	rm -f "$3"	# don't leave an empty file
135}
136
137# read from a serial port device
138#
139# $1: serial device to read from
140# $2: serial port speed
141# $3: filename to direct output of command into
142get_serial_bootlog () {
143
144	local TTY=$1
145	local SPEED=$2
146	local FILENAME=$3
147
148	if [ ! -c "$TTY" ]; then
149		echo "$TTY is not a valid serial device"
150		exit $EXIT_FAILURE
151	fi
152
153	# make the text more noticible
154	test_cmd $LOCAL "tput" $NONFATAL
155	tput_not_available=$?
156	if [ $tput_not_available -eq 0 ]; then
157		tput bold
158		tput setaf 10 # set bright green
159	fi
160
161	echo
162	echo "Waiting to receive boot log from $TTY"
163	echo "Press [Enter] when the boot is complete."
164	echo
165
166	if [ $tput_not_available -eq 0 ]; then
167		tput sgr0
168	fi
169
170	# set up the serial port
171	stty -F $TTY $SPEED cs8 -cstopb -parenb clocal
172
173	# read from the serial port - user must press enter when complete
174	test_cmd $LOCAL "tee"
175	while read LINE; do
176		echo "$LINE" | tee -a "$FILENAME"
177	done < "$SERIAL_DEVICE" &
178	PID=$!
179
180	read foo
181	kill "$PID" 2>/dev/null
182
183	echo "Finished reading boot log."
184}
185
186show_help() {
187	echo "Usage:
188	${0} <option>
189
190Options
191    -c, --cbmem
192        Path to cbmem on device under test (DUT).
193    -n, --nvramtool
194        Path to nvramtool on device under test (DUT).
195    -C, --clobber
196        Clobber temporary output when finished. Useful for debugging.
197    -h, --help
198        Show this message.
199    -i, --image  <image>
200        Path to coreboot image (Default is $COREBOOT_IMAGE).
201    -r, --remote-host  <host>
202        Obtain machine information from remote host (using ssh).
203    -s, --serial-device  </dev/xxx>
204        Obtain boot log via serial device.
205    -S, --serial-speed  <speed>
206        Set the port speed for the serial device (Default is $SERIAL_PORT_SPEED).
207    -u, --upload-results
208        Upload results to coreboot.org.
209
210Long options:
211    --ssh-port <port>
212        Use a specific SSH port.
213"
214}
215
216case $(uname) in
217	FreeBSD)
218		if [ ! -x /usr/local/bin/getopt ]; then
219			echo "Please install getopt, or build and install misc/getopt from ports."
220			exit $EXIT_FAILURE
221		else
222			GETOPT=/usr/local/bin/getopt
223		fi
224		;;
225	*)
226	GETOPT=/usr/bin/getopt
227	;;
228esac
229
230$GETOPT -T
231if [ $? -ne 4 ]; then
232	echo "GNU-compatible getopt(1) required."
233	exit $EXIT_FAILURE
234fi
235
236LONGOPTS="cbmem:,clobber,help,image:,remote-host:,upload-results"
237LONGOPTS="${LONGOPTS},serial-device:,serial-speed:"
238LONGOPTS="${LONGOPTS},ssh-port:"
239
240ARGS=$($GETOPT -o c:n:Chi:r:s:S:u -l "$LONGOPTS" -n "$0" -- "$@");
241if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
242eval set -- "$ARGS"
243while true ; do
244	case "$1" in
245		# generic options
246		-c|--cbmem)
247			shift
248			CBMEM_PATH="$1"
249			;;
250		-n|--nvramtool)
251			shift
252			NVRAMTOOL_PATH="$1"
253			;;
254		-C|--clobber)
255			CLOBBER_OUTPUT=1
256			;;
257		-h|--help)
258			show_help
259			exit $EXIT_SUCCESS
260			;;
261		-i|--image)
262			shift
263			COREBOOT_IMAGE="$1"
264			;;
265		-r|--remote-host)
266			shift
267			REMOTE_HOST="$1"
268			;;
269		-u|--upload-results)
270			UPLOAD_RESULTS=1
271			;;
272
273		# serial port options
274		-s|--serial-device)
275			shift
276			SERIAL_DEVICE="$1"
277			;;
278		-S|--serial-speed)
279			shift
280			SERIAL_PORT_SPEED="$1"
281			;;
282
283		# ssh options
284		--ssh-port)
285			shift
286			REMOTE_PORT_OPTION="-p $1"
287			;;
288
289		# error handling
290		--)
291			shift
292			if [ -n "$*" ]; then
293				echo "Non-option parameters detected: '$*'"
294				exit $EXIT_FAILURE
295			fi
296			break
297			;;
298		*)
299			echo "error processing options at '$1'"
300			exit $EXIT_FAILURE
301	esac
302	shift
303done
304
305grep -rH 'coreboot.org' .git/config >/dev/null 2>&1
306if [ $? -ne 0 ]; then
307	echo "Script must be run from root of coreboot directory"
308	exit $EXIT_FAILURE
309fi
310
311if [ ! -e "$COREBOOT_IMAGE" ]; then
312	echo "board_status needs $COREBOOT_IMAGE, but it does not exist."
313	echo "Use \"-i IMAGE_FILE\" to select a different image, or \"--help\" for more options."
314	exit $EXIT_FAILURE
315fi
316
317# Results will be placed in a temporary location until we're ready to upload.
318# If the user does not wish to upload, results will remain in /tmp.
319case $(uname) in
320	FreeBSD)
321		tmpdir=$(mktemp -d -t coreboot_board_status)
322		;;
323	*)
324		tmpdir=$(mktemp -d --tmpdir coreboot_board_status.XXXXXXXX)
325		;;
326esac
327
328# Obtain coreboot config by running cbfstool on the ROM image. cbfstool may
329# already exist in build/ or util/cbfstool/, but if not then we'll build it
330# now and clean it when we're done.
331cbfstool_cmd="build/cbfstool"
332do_clean_cbfstool=0
333if [ ! -x $cbfstool_cmd ]; then
334	cbfstool_cmd="util/cbfstool/cbfstool"
335	if [ -e $cbfstool_cmd ]; then
336		if test ! -x $cbfstool_cmd; then
337			echo "Cannot execute $cbfstool_cmd."
338			exit $EXIT_FAILURE
339		fi
340	else
341		$MAKE -C util/cbfstool/
342		do_clean_cbfstool=1
343	fi
344fi
345test_cmd $LOCAL "$cbfstool_cmd"
346
347tmpcfg=$(mktemp coreboot_config.XXXXXX)
348echo "Extracting config.txt from $COREBOOT_IMAGE"
349$cbfstool_cmd "$COREBOOT_IMAGE" extract -n config -f "${tmpdir}/config.txt" >/dev/null 2>&1
350mv "${tmpdir}/config.txt" "${tmpdir}/config.short.txt"
351cp "${tmpdir}/config.short.txt" "${tmpcfg}"
352yes "" | $MAKE "DOTCONFIG=${tmpcfg}" oldconfig 2>/dev/null >/dev/null
353mv "${tmpcfg}" "${tmpdir}/config.txt"
354rm -f "${tmpcfg}.old"
355$cbfstool_cmd "$COREBOOT_IMAGE" print > "${tmpdir}/cbfs.txt"
356rom_contents=$($cbfstool_cmd "$COREBOOT_IMAGE" print 2>&1)
357if [ -n "$(echo $rom_contents | grep payload_config)" ]; then
358	echo "Extracting payload_config from $COREBOOT_IMAGE"
359	$cbfstool_cmd "$COREBOOT_IMAGE" extract -n payload_config -f "${tmpdir}/payload_config.txt" >/dev/null 2>&1
360fi
361if [ -n "$(echo $rom_contents | grep payload_version)" ]; then
362	echo "Extracting payload_version from $COREBOOT_IMAGE"
363	$cbfstool_cmd "$COREBOOT_IMAGE" extract -n payload_version -f "${tmpdir}/payload_version.txt" >/dev/null 2>&1
364fi
365case $(uname) in
366	FreeBSD)
367		md5 "$COREBOOT_IMAGE" > "${tmpdir}/rom_checksum.txt"
368		;;
369	*)
370		md5sum -b "$COREBOOT_IMAGE" > "${tmpdir}/rom_checksum.txt"
371		;;
372esac
373
374if test $do_clean_cbfstool -eq 1; then
375	$MAKE -C util/cbfstool clean
376fi
377
378# Obtain board and revision info to form the directory structure:
379# <vendor>/<board>/<revision>/<timestamp>
380mainboard_dir="$(grep CONFIG_MAINBOARD_DIR "${tmpdir}/config.txt" | awk -F '"' '{ print $2 }')"
381vendor=$(echo "$mainboard_dir" | awk -F '/' '{ print $1 }')
382mainboard=$(echo "$mainboard_dir" | awk -F '/' '{ print $2 }')
383
384getrevision="util/board_status/getrevision.sh"
385test_cmd $LOCAL $getrevision
386tagged_version=$($getrevision -T)
387timestamp=$($getrevision -t)
388
389results="${vendor}/${mainboard}/${tagged_version}/${timestamp}"
390
391if [ -n "$(echo $tagged_version | grep dirty)" ]; then
392	echo "The repository is in a dirty state. Please see the output of"
393	echo "'git status' below."
394	git status
395	exit $EXIT_FAILURE
396fi
397
398echo "Temporarily placing output in ${tmpdir}/${results}"
399mkdir -p "${tmpdir}/${results}"
400
401mv "${tmpdir}/config.txt" "${tmpdir}/${results}"
402test -f "${tmpdir}/payload_config.txt" && mv "${tmpdir}/payload_config.txt" "${tmpdir}/${results}"
403test -f "${tmpdir}/payload_version.txt" && mv "${tmpdir}/payload_version.txt" "${tmpdir}/${results}"
404mv "${tmpdir}/config.short.txt" "${tmpdir}/${results}"
405mv "${tmpdir}/cbfs.txt" "${tmpdir}/${results}"
406mv "${tmpdir}/rom_checksum.txt" "${tmpdir}/${results}"
407
408touch "${tmpdir}/${results}/revision.txt"
409printf "Local revision: %s\n" "$($getrevision -l)" >> "${tmpdir}/${results}/revision.txt"
410printf "Tagged revision: %s\n" "${tagged_version}" >> "${tmpdir}/${results}/revision.txt"
411printf "Upstream revision: %s\n" "$($getrevision -u)" >> "${tmpdir}/${results}/revision.txt"
412printf "Upstream URL: %s\n" "$($getrevision -U)" >> "${tmpdir}/${results}/revision.txt"
413printf "Timestamp: %s\n" "$timestamp" >> "${tmpdir}/${results}/revision.txt"
414
415if [ -n "$CBMEM_PATH" ]; then
416	cbmem_cmd="$CBMEM_PATH"
417else
418	cbmem_cmd="cbmem"
419fi
420
421cmos_enabled=0
422if grep -q "CONFIG_USE_OPTION_TABLE=y" "${tmpdir}/${results}/config.short.txt" > /dev/null; then
423	cmos_enabled=1
424fi
425
426if [ -n "$NVRAMTOOL_PATH" ]; then
427	nvramtool_cmd="$NVRAMTOOL_PATH"
428else
429	nvramtool_cmd="nvramtool"
430fi
431
432if [ -n "$SERIAL_DEVICE" ]; then
433	get_serial_bootlog "$SERIAL_DEVICE" "$SERIAL_PORT_SPEED" "${tmpdir}/${results}/coreboot_console.txt"
434elif [ -n "$REMOTE_HOST" ]; then
435	echo "Verifying that CBMEM is available on remote device"
436	test_cmd $REMOTE "$cbmem_cmd"
437	echo "Getting coreboot boot log"
438	cmd $REMOTE "$cbmem_cmd -1" "${tmpdir}/${results}/coreboot_console.txt"
439	echo "Getting timestamp data"
440	cmd_nonfatal $REMOTE "$cbmem_cmd -t" "${tmpdir}/${results}/coreboot_timestamps.txt"
441
442	if [ "$cmos_enabled" -eq 1 ]; then
443		echo "Verifying that nvramtool is available on remote device"
444		test_cmd $REMOTE "$nvramtool_cmd"
445		echo "Getting all CMOS values"
446		cmd $REMOTE "$nvramtool_cmd -a" "${tmpdir}/${results}/cmos_values.txt"
447	fi
448
449	echo "Getting remote dmesg"
450	cmd $REMOTE dmesg "${tmpdir}/${results}/kernel_log.txt"
451else
452	echo "Verifying that CBMEM is available"
453	if [ $(id -u) -ne 0 ]; then
454		command -v "$cbmem_cmd" >/dev/null
455		if [ $? -ne 0 ]; then
456			echo "Failed to run $cbmem_cmd. Check \$PATH or" \
457			"use -c to specify path to cbmem binary."
458			exit $EXIT_FAILURE
459		else
460			cbmem_cmd="sudo $cbmem_cmd"
461		fi
462	else
463		test_cmd $LOCAL "$cbmem_cmd"
464	fi
465
466	echo "Getting coreboot boot log"
467	cmd $LOCAL "$cbmem_cmd -1" "${tmpdir}/${results}/coreboot_console.txt"
468
469	echo "Getting timestamp data"
470	cmd_nonfatal $LOCAL "$cbmem_cmd -t" "${tmpdir}/${results}/coreboot_timestamps.txt"
471
472	if [ "$cmos_enabled" -eq 1 ]; then
473		echo "Verifying that nvramtool is available"
474		if [ $(id -u) -ne 0 ]; then
475			command -v "$nvramtool_cmd" >/dev/null
476			if [ $? -ne 0 ]; then
477				echo "Failed to run $nvramtool_cmd. Check \$PATH or" \
478				"use -n to specify path to nvramtool binary."
479				exit $EXIT_FAILURE
480			else
481				nvramtool_cmd="sudo $nvramtool_cmd"
482			fi
483		else
484			test_cmd $LOCAL "$nvramtool_cmd"
485		fi
486
487		echo "Getting all CMOS values"
488		cmd $LOCAL "$nvramtool_cmd -a" "${tmpdir}/${results}/cmos_values.txt"
489	fi
490
491	echo "Getting local dmesg"
492	cmd $LOCAL "sudo dmesg" "${tmpdir}/${results}/kernel_log.txt"
493fi
494
495#
496# Check files
497#
498if [ $(grep -- -dirty "${tmpdir}/${results}/coreboot_console.txt") ]; then
499	echo "coreboot or the payload are built from a source tree in a" \
500	"dirty state, making it hard to reproduce the result. Please" \
501	"check in your source tree with 'git status'."
502	exit $EXIT_FAILURE
503fi
504
505if [ $(grep -- unknown "${tmpdir}/${results}/coreboot_timestamps.txt" >/dev/null 2>&1) ]; then
506	echo "Unknown timestamps found in 'coreboot_timestamps.txt'." \
507	"Please rebuild the 'cbmem' utility and try again."
508	exit $EXIT_FAILURE
509fi
510
511#
512# Finish up.
513#
514coreboot_dir=$(pwd)
515if [ $UPLOAD_RESULTS -eq 1 ]; then
516	# extract username from ssh://<username>@review.coreboot.org/blah
517	bsrepo=$(git config --get remote.origin.url | sed "s,\(.*\)/coreboot,\1/board-status,")
518
519	cd "util/board_status/"
520	if [ ! -e "board-status" ]; then
521		# FIXME: the board-status directory might get big over time.
522		# Is there a way we can push the results without fetching the
523		# whole repo?
524		git clone "$bsrepo"
525		if [ $? -ne 0 ]; then
526			echo "Error cloning board-status repo, aborting."
527			exit $EXIT_FAILURE
528		fi
529	fi
530
531	cd "board-status"
532
533	echo "Checking for duplicate results"
534	# get any updates to board-status
535	git pull
536
537	echo "${tagged_version}" | grep dirty >/dev/null 2>&1
538	clean_version=$?
539	existing_results=$(git ls-files "${mainboard_dir}/${tagged_version}")
540
541	# reject duplicate results of non-dirty versions
542	if [ "${clean_version}" -eq 1 ] && [ -n "${existing_results}" ] ; then
543		echo "Result is a duplicate, aborting"
544		exit $EXIT_FAILURE
545	fi
546
547	echo "Copying results to $(pwd)/${results}"
548
549	# Note: Result directory should be unique due to the timestamp.
550	cp -R "${tmpdir}/${vendor}" .
551
552	echo "Uploading results"
553	git add "${vendor}"
554	git commit -a -m "${mainboard_dir}/${tagged_version}/${timestamp}"
555	count=0
556	until git push origin main || test $count -eq 3; do
557	        git pull --rebase
558		count=$((count + 1))
559	done
560
561	# Results have been uploaded so it's pointless to keep the
562	# temporary files around.
563	rm -rf "${tmpdir}"
564	if test $count -eq 3; then
565		echo "Error uploading to board-status repo, aborting."
566		exit $EXIT_FAILURE
567	fi
568fi
569cd "$coreboot_dir"
570
571if [ $CLOBBER_OUTPUT -eq 1 ]; then
572	rm -rf "${tmpdir}"
573else
574	if [ $UPLOAD_RESULTS -eq 1 ]; then
575		echo
576		echo "output files are in $(dirname $0)/board-status/${mainboard_dir}/${tagged_version}/${timestamp}"
577	else
578		echo
579		echo "output files are in ${tmpdir}/${results}"
580	fi
581fi
582
583exit $EXIT_SUCCESS
584