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