mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-09 17:11:42 +00:00
BUG=chromium-os:10213 TEST=manual From a shell, run cd /tmp /usr/share/vboot/bin/make_dev_ssd.sh --save_config foo You should see messages about kernel A, kernel B, and kernel C. It doesn't matter what those messages are (well, it does, but testing that is way too complicated and only useful for dev-mode hacking). Change-Id: I32aaeae18fb9dd957ab17a452d1ea6d7cd8fe788 Review URL: http://codereview.chromium.org/5698004
296 lines
9.4 KiB
Bash
Executable File
296 lines
9.4 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# Copyright (c) 2010 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.
|
|
#
|
|
# This script can change key (usually developer keys) and kernel config
|
|
# of a kernels on SSD.
|
|
|
|
SCRIPT_BASE="$(dirname "$0")"
|
|
. "$SCRIPT_BASE/common.sh"
|
|
load_shflags || exit 1
|
|
|
|
# Constants used by DEFINE_*
|
|
VBOOT_BASE='/usr/share/vboot'
|
|
DEFAULT_KEYS_FOLDER="$VBOOT_BASE/devkeys"
|
|
DEFAULT_BACKUP_FOLDER='/mnt/stateful_partition/backups'
|
|
|
|
# DEFINE_string name default_value description flag
|
|
DEFINE_string image "/dev/sda" "Path to device or image file" "i"
|
|
DEFINE_string keys "$DEFAULT_KEYS_FOLDER" "Path to folder of dev keys" "k"
|
|
DEFINE_boolean remove_rootfs_verification \
|
|
$FLAGS_FALSE "Modify kernel boot config to disable rootfs verification" ""
|
|
DEFINE_string backup_dir \
|
|
"$DEFAULT_BACKUP_FOLDER" "Path of directory to store kernel backups" ""
|
|
DEFINE_boolean debug $FLAGS_FALSE "Provide debug messages" "d"
|
|
DEFINE_string save_config "" \
|
|
"Base filename to store kernel configs to, instead of resigning." ""
|
|
DEFINE_string set_config "" \
|
|
"Base filename to load kernel configs from" ""
|
|
|
|
# Parse command line
|
|
FLAGS "$@" || exit 1
|
|
eval set -- "$FLAGS_ARGV"
|
|
|
|
# Globals
|
|
# ----------------------------------------------------------------------------
|
|
set -e
|
|
|
|
# a log file to keep the output results of executed command
|
|
EXEC_LOG="$(make_temp_file)"
|
|
|
|
# Functions
|
|
# ----------------------------------------------------------------------------
|
|
# Reports error message and exit(1)
|
|
err_die() {
|
|
echo "ERROR: $*" 1>&2
|
|
exit 1
|
|
}
|
|
|
|
# Returns true if we're running in debug mode
|
|
is_debug_mode() {
|
|
[ "$FLAGS_debug" = $FLAGS_TRUE ]
|
|
}
|
|
|
|
# Prints messages (in parameters) in debug mode
|
|
debug_msg() {
|
|
if is_debug_mode; then
|
|
echo "DEBUG: $*" 1>&2
|
|
fi
|
|
}
|
|
|
|
# Removes rootfs verification from kernel boot parameter
|
|
remove_rootfs_verification() {
|
|
echo "$*" | sed '
|
|
s| root=/dev/dm-0 | root=/dev/sd%D%P |
|
|
s| dm_verity[^=]*=[-0-9]*||g
|
|
s| dm="[^"]*"||
|
|
s| ro | rw |'
|
|
}
|
|
|
|
# Checks if rootfs verification is enabled from kernel boot parameter
|
|
is_rootfs_verification_enabled() {
|
|
echo "$*" | grep -q 'root=/dev/dm-0'
|
|
}
|
|
|
|
# Wrapped version of dd
|
|
mydd() {
|
|
# oflag=sync is safer, but since we need bs=512, syncing every block would be
|
|
# very slow.
|
|
dd "$@" >"$EXEC_LOG" 2>&1 ||
|
|
err_die "Failed in [dd $@], Message: $(cat "$EXEC_LOG")"
|
|
}
|
|
|
|
# Prints a more friendly name from kernel index number
|
|
cros_kernel_name() {
|
|
case $1 in
|
|
2)
|
|
echo "Kernel A"
|
|
;;
|
|
4)
|
|
echo "Kernel B"
|
|
;;
|
|
6)
|
|
echo "Kernel C"
|
|
;;
|
|
*)
|
|
err_die "unknown kernel index: $1"
|
|
esac
|
|
}
|
|
|
|
# Resigns a kernel on SSD or image.
|
|
resign_ssd_kernel() {
|
|
# bs=512 is the fixed block size for dd and cgpt
|
|
local bs=512
|
|
local ssd_device="$1"
|
|
|
|
# reasonable size for current kernel partition
|
|
local min_kernel_size=32000
|
|
local max_kernel_size=65536
|
|
local resigned_kernels=0
|
|
|
|
for kernel_index in 2 4 6; do
|
|
local old_blob="$(make_temp_file)"
|
|
local new_blob="$(make_temp_file)"
|
|
local name="$(cros_kernel_name $kernel_index)"
|
|
local rootfs_index="$(($kernel_index + 1))"
|
|
|
|
debug_msg "Probing $name information"
|
|
local offset size
|
|
offset="$(partoffset "$ssd_device" "$kernel_index")" ||
|
|
err_die "Failed to get partition $kernel_index offset from $ssd_device"
|
|
size="$(partsize "$ssd_device" "$kernel_index")" ||
|
|
err_die "Failed to get partition $kernel_index size from $ssd_device"
|
|
if [ ! $size -gt $min_kernel_size ]; then
|
|
echo "INFO: $name seems too small ($size), ignored."
|
|
continue
|
|
fi
|
|
if [ ! $size -le $max_kernel_size ]; then
|
|
echo "INFO: $name seems too large ($size), ignored."
|
|
continue
|
|
fi
|
|
|
|
debug_msg "Reading $name from partition $kernel_index"
|
|
mydd if="$ssd_device" of="$old_blob" bs=$bs skip=$offset count=$size
|
|
|
|
debug_msg "Checking if $name is valid"
|
|
local kernel_config
|
|
if ! kernel_config="$(dump_kernel_config "$old_blob" 2>"$EXEC_LOG")"; then
|
|
debug_msg "dump_kernel_config error message: $(cat "$EXEC_LOG")"
|
|
echo "INFO: $name: no kernel boot information, ignored."
|
|
continue
|
|
fi
|
|
|
|
if [ -n "${FLAGS_save_config}" ]; then
|
|
# Save current kernel config
|
|
local old_config_file
|
|
old_config_file="${FLAGS_save_config}.$kernel_index"
|
|
echo "Saving $name config to $old_config_file"
|
|
echo "$kernel_config" > "$old_config_file"
|
|
# Just save; don't resign
|
|
continue
|
|
fi
|
|
|
|
if [ -n "${FLAGS_set_config}" ]; then
|
|
# Set new kernel config from file
|
|
local new_config_file
|
|
new_config_file="${FLAGS_set_config}.$kernel_index"
|
|
kernel_config="$(cat "$new_config_file")" ||
|
|
err_die "Failed to read new kernel config from $new_config_file"
|
|
debug_msg "New kernel config: $kernel_config)"
|
|
echo "$name: Replaced config from $new_config_file"
|
|
fi
|
|
|
|
if [ ${FLAGS_remove_rootfs_verification} = $FLAGS_FALSE ]; then
|
|
debug_msg "Bypassing rootfs verification check"
|
|
elif ! is_rootfs_verification_enabled "$kernel_config"; then
|
|
echo "INFO: $name: rootfs verification was not enabled."
|
|
else
|
|
debug_msg "Changing boot parameter to remove rootfs verification"
|
|
kernel_config="$(remove_rootfs_verification "$kernel_config")"
|
|
debug_msg "New kernel config: $kernel_config"
|
|
echo "$name: Disabled rootfs verification."
|
|
fi
|
|
|
|
local new_kernel_config_file="$(make_temp_file)"
|
|
echo "$kernel_config" >"$new_kernel_config_file"
|
|
|
|
debug_msg "Re-signing $name from $old_blob to $new_blob"
|
|
debug_msg "Using key: $KERNEL_DATAKEY"
|
|
vbutil_kernel \
|
|
--repack "$new_blob" \
|
|
--keyblock "$KERNEL_KEYBLOCK" \
|
|
--config "$new_kernel_config_file" \
|
|
--signprivate "$KERNEL_DATAKEY" \
|
|
--oldblob "$old_blob" >"$EXEC_LOG" 2>&1 ||
|
|
err_die "Failed to resign $name. Message: $(cat "$EXEC_LOG")"
|
|
|
|
debug_msg "Creating new kernel image (vboot+code+config)"
|
|
local new_kern="$(make_temp_file)"
|
|
cp "$old_blob" "$new_kern"
|
|
mydd if="$new_blob" of="$new_kern" conv=notrunc
|
|
|
|
if is_debug_mode; then
|
|
debug_msg "for debug purposes, check *.dbgbin"
|
|
cp "$old_blob" old_blob.dbgbin
|
|
cp "$new_blob" new_blob.dbgbin
|
|
cp "$new_kern" new_kern.dbgbin
|
|
fi
|
|
|
|
debug_msg "Verifying new kernel and keys"
|
|
vbutil_kernel \
|
|
--verify "$new_kern" \
|
|
--signpubkey "$KERNEL_PUBKEY" --verbose >"$EXEC_LOG" 2>&1 ||
|
|
err_die "Failed to verify new $name. Message: $(cat "$EXEC_LOG")"
|
|
|
|
debug_msg "Backup old kernel blob"
|
|
local backup_date_time="$(date +'%Y%m%d_%H%M%S')"
|
|
local backup_name="$(echo "$name" | sed 's/ /_/g; s/^K/k/')"
|
|
local backup_file_name="${backup_name}_${backup_date_time}.bin"
|
|
local backup_file_path="$FLAGS_backup_dir/$backup_file_name"
|
|
if mkdir -p "$FLAGS_backup_dir" &&
|
|
cp -f "$old_blob" "$backup_file_path"; then
|
|
echo "Backup of $name is stored in: $backup_file_path"
|
|
else
|
|
echo "WARNING: Cannot create file in $FLAGS_backup_dir... Ignore backups."
|
|
fi
|
|
|
|
debug_msg "Writing $name to partition $kernel_index"
|
|
mydd \
|
|
if="$new_kern" \
|
|
of="$ssd_device" \
|
|
seek=$offset \
|
|
bs=$bs \
|
|
count=$size \
|
|
conv=notrunc
|
|
resigned_kernels=$(($resigned_kernels + 1))
|
|
|
|
debug_msg "Make the root filesystem writable if needed."
|
|
# TODO(hungte) for safety concern, a more robust way would be to:
|
|
# (1) change kernel config to ro
|
|
# (2) check if we can enable rw mount
|
|
# (3) change kernel config to rw
|
|
if [ ${FLAGS_remove_rootfs_verification} = $FLAGS_TRUE ]; then
|
|
local root_offset_sector=$(partoffset "$ssd_device" $rootfs_index)
|
|
local root_offset_bytes=$((root_offset_sector * 512))
|
|
if ! is_ext2 "$ssd_device" "$root_offset_bytes"; then
|
|
debug_msg "Non-ext2 partition: $ssd_device$rootfs_index, skip."
|
|
elif ! rw_mount_disabled "$ssd_device" "$root_offset_bytes"; then
|
|
debug_msg "Root file system is writable. No need to modify."
|
|
else
|
|
# disable the RO ext2 hack
|
|
debug_msg "Disabling rootfs ext2 RO bit hack"
|
|
enable_rw_mount "$ssd_device" "$root_offset_bytes" >"$EXEC_LOG" 2>&1 ||
|
|
err_die "Failed turning off rootfs RO bit. OS may be corrupted. " \
|
|
"Message: $(cat "$EXEC_LOG")"
|
|
fi
|
|
fi
|
|
|
|
# Sometimes doing "dump_kernel_config" or other I/O now (or after return to
|
|
# shell) will get the data before modification. Not a problem now, but for
|
|
# safety, let's try to sync more.
|
|
sync; sync; sync
|
|
|
|
echo "$name: Re-signed with developer keys successfully."
|
|
done
|
|
|
|
# If we saved the kernel config, exit now so we don't print an error
|
|
if [ -n "${FLAGS_save_config}" ]; then
|
|
echo "(Kernels have not been resigned.)"
|
|
exit 0
|
|
fi
|
|
|
|
return $resigned_kernels
|
|
}
|
|
|
|
# Main
|
|
# ----------------------------------------------------------------------------
|
|
main() {
|
|
local num_signed=0
|
|
# Check parameters
|
|
KERNEL_KEYBLOCK="$FLAGS_keys/kernel.keyblock"
|
|
KERNEL_DATAKEY="$FLAGS_keys/kernel_data_key.vbprivk"
|
|
KERNEL_PUBKEY="$FLAGS_keys/kernel_subkey.vbpubk"
|
|
|
|
debug_msg "Prerequisite check"
|
|
ensure_files_exist \
|
|
"$KERNEL_KEYBLOCK" \
|
|
"$KERNEL_DATAKEY" \
|
|
"$KERNEL_PUBKEY" \
|
|
"$FLAGS_image" ||
|
|
exit 1
|
|
|
|
resign_ssd_kernel "$FLAGS_image" || num_signed=$?
|
|
|
|
debug_msg "Complete."
|
|
if [ $num_signed -gt 0 -a $num_signed -le 2 ]; then
|
|
# signed 1 or two kernels
|
|
echo "Successfully re-signed $num_signed kernel(s) on device $FLAGS_image".
|
|
else
|
|
err_die "Failed re-signing kernels."
|
|
fi
|
|
}
|
|
|
|
main
|