summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2011-11-08 10:06:06 -0800
committerBill Richardson <wfrichar@chromium.org>2011-11-09 16:38:15 -0800
commit9b717be86ba6155a7542bf1649dd3ab2dbc2dc3b (patch)
treeae12e6a45c6444e0a5de566f9c4c05fa03902c11
parentf1282d321f40ab52e696795b28ba77a2083edb5f (diff)
downloadvboot-9b717be86ba6155a7542bf1649dd3ab2dbc2dc3b.tar.gz
New and improved dev_debug_vboot
This new version adds a bunch more output, displays the TPM rollback version values (if it can; Cr-48 doesn't export this info through crossystem), looks for and validates all kernels on all devices, etc. It also add some command-line arguments to use to examine files containing BIOS, kernel, and disk images. BUG=chromium-os:6676 TEST=manual Boot, wait a minute or so, then log in and go to chrome://system Click the Expand button for "verified boot". You should see a bunch of useful text describing the firmware and kernel partitions. I tried this on Cr-48, Stumpy, and Kaen. Change-Id: I2d9aa0fcb0c12cf2b951ce9e2316b89532901125 Reviewed-on: https://gerrit.chromium.org/gerrit/11327 Reviewed-by: Bill Richardson <wfrichar@chromium.org> Tested-by: Bill Richardson <wfrichar@chromium.org>
-rwxr-xr-xutility/dev_debug_vboot401
1 files changed, 270 insertions, 131 deletions
diff --git a/utility/dev_debug_vboot b/utility/dev_debug_vboot
index 2f0b643b..760d6f86 100755
--- a/utility/dev_debug_vboot
+++ b/utility/dev_debug_vboot
@@ -1,4 +1,4 @@
-#!/bin/sh -u
+#!/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.
@@ -9,30 +9,65 @@
# summary is printed on stdout, more detailed information and working files are
# left in a log directory.
#
+##############################################################################
-PATH=/bin:/sbin:/usr/bin:/usr/sbin
+# 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
-TMPDIR=$(mktemp -d /tmp/debug_vboot_XXXXXXXXX)
-LOGFILE="${TMPDIR}/noisy.log"
PUBLOGFILE="/var/log/debug_vboot_noisy.log"
-ARCH="$(crossystem arch)"
-ROOT_BASE=$(rootdev -s -d)
+OPT_CLEANUP=
+OPT_BIOS=
+OPT_IMAGE=
+OPT_KERNEL=
+OPT_VERBOSE=
-if [ "${ARCH}" = "arm" ]; then
- HD='/dev/mmcblk0'
- PARTITION_PREFIX='p'
-else
- HD='/dev/sda'
- PARTITION_PREFIX=''
-fi
+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 [ -z "${USE_EXISTING:-}" ]; then
- cp -f "${LOGFILE}" "${PUBLOGFILE}"
- info "exporting log file as ${PUBLOGFILE}"
+ 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 "${CLEANUP:-}" ]; then
+ if [ -n "${OPT_CLEANUP}" ] && [ -d "${TMPDIR}" ] ; then
cd /
rm -rf "${TMPDIR}"
fi
@@ -53,6 +88,10 @@ infon() {
echo "#" "$@" >> "$LOGFILE"
}
+debug() {
+ echo "#" "$@" >> "$LOGFILE"
+}
+
log() {
echo "+" "$@" >> "$LOGFILE"
"$@" >> "$LOGFILE" 2>&1
@@ -64,161 +103,261 @@ loghead() {
}
logdie() {
- echo "+" "$@" >> "$LOGFILE"
+ echo "+ERROR:" "$@" >> "$LOGFILE"
die "$@"
}
result() {
- if [ "$?" = "0" ]; then
+ LAST_RESULT=$?
+ if [ "${LAST_RESULT}" = "0" ]; then
info "OK"
else
info "FAILED"
fi
}
-require_chromeos_bios() {
- log cgpt show "${HD}"
- log rootdev -s
- log crossystem --all
- log ls -aCF /root
- log ls -aCF /mnt/stateful_partition
-}
+require_utils() {
+ local missing
-# Search for files from the FMAP, in the order listed. Return the first one
-# found or die if none are there.
-find_name() {
- for fn in "$@"; do
- if [ -e "$fn" ]; then
- echo "$fn"
- return
+ missing=
+ for tool in $* ; do
+ if ! type "$tool" >/dev/null 2>&1 ; then
+ missing="$missing $tool"
fi
done
- echo "+ no files named $@" >> "$LOGFILE"
- exit 1
+ if [ -n "$missing" ]; then
+ logdie "can't find these programs: $missing"
+ fi
}
-# Iterate through block devices, as soon as the first removable USB device
-# which is not mounted as root file system is found - consider it the USB
-# flash stick and return its device name.
-find_usb_device() {
- dm_dev=$(rootdev | cut -f3 -d/)
- real_dev=$(echo ${ROOT_BASE} | cut -f3 -d/)
- block_dir='/sys/block'
- for d in $(ls ${block_dir}); do
- if [ "$d" = "${dm_dev}" -o "$d" = "${real_dev}" ]; then
- continue # Skip root file system.
- fi
- if [ "$(readlink -f ${block_dir}/$d | grep usb)" = "" ]; then
- continue # Not a usb device.
- fi
- r=${block_dir}/$d/removable
- if [ -f $r -a "$(cat $r)" = "1" ]; then
- echo /dev/$d
- return
- 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
-trap cleanup EXIT
-# Parse args
-if [ -n "${1:-}" ]; then
- if [ "$1" = "--cleanup" ]; then
- CLEANUP=1
- else
- TMPDIR="$1"
- [ -d ${TMPDIR} ] || die "${TMPDIR} doesn't exist"
- USE_EXISTING=yes
- fi
+# Pre-parse args to replace actual args with a sanitized version.
+TEMP=$(getopt -o hvb:i:k:c --long help,bios:,image:,kernel:,cleanup \
+ -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
+ ;;
+ -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 "$0 $*" > "$LOGFILE"
+echo "Running $0 $*" > "$LOGFILE"
log date
+debug "OPT_CLEANUP=($OPT_CLEANUP)"
+debug "OPT_BIOS=($OPT_BIOS)"
+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
-BIOS=bios.rom
-# Find BIOS and kernel images
-if [ -n "${USE_EXISTING:-}" ]; then
- info "Using images in $(pwd)/"
-else
- require_chromeos_bios
- info "Extracting BIOS image from flash..."
- log flashrom -p internal:bus=spi --wp-status
- log flashrom -p internal:bus=spi -r ${BIOS}
+# Make sure we have the programs we need
+need="vbutil_key vbutil_keyblock vbutil_kernel vbutil_firmware"
+[ -z "${OPT_BIOS}" ] && need="$need flashrom"
+[ -z "${OPT_KERNEL}" ] && need="$need cgpt"
+require_utils $need
- HD_KERN_A="${HD}${PARTITION_PREFIX}2"
- HD_KERN_B="${HD}${PARTITION_PREFIX}4"
- if [ "${ROOT_BASE}" != "${HD}" ]; then
- # Not running off HD, must be ruining off USB.
- USB_KERN_A="${ROOT_BASE}2"
- else
- USB_DEV="$(find_usb_device)"
- if [ -n "${USB_DEV}" ]; then
- USB_KERN_A="${USB_DEV}2"
- fi
- fi
- info "Extracting kernel images from drives..."
- log dd if=${HD_KERN_A} of=hd_kern_a.blob
- log dd if=${HD_KERN_B} of=hd_kern_b.blob
- if [ -n "${USB_KERN_A:-}" ]; then
- log dd if=${USB_KERN_A} of=usb_kern_a.blob
- fi
-fi
+# 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 internal:bus=spi --wp-status
+tpm_fwver=$(crossystem tpm_fwver) || tpm_fwver="UNKNOWN"
+tpm_kernver=$(crossystem tpm_kernver) || tpm_kernver="UNKNOWN"
+set -e
-# Make sure we have something to work on
-[ -f "$BIOS" ] || logdie "no BIOS image found"
-ls *kern*.blob >/dev/null 2>&1 || logdie "no kernel images found"
info "Extracting BIOS components..."
-log dump_fmap -x ${BIOS} || logdie "Unable to extract BIOS components"
-
-# Find the FMAP regions we're interested in. Look first for the new names, then
-# the old names.
-area_gbb=$(find_name GBB GBB_Area) || \
- logdie "no area_gbb"
-area_vblock_a=$(find_name VBLOCK_A Firmware_A_Key) || \
- logdie "no area_vblock_a"
-area_vblock_b=$(find_name VBLOCK_B Firmware_B_Key) || \
- logdie "no area_vblock_b"
-area_fw_main_a=$(find_name FW_MAIN_A Firmware_A_Data) || \
- logdie "no area_fw_main_a"
-area_fw_main_b=$(find_name FW_MAIN_B Firmware_B_Data) || \
- logdie "no area_fw_main_a"
+if [ -n "${OPT_BIOS}" ]; then
+ # If we've already got a file, just extract everything.
+ log dump_fmap -x "${OPT_BIOS}"
+ fix_old_names
+else
+ # Read it from the flash
+ if log flashrom -p internal:bus=spi -r bios.rom ; then
+ # If we can read the whole BIOS at once, great.
+ log dump_fmap -x bios.rom
+ fix_old_names
+ else
+ # Otherwise pull just the components we want (implying new-style names)
+ info " ...individually..."
+ log flashrom -p internal:bus=spi -r /dev/null \
+ -i"GBB":GBB \
+ -i"VBLOCK_A":VBLOCK_A \
+ -i"VBLOCK_B":VBLOCK_B \
+ -i"FW_MAIN_A":FW_MAIN_A \
+ -i"FW_MAIN_B":FW_MAIN_B
+ fi
+fi
info "Pulling root and recovery keys from GBB..."
log gbb_utility -g --rootkey rootkey.vbpubk --recoverykey recoverykey.vbpubk \
- "$area_gbb" || logdie "Unable to extract keys from GBB"
+ "GBB" || logdie "Unable to extract keys from GBB"
log vbutil_key --unpack rootkey.vbpubk
log vbutil_key --unpack recoverykey.vbpubk
+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 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
+ 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"
-infon "Verify firmware A with root key... "
-log vbutil_firmware --verify "$area_vblock_a" --signpubkey rootkey.vbpubk \
- --fv "$area_fw_main_a" --kernelkey kernel_subkey_a.vbpubk ; result
-infon "Verify firmware B with root key... "
-log vbutil_firmware --verify "$area_vblock_b" --signpubkey rootkey.vbpubk \
- --fv "$area_fw_main_b" --kernelkey kernel_subkey_b.vbpubk ; result
+# 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
-for key in kernel_subkey_a.vbpubk kernel_subkey_b.vbpubk; do
- infon "Test $key... "
- log vbutil_key --unpack $key ; result
-done
+ infon "Kernel ${kname}: "
+ log 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 vbutil_kernel --verify "${kfile}" --signpubkey "$key" ; result
+ if [ "${LAST_RESULT}" = "0" ]; then
+ # rerun to get version numbers
+ vbutil_kernel --verify "${kfile}" --signpubkey "$key" > tmp.txt
+ ver=$(format_as_tpm_version tmp.txt)
+ info " TPM=${tpm_kernver} this=${ver}"
+ fi
+ done
+ fi
-for keyblock in *kern*.blob; do
- infon "Test $keyblock... "
- log vbutil_keyblock --unpack $keyblock ; result
- loghead od -Ax -tx1 $keyblock
+ kc=$(expr $kc + 1)
done
-# Test each kernel with each key
-for key in kernel_subkey_a.vbpubk kernel_subkey_b.vbpubk recoverykey.vbpubk; do
- for kern in *kern*.blob; do
- infon "Verify $kern with $key... "
- log vbutil_kernel --verify $kern --signpubkey $key ; result
- done
-done
+exit 0