Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(5452)

Unified Diff: chromeos-base/chromeos-initramfs/files/init

Issue 4120005: kernel, chromeos-initramfs: overhaul for recovery support (Closed) Base URL: http://git.chromium.org/git/chromiumos-overlay.git
Patch Set: disable shim script in official mode pending http://crosbug/8390 Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chromeos-base/chromeos-initramfs/files/init
diff --git a/chromeos-base/chromeos-initramfs/files/init b/chromeos-base/chromeos-initramfs/files/init
index 3edc0b6840a6a658784e82109a40a50006bddc8d..a1aabd36e905c0023daf66b792876ca88fc0aa3d 100644
--- a/chromeos-base/chromeos-initramfs/files/init
+++ b/chromeos-base/chromeos-initramfs/files/init
@@ -9,12 +9,38 @@
# USB card partition and mount point.
USB_DEVS="sdb3 sdc3 mmcblk1p3"
+USB_SHIM_DEVS="sdb1 sdc1 mmcblk1p1"
USB_MNT=/usb
REAL_USB_DEV=
+DST=
+
STATEFUL_MNT=/stateful
STATE_DEV=
+LOG_DEV=
+LOG_FILE="/log/recovery.log"
+
+TPM_B_LOCKED=
+TPM_PP_LOCKED=
+
+# Developer script to run
+SHIM_SCRIPT="$STATEFUL_MNT/userdir/runme"
+SHIM_VBLOCK="$STATEFUL_MNT/userdir/runme.vblock"
+
+KERN_B_VBLOCK="$STATEFUL_MNT/vmlinuz_hd.vblock"
+REAL_KERN_B_HASH=
+
+MOVE_MOUNTS="/sys /proc /dev"
+
+# To be updated to keep logging after move_mounts.
+TTY_PATH="/dev/tty"
+TAIL_PID=
+
+# Used to ensure the factory check only occurs with
+# a properly matched root and kernel.
+UNOFFICIAL_ROOT=0
+
# Size of the root ramdisk.
TMPFS_SIZE=300M
@@ -23,6 +49,13 @@ on_error() {
# start a shell) because it would be trivially easy to get here (just unplug
# the USB drive after the kernel starts but before the USB drives are probed
# by the kernel) and starting a shell here would be a BIG security hole.
+ log
+ log
+ log "An unrecoverable error occurred during recovery!"
+ log
+ log "Please try again or try a newer recovery image."
+ save_log_file
+ sleep 1d
exit 1
}
@@ -33,141 +66,248 @@ initial_mounts() {
if ! mount -t devtmpfs -o mode=0755 none /dev; then
mount -t tmpfs -o mode=0755 none /dev
mknod -m 0600 /dev/console c 5 1
- mknod -m 0600 /dev/tty1 c 4 1
+ mknod -m 0601 /dev/tty1 c 4 1
+ mknod -m 0601 /dev/tty2 c 4 2
+ mknod -m 0601 /dev/tty3 c 4 3
+ mknod -m 0600 /dev/tpm0 c 10 224
mknod /dev/null c 1 3
fi
- mkdir /dev/pts
+ mkdir -p /dev/pts
mount -t devpts -o noexec,nosuid none /dev/pts || true
}
+# Look for a device with our GPT ID.
+wait_for_gpt_root() {
+ [ -z "$KERN_ARG_KERN_GUID" ] && return 1
+ dlog -n "Looking for rootfs using kern_guid..."
+ for try in $(seq 20); do
+ plog " ."
+ kern=$(cgpt find -1 -u $KERN_ARG_KERN_GUID)
+ # We always try ROOT-A in recovery.
+ newroot="${kern%[0-9]*}3"
+ if [ -b "$newroot" ]; then
+ USB_DEV="$newroot"
+ dlog "Found $USB_DEV"
+ return 0
+ fi
+ sleep 1
+ done
+ dlog "Failed waiting for kern_guid"
+ return 1
+}
+
# Look for any USB device.
wait_for_root() {
- echo -n "Waiting for $USB_DEVS to appear"
+ dlog -n "Waiting for $USB_DEVS to appear"
for try in $(seq 20); do
- echo -n "."
+ plog " ."
for dev in $USB_DEVS; do
if [ -b "/dev/${dev}" ]; then
USB_DEV="/dev/${dev}"
- echo "Found $USB_DEV"
+ dlog "Found $USB_DEV"
return 0
fi
done
sleep 1
done
- echo "Failed waiting for root!"
+ dlog "Failed waiting for root!"
return 1
}
# Wait for dm-0 to come up.
wait_for_dm_control() {
MAPPER_CONTROL=/dev/mapper/control
- echo -n "Waiting for $MAPPER_CONTROL to appear"
+ dlog -n "Waiting for $MAPPER_CONTROL to appear"
for try in $(seq 20); do
- echo -n "."
+ plog " ."
if [ -c "$MAPPER_CONTROL" ]; then
return 0
fi
sleep 1
done
- echo "Failed waiting for $MAPPER_CONTROL!"
+ dlog "Failed waiting for $MAPPER_CONTROL!"
+ return 1
+}
+
+check_if_dm_root() {
+ [ "$KERN_ARG_ROOT" = "/dev/dm-0" ]
+}
+
+# Attempt to find the root defined in the signed recovery
+# kernel we're booted into to. Exports REAL_USB_DEV if there
+# is a root partition that may be used - on succes or failure.
+find_official_root() {
+ plog "Checking for an official recovery image . . ."
+
+ # Check for a kernel selected root device or one in a well known location.
+ wait_for_gpt_root || wait_for_root || return 1
+
+ # Now see if it has a Chrome OS rootfs partition.
+ cgpt find -t rootfs "$(strip_partition "$USB_DEV")" || return 1
+ REAL_USB_DEV="$USB_DEV"
+
+ LOG_DEV="$(strip_partition "$USB_DEV")"1 # Default to stateful.
+
+ # Now see if the root should be integrity checked.
+ if check_if_dm_root; then
+ setup_dm_root || return 1
+ fi
+
+ mount_usb || return 1
+ return 0
+}
+
+find_developer_root() {
+ is_developer_mode || return 1
+ # Lock the TPM prior to using an untrusted root.
+ lock_tpm || return 1
+ plog "\nSearching for developer root . . ."
+ # If an official root could not be mounted, free up the underlying device
+ # if it is claimed by verity.
+ dmsetup remove "$DM_NAME"
+
+ # If we found a valid rootfs earlier, then we're done.
+ USB_DEV="$REAL_USB_DEV"
+ [ -z "$USB_DEV" ] && return 1
+ set_unofficial_root || return 1
+ mount_usb || return 1
+ return 0
+}
+
+# If this kernel image has been placed on a drive with only a
+# stateful partition, root detection will rightly fail. However,
+# we can still run a developer supplied script so we will pretend
+# stateful is the root (USB_DEV).
+find_shim_root() {
+ # Lock the TPM prior to using an untrusted root.
+ lock_tpm || return 1
+ plog "\nSearching for an alternate recovery image . . ."
+ dlog -n "Waiting for $USB_SHIM_DEVS to appear"
+ for try in $(seq 20); do
+ plog " ."
+ for dev in $USB_SHIM_DEVS; do
+ if [ -b "/dev/${dev}" ]; then
+ USB_DEV="/dev/${dev}"
+ REAL_USB_DEV="$USB_DEV"
+ dlog "Found $USB_DEV"
+ set_unofficial_root || on_error
+ mount_usb || return 1
+ return 0
+ fi
+ done
+ sleep 1
+ done
return 1
}
+# If we have a verified recovery root, ensure all blocks are valid before
+# handing it off to the installer.
+validate_recovery_root() {
+ # Allow test recovery roots that are unverified.
+ [ "$USB_DEV" = "/dev/dm-0" ] || return 0
+ is_unofficial_root && return 0
+
+ plog "Validating official recovery image . . . "
+ # Ensure the verified rootfs is fully intact or fail with no USB_DEV.
+ # REAL_USB_DEV is left intact.
+ # Correctness wins over speed for this.
+ if ! dd if="$USB_DEV" of=/dev/null bs=$((16 * 1024 * 1024)); then
+ dlog "Included root filesystem could not be verified."
+ log " failed!"
+ dmsetup remove "$DM_NAME" # Free up the real root for use.
+ USB_DEV=
+ return 1
+ fi
+ log " completed."
+ return 0
+}
+
setup_dm_root() {
- echo -n "Extracting the device mapper configuration..."
+ dlog -n "Extracting the device mapper configuration..."
+ # export_args can't handle dm="..." at present.
DMARG=$(cat /proc/cmdline | sed -e 's/.*dm="\([^"\]*\)".*/\1/g')
DM_NAME=$(echo "$DMARG" | cut -f1 -d' ')
# We override the reboot-to-recovery error behavior so that we can fail
# gracefully on invalid rootfs.
DM_TABLE="$(echo "$DMARG" | cut -f2 -d,) eio"
+
+ # Don't attempt to call dmsetup if the root device isn't one that was
+ # discovered as the creation process will hang.
+ # TODO(wad) once we pass the UUID this will be easier to robustly check.
+ if [ -n "${USB_DEV}" ]; then
+ [ "${DM_TABLE%$USB_DEV*}" = "${DM_TABLE}" ] && return 1
+ fi
+
if ! dmsetup create -r "$DM_NAME" --major 254 --minor 0 --table "$DM_TABLE"
then
- echo "Failed to configure device mapper root"
+ dlog "Failed to configure device mapper root"
return 1
fi
USB_DEV="/dev/dm-0"
if [ ! -b "$USB_DEV" ]; then
- mknod -m 0600 /dev/dm-0 b 254 0
+ mknod -m 0600 "$USB_DEV" b 254 0
fi
- echo "Created device mapper root $DM_NAME."
+ dlog "Created device mapper root $DM_NAME."
return 0
}
-# Look for a device with our GPT ID.
-wait_for_gpt_root() {
- echo -n "Looking for rootfs..."
- for try in $(seq 20); do
- echo -n "."
- newroot=$(/usr/sbin/chromeos-findrootfs)
- if [ -n "$newroot" ]; then
- USB_DEV="$newroot"
- echo "Found $USB_DEV"
- return 0
- fi
- sleep 1
- done
- echo "Failed waiting for kern_guid"
- return 1
-}
-
mount_usb() {
- echo -n "Mounting usb"
+ dlog -n "Mounting usb"
for try in $(seq 20); do
- echo -n "."
+ plog " ."
if mount -n -o ro "$USB_DEV" "$USB_MNT"; then
- echo "ok"
+ dlog "ok"
return 0
fi
sleep 1
done
- echo "Failed to mount usb!"
+ dlog "Failed to mount usb!"
return 1
}
-check_if_dm_root() {
- grep -q "root=/dev/dm-0" /proc/cmdline
-}
-
check_if_factory_install() {
+ if is_unofficial_root; then
+ dlog "Skipping factory install check."
+ return 1
+ fi
+
if [ -e "${USB_MNT}/root/.factory_installer" ]; then
- echo "Detected factory install."
+ log "Detected factory install."
return 0
fi
return 1
}
get_stateful_dev() {
- # Determine the STATE_DEV using rootdev on the target
- # to work seamlessly with dm-verity or normal devices.
- REAL_USB_DEV=$(rootdev -s "$USB_MNT")
STATE_DEV=${REAL_USB_DEV%[0-9]*}1
if [ ! -b "$STATE_DEV" ]; then
- echo "Failed to determine stateful device"
+ dlog "Failed to determine stateful device"
return 1
fi
return 0
}
mount_tmpfs() {
- echo "Mounting tmpfs..."
+ dlog "Mounting tmpfs..."
mount -n -t tmpfs tmpfs "$NEWROOT_MNT" -o "size=$TMPFS_SIZE"
return $?
}
copy_contents() {
- echo "Copying usb->tmpfs..."
+ log "Copying usb->tmpfs..."
(cd "${USB_MNT}" ; tar cf - . | (cd "${NEWROOT_MNT}" && tar xf -))
RES=$?
- echo "Copy returned with result $RES"
+ log "Copy returned with result $RES"
return $RES
}
copy_lsb() {
STATEFUL_LSB="dev_image/etc/lsb-factory"
mkdir -p "${NEWROOT_MNT}/mnt/stateful_partition/dev_image/etc"
- mount -n -o ro -t ext3 "$STATE_DEV" "$STATEFUL_MNT"
+ # Mounting ext3 as ext2 since the journal is unneeded in ro.
+ mount -n -o ro -t ext2 "$STATE_DEV" "$STATEFUL_MNT"
if [ -f "${STATEFUL_MNT}/${STATEFUL_LSB}" ]; then
- echo "Found ${STATEFUL_MNT}/${STATEFUL_LSB}"
+ log "Found ${STATEFUL_MNT}/${STATEFUL_LSB}"
cp -a "${STATEFUL_MNT}/${STATEFUL_LSB}" \
"${NEWROOT_MNT}/mnt/stateful_partition/${STATEFUL_LSB}"
fi
@@ -176,50 +316,434 @@ copy_lsb() {
}
move_mounts() {
- echo "Moving sys. proc, dev to $NEWROOT_MNT"
- mount -n -o move /sys "$NEWROOT_MNT/sys"
- mount -n -o move /proc "$NEWROOT_MNT/proc"
- mount -n -o move /dev "$NEWROOT_MNT/dev"
- echo "Done."
+ dlog "Moving sys. proc, dev to $NEWROOT_MNT"
+ for mnt in $MOVE_MOUNTS; do
+ mkdir -p "$NEWROOT_MNT$mnt"
+ mount -n -o move "$mnt" "$NEWROOT_MNT$mnt"
+ done
+ TTY_PATH="$NEWROOT_MNT/dev/tty"
+ dlog "Done."
+ return 0
+}
+
+unmove_mounts() {
+ dlog "Moving sys. proc, dev to $NEWROOT_MNT"
+ for mnt in $MOVE_MOUNTS; do
+ mount -n -o move "$NEWROOT_MNT$mnt" "$mnt"
+ done
+ TTY_PATH="/dev/tty"
+ dlog "Done."
return 0
}
unmount_usb() {
- echo "Unmounting $USB_MNT"
+ dlog "Unmounting $USB_MNT"
umount "$USB_MNT"
- echo
- echo "$USB_DEV can now be safely removed"
- echo
+ dlog
+ dlog "$USB_DEV can now be safely removed"
+ dlog
+ return 0
+}
+
+strip_partition() {
+ local dev="${1%[0-9]*}"
+ # handle mmcblk0p case as well
+ echo "${dev%p*}"
+}
+
+# Accomodate sd* or mmcblk* devices
+get_dst() {
+ DST=$(echo ${REAL_USB_DEV%[0-9]*} | \
+ tr -s '[0-9]' '0' | \
+ sed -e 's/sd[b-z]/sda/g')
+}
+
+dev_wait_or_error() {
+ is_developer_mode || on_error # terminal if we get here in regular mode.
+ log ""
+ log "A developer key change is required to proceed."
+ plog "Please wait 300 seconds . . ."
+ # TODO(wad) Divvy the total up into a few different prompts.
+ make_user_wait 300
+ log ""
+}
+
+recovery_wait() {
+ log ""
+ log "Preparing to recover your system image."
+ plog "If you do not wish to proceed, please reboot in the next 10 seconds ."
+ make_user_wait 10
+ log ""
+ log "System recovery is beginning."
+ log "Please do not disconnect from a power source or power down."
+ log ""
+}
+
+make_user_wait() {
+ local delay_in_sec="${1-300}"
+ while [ $delay_in_sec -gt 0 ]; do
+ plog " ."
+ sleep 1
+ delay_in_sec=$((delay_in_sec - 1))
+ done
+ log ""
+}
+
+check_key_or_wait() {
+ plog "Searching the system disk for a matching kernel key . . ."
+ if ! cgpt find -t kernel -M "$1" "$DST"; then
+ log " failed."
+ dev_wait_or_error
+ return 0
+ fi
+ log " found."
+
+ plog "Validating matching signature(s) . . ."
+ # If we found a keyblock, at the right offset, make sure it actually signed
+ # the subsequent payload.
+ local kdev=
+ for kdev in $(cgpt find -t kernel -M "$1" "$DST"); do
+ plog " ."
+ verify_kernel_signature "$kdev" "/tmp/kern.keyblock" || continue
+ log " done."
+ return 0
+ done
+
+ log " failed."
+ dev_wait_or_error
return 0
}
+# Never returns on success.
+attempt_shim_script() {
+ # TODO(wad) Add static root of trust validation then remove the next line.
+ # http://crosbug/8390
+ is_developer_mode || return 1
+
+ # Now we will either install a colocated Chromium OS image by
+ # checking the keys on KERN-B against any on disk (KERN-[ABC])
+ # or by checking a signed script on stateful.
+ dlog "Checking for a shim script . . ."
+ [ -x "$SHIM_SCRIPT" ] || return 1
+ [ -f "$SHIM_VBLOCK" ] || return 1
+ log "Shim script and signing file found!"
+
+ plog "Verifying the signature on the script . . ."
+ # Extract pubkey and check signature
+ if ! dev_sign_file --verify "$SHIM_SCRIPT" \
+ --vblock "$SHIM_VBLOCK" \
+ --keyblock /tmp/shim.keyblock; then
+ log " failed."
+ fi
+ log " done."
+
+ # If we're not in developer mode, this will be terminal on failure.
+ check_key_or_wait /tmp/shim.keyblock
+
+ # Run the user supplied script. It is done in the current environment
+ # to avoid needing anything other than the script/program on the partition.
+ log "Executing shim script . . ."
+
+ dlog "calling $SHIM_SCRIPT with exec"
+ # Fix up the input/output
+ stop_log_file
+ set +x
+ exec &> "$TTY_PATH"1
+ exec < "$TTY_PATH"1
+ # Call the script!
+ exec "$SHIM_SCRIPT"
+
+ # Never reached.
+ save_log_file
+ return 0
+}
+
+get_kern_b_device() {
+ # TODO(wad) By changing boot priority, could we end up
+ # checking the recovery image or the recovery image could not
+ # be in slot A. In that case, it should fail in normal mode.
+ KERN_B_DEV=${REAL_USB_DEV%[0-9]*}4
+ if [ ! -b "${KERN_B_DEV}" ]; then
+ return 1
+ fi
+ return 0
+}
+
+get_real_kern_b_hash() {
+ REAL_KERN_B_HASH=$(dd if="${KERN_B_DEV}" | \
+ sha1sum | \
+ cut -f1 -d' ')
+ [ -n "$REAL_KERN_B_HASH" ]
+}
+
+verify_kernel_signature() {
+ local kern_dev="$1"
+ local keyblock="$2"
+
+ if ! dd if="$kern_dev" of="/tmp/kern.bin"; then
+ return 1
+ fi
+
+ # Validates the signature and outputs a keyblock.
+ if ! vbutil_kernel --verify "/tmp/kern.bin" \
+ --keyblock "$keyblock"; then
+ return 1
+ fi
+ return 0
+}
+
+verify_install_kernel() {
+ get_kern_b_device || return 1
+ get_real_kern_b_hash || return 1
+
+ # TODO(wad) check signatures from stateful on kern b using the
+ # root of trust instead of using a baked in cmdline.
+ if [ "$REAL_KERN_B_HASH" != "$KERN_ARG_KERN_B_HASH" ]; then
+ if ! is_developer_mode; then
+ log "The recovery image cannot be verified."
+ return 1
+ fi
+
+ # Extract the kernel so that vbutil_kernel will happily consume it.
+ log "Checking the install kernel for a valid developer signature . . ."
+ verify_kernel_signature "$KERN_B_DEV" "/tmp/kern_b.keyblock" || return 1
+ check_key_or_wait /tmp/kern_b.keyblock
+ fi
+ return 0
+}
+
+touch_developer_mode_file() {
+ is_developer_mode || return 1
+ mount -n -o rw -t ext3 "$DST"1 "$STATEFUL_MNT" || return 1
+ touch "$STATEFUL_MNT/.developer_mode" || return 1
+ umount "$STATEFUL_MNT" || return 1
+ return 0
+}
+
+call_image_recovery_script() {
+ mount -t tmpfs -o mode=1777 none "$USB_MNT/tmp" || return 1
+ move_mounts || return 1
+
+ # Start the copy.
+ log ""
+ log "Beginning system image copy. This will take some time . . ."
+ log ""
+ # Until images are built with the installer keyblock in KERN-B by
+ # default, we keep copying over the installer vblock from the
+ # stateful partition.
+ # TODO(wad) http://crosbug/8378
+ export IS_RECOVERY_INSTALL=1
+ chroot "$USB_MNT" \
+ /usr/sbin/chromeos-install --run_as_root --yes \
+ --payload_image="$1" \
+ --use_payload_kern_b
+ if [ $? -eq 0 ]; then
+ log "System copy completed."
+ else
+ log "Error performing system recovery!"
+ fi
+
+ # Clean up doesn't need to be successful.
+ umount "$USB_MNT/tmp"
+ unmove_mounts
+
+ # If we're in developer mode, touch the .developer_mode file on stateful
+ # to avoid a bonus wait period on reboot.
+ # Failure here is non-terminal and it may not succeed just because the
+ # partition table of the destination has not been synchronized.
+ dlog "Prepping destination stateful to avoid a secondary delay."
+ touch_developer_mode_file && log "Prepped image for developer use."
+
+ return 0
+}
+
+clear_tpm() {
+ plog "Resetting security device . . ."
+ # TODO(wad) should we fail on this?
+ tpmc ppon || dlog "tpmc ppon error: $?"
+ tpmc clear || dlog "tpmc clear error: $?"
+ tpmc enable || dlog "tpmc enable error: $?"
+ tpmc activate || dlog "tpmc activate error: $?"
+ tpmc pplock || dlog "tpmc pplock error: $?"
+ log " done."
+ return 0
+}
+
+save_log_file() {
+ local log_dev="${1:-$LOG_DEV}"
+ [ -z "$log_dev" ] && return 0
+ # The recovery stateful is usually too small for ext3.
+ # TODO(wad) We could also just write the data raw if needed.
+ # Should this also try to save
+ dlog "Attempting to save log file: $LOG_FILE -> $log_dev"
+ mount -n -o sync,rw -t ext2 "$log_dev" /tmp
+ cp "$LOG_FILE" /tmp
+ umount -n /tmp
+}
+
+stop_log_file() {
+ # Drop logging
+ exec &> "$TTY_PATH"3
+ [ -n "$TAIL_PID" ] && kill $TAIL_PID
+}
+
+is_unofficial_root() {
+ [ $UNOFFICIAL_ROOT -eq 1 ]
+}
+
+set_unofficial_root() {
+ UNOFFICIAL_ROOT=1
+ return 0
+}
+
+recover_system() {
+ local source=$(strip_partition "$REAL_USB_DEV")
+ dlog "Beginning system recovery from $source"
+
+ recovery_wait
+
+ if is_unofficial_root; then
+ dlog "Attempting to use shim . . ."
+ # Mounting read only so a journal is not needed.
+ # If it fails, we can still proceed on a normal recovery path.
+ mount -n -o ro -t ext2 "$STATE_DEV" "$STATEFUL_MNT"
+ attempt_shim_script # never returns on success.
+ umount "$STATEFUL_MNT"
+ fi
+
+ # If we're not running a developer script then we're either
+ # installing a developer image or an official one. If we're
+ # in normal recovery mode, then we require that the KERN-B
+ # on the recovery image matches the hash on the command line.
+ # In developer mode, we will just check the keys.
+ verify_install_kernel || return 1
+
+ # Only clear on full installs. Shim scripts can call tpmc if they
+ # like. Only bGlobalLock will be in place in advance.
+ clear_tpm || return 1
+
+ call_image_recovery_script "$source" || return 1
+
+ return 0
+}
+
+use_new_root() {
+ move_mounts || on_error
+
+ # Chroot into newroot, erase the contents of the old /, and exec real init.
+ log "About to switch root"
+ stop_log_file
+ exec switch_root -c /dev/console "$NEWROOT_MNT" /sbin/init
+
+ # This should not really happen.
+ log "Failed to switch root."
+ save_log_file
+ return 1
+}
+
+is_developer_mode() {
+ # See Firmware High-Level Spec for details on CHSW values
+ CHSW=$(cat /sys/devices/platform/chromeos_acpi/CHSW)
+ # If the switch is unsupported, treat as developer mode.
+ [ -z "$CHSW" ] && return 0
+ if [ $CHSW -gt 0 -a $((CHSW & 32)) -eq 32 ]; then
+ return 0
+ fi
+ return 1
+}
+
+lock_tpm() {
+ if [ -z "$TPM_B_LOCKED" ]; then
+ # Depending on the system, the tpm may need to be started.
+ # Don't fail if it doesn't work though.
+ tpmc startup
+ tpmc ctest
+ if ! tpmc block; then
+ log "An unrecoverable error occurred with your security device"
+ log "Please power down and try again."
+ dlog "Failed to lock bGlobalLock."
+ on_error
+ return 1 # Never reached.
+ fi
+ TPM_B_LOCKED=y
+ fi
+ if [ -z "$TPM_PP_LOCKED" ]; then
+ # TODO: tpmc pplock if appropriate
+ TPM_PP_LOCKED=y
+ fi
+ return 0
+}
+
+# Extract and export kernel arguments
+export_args() {
+ # We trust out kernel command line explicitly.
+ local arg=
+ local key=
+ local val=
+ local acceptable_set='[A-Za-z0-9]_'
+ for arg in "$@"; do
+ key=$(echo "${arg%=*}" | tr 'a-z' 'A-Z' | \
+ tr -dc "$acceptable_set" '_')
+ val="${arg#*=}"
+ export "KERN_ARG_$key"="$val"
+ dlog "Exporting kernel argument $key as KERN_ARG_$key"
+ done
+}
+
+dlog() {
+ echo "$@" | tee -a "$TTY_PATH"2 "$TTY_PATH"3
+}
+
+# User visible
+log() {
+ echo "$@" | tee -a "$TTY_PATH"1 "$TTY_PATH"2
+}
+
+plog() {
+ # plog doesn't go to /dev/tty3 or log file.
+ printf "$@" | tee "$TTY_PATH"1 "$TTY_PATH"2
+}
+
main() {
+ exec &> "$LOG_FILE"
+
# Set up basic mounts, console.
initial_mounts
- # Send any output to where we can see it.
- exec &> /dev/tty1
- echo "Starting initramfs"
+ # Send all verbose output to tty3
+ (tail -f "$LOG_FILE" > "$TTY_PATH"3) &
+ TAIL_PID=$!
- # If we were booted with a dm-verity rootfs, then
- # we can just wait for it to come up.
- if check_if_dm_root; then
- wait_for_dm_control || on_error
- setup_dm_root || on_error
- # Has the BIOS told us the partition ID?
- elif check_for_gptid; then
- wait_for_gpt_root || on_error
+ log "Recovery image booting . . ."
+ log ""
+ log "Press Ctrl + Alt + F1 - F3 for more detailed information."
+ log ""
+
+ # Export the kernel command line as a parsed blob prepending KERN_ARG_ to each
+ # argument.
+ export_args $(cat /proc/cmdline | sed -e 's/"[^"]*"/DROPPED/g')
+
+ if is_developer_mode; then
+ log "! Your computer's developer mode switch is in the ENABLED position."
+ log "!"
+ log "! If this is unintentional, you should power off and toggle it back "
+ log "! after recovery is completed."
+ log ""
+ fi
+
+ if find_official_root || find_developer_root || find_shim_root; then
+ log " found."
else
- # Just wait for any rootfs.
- # TODO(nsanders): add kern_guid into legacy cmdline?
- wait_for_root || on_error
+ log " not found."
+ on_error
fi
- mount_usb || on_error
+ # Extract the real boot source which may be masked by dm-verity.
get_stateful_dev || on_error
# Check if we want to run from RAM, in the factory.
if check_if_factory_install; then
+ is_developer_mode || on_error # factory install requires it.
# Copy rootfs contents to tmpfs, then unmount USB device.
NEWROOT_MNT=/newroot
mount_tmpfs || on_error
@@ -227,24 +751,56 @@ main() {
copy_lsb || on_error
# USB device is unmounted, we can remove it now.
unmount_usb || on_error
- else
- NEWROOT_MNT="$USB_MNT"
+ # Switch to the new root
+ use_new_root || on_error
+ on_error # !! Never reached. !!
fi
- move_mounts || on_error
- # Chroot into newroot, erase the contents of the old /, and exec real init.
- echo "About to switch root"
- exec switch_root -c /dev/console "$NEWROOT_MNT" /sbin/init
+ # If not, we must be a recovery kernel.
+ NEWROOT_MNT="$USB_MNT"
- # This should not really happen.
- echo "Failed to switch root"
+ # Always lock the TPM. If a NVRAM reset is ever needed, we can change it.
+ lock_tpm || on_error
- # Fail here.
- on_error
-}
+ # Perform a full device mapper root validation to avoid any unexpected
+ # failures during postinst. It also allows us to detect if the root is
+ # intentionally mismatched - such as during Chromium OS recovery with a
+ # Chrome OS recovery kernel.
+ if ! validate_recovery_root; then
+ is_developer_mode || on_error
+ find_developer_root || find_shim_root || on_error
+ log " found."
+ # This logic is duplicated to avoid double validating factory media. It
+ # will only be hit if a verified root can be mounted but is actually not
+ # intact.
+ get_stateful_dev || on_error
+ fi
+
+ get_dst || on_error
+ recover_system || on_error
-main
+ log "System recovery is complete!"
+ log "Please remove the recovery device and reboot."
+
+ stop_log_file
+ # Save the recovery log to the target on success and the USB.
+ save_log_file "$DST"1
+ save_log_file
+
+ unmount_usb
+
+ log ""
+ log ""
+ plog "The system will automatically reboot in 2 minutes"
+ make_user_wait 120
+ exit 0
+}
+
+# Make this source-able for testing.
+if [ "$0" = "/init" ]; then
+ main "$@"
+ # Should never reach here.
+ exit 1
+fi
-# Should never reach here.
-exit 1
« no previous file with comments | « chromeos-base/chromeos-initramfs/chromeos-initramfs-9999.ebuild ('k') | chromeos-base/kernel/kernel-9999.ebuild » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698