mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-11-24 10:14:55 +00:00
The dev_debug_vboot program can sometimes interfere with automated firmware testing because it takes too long to read the BIOS flash. Limiting the sections of flash that are read may help, but in some cases skipping this program entirely may be better. This CL does three things: 1. dev_debug_vboot will read only some sections of the BIOS flash, falling back to reading the whole thing only if it fails at that. 2. dev_debug_vboot will source /etc/default/vboot_reference if it exists. Putting DEV_DEBUG_FORCE=1 in that file will prevent dev_debug_vboot from reading the flash at all unless it's invoked with --force option. 3. The Makefile will create the /etc/default/vboot_reference file in the install directory, setting DEV_DEBUG_FORCE to the value in effect at build time. This will let a future CL change the default behavior for each target. BUG=chromium:438854 BRANCH=none TEST=manual Built and tested on Samus. /etc/default/vboot_reference was present, containing "DEV_DEBUG_FORCE=". The dev_debug_vboot script ran normally. Manually changing /etc/default/vboot_reference to contain "DEV_DEBUG_FORCE=1" and rebooting caused dev_debug_vboot to stop before reading the BIOS flash. I also manually forced various flashrom invocations to fail to test each part of the new flow. Change-Id: Ib319dd16b9026162d01f435f15570ec8ba99c512 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/233228 Reviewed-by: David Hendricks <dhendrix@chromium.org> Reviewed-by: Stefan Reinauer <reinauer@chromium.org>
388 lines
9.4 KiB
Bash
Executable File
388 lines
9.4 KiB
Bash
Executable File
#!/bin/sh -ue
|
|
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
#
|
|
# Usage: dev_debug_vboot [ --cleanup | DIRECTORY ]
|
|
#
|
|
# This extracts some useful debugging information about verified boot. A short
|
|
# summary is printed on stdout, more detailed information and working files are
|
|
# left in a log directory.
|
|
#
|
|
##############################################################################
|
|
|
|
# Clean up PATH for root use. Note that we're assuming [ is always built-in.
|
|
[ "${EUID:-0}" = 0 ] && PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
|
|
|
PUBLOGFILE="/var/log/debug_vboot_noisy.log"
|
|
|
|
OPT_CLEANUP=
|
|
OPT_BIOS=
|
|
OPT_FORCE=
|
|
OPT_IMAGE=
|
|
OPT_KERNEL=
|
|
OPT_VERBOSE=
|
|
|
|
FLAG_SAVE_LOG_FILE=yes
|
|
|
|
LOGFILE=/dev/stdout
|
|
TMPDIR=
|
|
|
|
##############################################################################
|
|
|
|
usage() {
|
|
local prog
|
|
|
|
prog=${0##*/}
|
|
cat <<EOF
|
|
|
|
Usage: $prog [options] [DIRECTORY]
|
|
|
|
This logs as much as it can about the verified boot process. With no arguments
|
|
it will attempt to read the current BIOS, extract the firmware keys, and use
|
|
those keys to validate all the ChromeOS kernel partitions it can find. A
|
|
summary output is printed on stdout, and the detailed log is copied to
|
|
$PUBLOGFILE afterwards.
|
|
|
|
If a directory is given, it will attempt to use the components from that
|
|
directory and will leave the detailed log in that directory.
|
|
|
|
Options:
|
|
|
|
-b FILE, --bios FILE Specify the BIOS image to use
|
|
-i FILE, --image FILE Specify the disk image to use
|
|
-k FILE, --kernel FILE Specify the kernel partition image to use
|
|
-v Spew the detailed log to stdout
|
|
|
|
-c, --cleanup Delete the DIRECTORY when done
|
|
|
|
-h, --help Print this help message and exit
|
|
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
cleanup() {
|
|
if [ -n "${FLAG_SAVE_LOG_FILE}" ]; then
|
|
if cp -f "${LOGFILE}" "${PUBLOGFILE}" 2>/dev/null; then
|
|
info "Exporting log file as ${PUBLOGFILE}"
|
|
fi
|
|
fi
|
|
if [ -n "${OPT_CLEANUP}" ] && [ -d "${TMPDIR}" ] ; then
|
|
cd /
|
|
rm -rf "${TMPDIR}"
|
|
fi
|
|
}
|
|
|
|
die() {
|
|
echo "$*" 1>&2
|
|
exit 1
|
|
}
|
|
|
|
info() {
|
|
echo "$@"
|
|
echo "#" "$@" >> "$LOGFILE"
|
|
}
|
|
|
|
infon() {
|
|
echo -n "$@"
|
|
echo "#" "$@" >> "$LOGFILE"
|
|
}
|
|
|
|
debug() {
|
|
echo "#" "$@" >> "$LOGFILE"
|
|
}
|
|
|
|
log() {
|
|
echo "+" "$@" >> "$LOGFILE"
|
|
"$@" >> "$LOGFILE" 2>&1
|
|
}
|
|
|
|
loghead() {
|
|
echo "+" "$@" "| head" >> "$LOGFILE"
|
|
"$@" | head >> "$LOGFILE" 2>&1
|
|
}
|
|
|
|
logdie() {
|
|
echo "+ERROR:" "$@" >> "$LOGFILE"
|
|
die "$@"
|
|
}
|
|
|
|
result() {
|
|
LAST_RESULT=$?
|
|
if [ "${LAST_RESULT}" = "0" ]; then
|
|
info "OK"
|
|
else
|
|
info "FAILED"
|
|
fi
|
|
}
|
|
|
|
require_utils() {
|
|
local missing
|
|
|
|
missing=
|
|
for tool in $* ; do
|
|
if ! type "$tool" >/dev/null 2>&1 ; then
|
|
missing="$missing $tool"
|
|
fi
|
|
done
|
|
if [ -n "$missing" ]; then
|
|
logdie "can't find these programs: $missing"
|
|
fi
|
|
}
|
|
|
|
extract_kerns_from_file() {
|
|
local start
|
|
local size
|
|
local part
|
|
local rest
|
|
|
|
debug "Extracting kernel partitions from $1 ..."
|
|
cgpt find -v -t kernel "$1" | grep 'Label:' |
|
|
while read start size part rest; do
|
|
name="part_${part}"
|
|
log dd if="$1" bs=512 skip=${start} count=${size} of="${name}" &&
|
|
echo "${name}"
|
|
done
|
|
}
|
|
|
|
format_as_tpm_version() {
|
|
local a
|
|
local b
|
|
local what
|
|
local num
|
|
local rest
|
|
|
|
a='/(Data|Kernel) key version/ {print $1,$4}'
|
|
b='/Kernel version/ {print $1, $3}'
|
|
awk "$a $b" "$1" | while read what num rest; do
|
|
[ "${what}" = "Data" ] && block="${num}"
|
|
[ "${what}" = "Kernel" ] && printf '0x%04x%04x' "${block}" "${num}"
|
|
done
|
|
}
|
|
|
|
fix_old_names() {
|
|
# Convert any old-style names to new-style
|
|
[ -f GBB_Area ] && log mv -f GBB_Area GBB
|
|
[ -f Firmware_A_Key ] && log mv -f Firmware_A_Key VBLOCK_A
|
|
[ -f Firmware_B_Key ] && log mv -f Firmware_B_Key VBLOCK_B
|
|
[ -f Firmware_A_Data ] && log mv -f Firmware_A_Data FW_MAIN_A
|
|
[ -f Firmware_B_Data ] && log mv -f Firmware_B_Data FW_MAIN_B
|
|
true
|
|
}
|
|
|
|
##############################################################################
|
|
# Here we go...
|
|
|
|
umask 022
|
|
|
|
# defaults
|
|
DEV_DEBUG_FORCE=
|
|
|
|
# override them?
|
|
[ -f /etc/default/vboot_reference ] && . /etc/default/vboot_reference
|
|
|
|
# Pre-parse args to replace actual args with a sanitized version.
|
|
TEMP=$(getopt -o hvb:i:k:cf --long help,bios:,image:,kernel:,cleanup,force \
|
|
-n $0 -- "$@")
|
|
eval set -- "$TEMP"
|
|
|
|
# Now look at them.
|
|
while true ; do
|
|
case "${1:-}" in
|
|
-b|--bios)
|
|
OPT_BIOS=$(readlink -f "$2")
|
|
shift 2
|
|
FLAG_SAVE_LOG_FILE=
|
|
;;
|
|
-i|--image=*)
|
|
OPT_IMAGE=$(readlink -f "$2")
|
|
shift 2
|
|
FLAG_SAVE_LOG_FILE=
|
|
;;
|
|
-k|--kernel)
|
|
OPT_KERNEL=$(readlink -f "$2")
|
|
shift 2
|
|
FLAG_SAVE_LOG_FILE=
|
|
;;
|
|
-c|--cleanup)
|
|
OPT_CLEANUP=yes
|
|
shift
|
|
;;
|
|
-f|--force)
|
|
OPT_FORCE=yes
|
|
shift
|
|
;;
|
|
-v)
|
|
OPT_VERBOSE=yes
|
|
shift
|
|
FLAG_SAVE_LOG_FILE=
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
break
|
|
;;
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
*)
|
|
die "Internal error in option parsing"
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ -z "${1:-}" ]; then
|
|
TMPDIR=$(mktemp -d /tmp/debug_vboot_XXXXXXXXX)
|
|
else
|
|
TMPDIR="$1"
|
|
[ -d ${TMPDIR} ] || die "$TMPDIR doesn't exist"
|
|
FLAG_SAVE_LOG_FILE=
|
|
fi
|
|
[ -z "${OPT_VERBOSE}" ] && LOGFILE="${TMPDIR}/noisy.log"
|
|
|
|
[ -d ${TMPDIR} ] || mkdir -p ${TMPDIR} || exit 1
|
|
cd ${TMPDIR} || exit 1
|
|
echo "Running $0 $*" > "$LOGFILE"
|
|
log date
|
|
debug "DEV_DEBUG_FORCE=($DEV_DEBUG_FORCE)"
|
|
debug "OPT_CLEANUP=($OPT_CLEANUP)"
|
|
debug "OPT_BIOS=($OPT_BIOS)"
|
|
debug "OPT_FORCE=($OPT_FORCE)"
|
|
debug "OPT_IMAGE=($OPT_IMAGE)"
|
|
debug "OPT_KERNEL=($OPT_KERNEL)"
|
|
debug "FLAG_SAVE_LOG_FILE=($FLAG_SAVE_LOG_FILE)"
|
|
echo "Saving verbose log as $LOGFILE"
|
|
trap cleanup EXIT
|
|
|
|
if [ -n "${DEV_DEBUG_FORCE}" ] && [ -z "${OPT_FORCE}" ]; then
|
|
info "Not gonna do anything without the --force option."
|
|
exit 0
|
|
fi
|
|
|
|
|
|
# Make sure we have the programs we need
|
|
need="futility"
|
|
[ -z "${OPT_BIOS}" ] && need="$need flashrom"
|
|
[ -z "${OPT_KERNEL}" ] && need="$need cgpt"
|
|
require_utils $need
|
|
|
|
|
|
# Assuming we're on a ChromeOS device, see what we know.
|
|
set +e
|
|
log crossystem --all
|
|
log rootdev -s
|
|
log ls -aCF /root
|
|
log ls -aCF /mnt/stateful_partition
|
|
devs=$(awk '/(mmcblk[0-9])$|(sd[a-z])$/ {print "/dev/"$4}' /proc/partitions)
|
|
for d in $devs; do
|
|
log cgpt show $d
|
|
done
|
|
log flashrom -V -p host --wp-status
|
|
tpm_fwver=$(crossystem tpm_fwver) || tpm_fwver="UNKNOWN"
|
|
tpm_kernver=$(crossystem tpm_kernver) || tpm_kernver="UNKNOWN"
|
|
set -e
|
|
|
|
|
|
info "Extracting BIOS components..."
|
|
if [ -n "${OPT_BIOS}" ]; then
|
|
# If we've already got a file, just extract everything.
|
|
log futility dump_fmap -x "${OPT_BIOS}"
|
|
fix_old_names
|
|
else
|
|
# First try pulling just the components we want (using new-style names)
|
|
if log flashrom -p host -r /dev/null \
|
|
-i"GBB":GBB \
|
|
-i"FMAP":FMAP \
|
|
-i"VBLOCK_A":VBLOCK_A \
|
|
-i"VBLOCK_B":VBLOCK_B \
|
|
-i"FW_MAIN_A":FW_MAIN_A \
|
|
-i"FW_MAIN_B":FW_MAIN_B ; then
|
|
log futility dump_fmap FMAP
|
|
else
|
|
info "Couldn't read individual components. Read the whole thing..."
|
|
if log flashrom -p host -r bios.rom ; then
|
|
log futility dump_fmap -x bios.rom
|
|
fix_old_names
|
|
else
|
|
logdie "Can't read BIOS at all. Giving up."
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
info "Pulling root and recovery keys from GBB..."
|
|
log futility gbb_utility -g --rootkey rootkey.vbpubk \
|
|
--recoverykey recoverykey.vbpubk \
|
|
"GBB" || logdie "Unable to extract keys from GBB"
|
|
log futility vbutil_key --unpack rootkey.vbpubk
|
|
log futility vbutil_key --unpack recoverykey.vbpubk
|
|
futility vbutil_key --unpack rootkey.vbpubk |
|
|
grep -q b11d74edd286c144e1135b49e7f0bc20cf041f10 &&
|
|
info " Looks like dev-keys"
|
|
# Okay if one of the firmware verifications fails
|
|
set +e
|
|
for fw in A B; do
|
|
infon "Verify firmware ${fw} with root key: "
|
|
log futility vbutil_firmware --verify "VBLOCK_${fw}" \
|
|
--signpubkey rootkey.vbpubk \
|
|
--fv "FW_MAIN_${fw}" --kernelkey "kern_subkey_${fw}.vbpubk" ; result
|
|
if [ "${LAST_RESULT}" = "0" ]; then
|
|
# rerun to get version numbers
|
|
futility vbutil_firmware --verify "VBLOCK_${fw}" \
|
|
--signpubkey rootkey.vbpubk \
|
|
--fv "FW_MAIN_${fw}" > tmp.txt
|
|
ver=$(format_as_tpm_version tmp.txt)
|
|
info " TPM=${tpm_fwver}, this=${ver}"
|
|
fi
|
|
done
|
|
set -e
|
|
|
|
info "Examining kernels..."
|
|
if [ -n "${OPT_KERNEL}" ]; then
|
|
kernparts="${OPT_KERNEL}"
|
|
elif [ -n "${OPT_IMAGE}" ]; then
|
|
if [ -f "${OPT_IMAGE}" ]; then
|
|
kernparts=$(extract_kerns_from_file "${OPT_IMAGE}")
|
|
else
|
|
kernparts=$(cgpt find -t kernel "${OPT_IMAGE}")
|
|
fi
|
|
else
|
|
kernparts=$(cgpt find -t kernel)
|
|
fi
|
|
[ -n "${kernparts}" ] || logdie "No kernels found"
|
|
|
|
# Okay if any of the kernel verifications fails
|
|
set +e
|
|
kc=0
|
|
for kname in ${kernparts}; do
|
|
if [ -f "${kname}" ]; then
|
|
kfile="${kname}"
|
|
else
|
|
kfile="kern_${kc}"
|
|
debug "copying ${kname} to ${kfile}..."
|
|
log dd if="${kname}" of="${kfile}"
|
|
fi
|
|
|
|
infon "Kernel ${kname}: "
|
|
log futility vbutil_keyblock --unpack "${kfile}" ; result
|
|
if [ "${LAST_RESULT}" != "0" ]; then
|
|
loghead od -Ax -tx1 "${kfile}"
|
|
else
|
|
# Test each kernel with each key
|
|
for key in kern_subkey_A.vbpubk kern_subkey_B.vbpubk recoverykey.vbpubk; do
|
|
infon " Verify ${kname} with $key: "
|
|
log futility vbutil_kernel --verify "${kfile}" --signpubkey "$key" ; result
|
|
if [ "${LAST_RESULT}" = "0" ]; then
|
|
# rerun to get version numbers
|
|
futility vbutil_kernel --verify "${kfile}" --signpubkey "$key" > tmp.txt
|
|
ver=$(format_as_tpm_version tmp.txt)
|
|
info " TPM=${tpm_kernver} this=${ver}"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
kc=$(expr $kc + 1)
|
|
done
|
|
|
|
exit 0
|