blob: 0376a8e14698ca9a0f48ab9140ab97b4489ada27 [file] [log] [blame]
Nick Sandersf11aca62011-01-28 17:39:55 -08001#!/bin/sh -x
Colin Choweb584db2010-07-08 16:02:56 -07002
3# Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6#
7# /init script for use in factory install shim. Requires busybox in
8# /bin/busybox, and a symlink from /bin/sh -> busybox.
9
Nick Sandersf11aca62011-01-28 17:39:55 -080010# USB card partition and mount point.
11USB_DEVS="sdb3 sdc3 mmcblk1p3"
12USB_SHIM_DEVS="sdb1 sdc1 mmcblk1p1"
13USB_MNT=/usb
14REAL_USB_DEV=
15DM_NAME=
16
17DST=
18
19STATEFUL_MNT=/stateful
20STATE_DEV=
21
22LOG_DEV=
23LOG_FILE="/log/recovery.log"
24
25TPM_B_LOCKED=
26TPM_PP_LOCKED=
27
28# Developer script to run
29SHIM_SCRIPT="$STATEFUL_MNT/userdir/runme"
30SHIM_VBLOCK="$STATEFUL_MNT/userdir/runme.vblock"
31
32KERN_B_VBLOCK="$STATEFUL_MNT/vmlinuz_hd.vblock"
33REAL_KERN_B_HASH=
34
35MOVE_MOUNTS="/sys /proc /dev"
36
37# To be updated to keep logging after move_mounts.
38TTY_PATH="/dev/tty"
39TAIL_PID=
40
41# Used to ensure the factory check only occurs with
42# a properly matched root and kernel.
43UNOFFICIAL_ROOT=0
44
Colin Choweb584db2010-07-08 16:02:56 -070045# Size of the root ramdisk.
46TMPFS_SIZE=300M
47
Nick Sandersf11aca62011-01-28 17:39:55 -080048on_error() {
49 # Exit, thus causing a kernel panic. We don't want to do anything else (like
50 # start a shell) because it would be trivially easy to get here (just unplug
51 # the USB drive after the kernel starts but before the USB drives are probed
52 # by the kernel) and starting a shell here would be a BIG security hole.
53 log
54 log
55 log "An unrecoverable error occurred during recovery!"
56 log
57 log "Please try again or try a newer recovery image."
58 save_log_file
59 sleep 1d
60 exit 1
61}
62
Colin Choweb584db2010-07-08 16:02:56 -070063initial_mounts() {
Nick Sandersf11aca62011-01-28 17:39:55 -080064 mkdir -p /var/lock
65 mount -t proc -o nodev,noexec,nosuid none /proc
66 mount -t sysfs -o nodev,noexec,nosuid none /sys
67 if ! mount -t devtmpfs -o mode=0755 none /dev; then
68 mount -t tmpfs -o mode=0755 none /dev
69 mknod -m 0600 /dev/console c 5 1
70 mknod -m 0601 /dev/tty1 c 4 1
71 mknod -m 0601 /dev/tty2 c 4 2
72 mknod -m 0601 /dev/tty3 c 4 3
73 mknod -m 0600 /dev/tpm0 c 10 224
74 mknod /dev/null c 1 3
75 fi
76 mkdir -p /dev/pts
77 mount -t devpts -o noexec,nosuid none /dev/pts || true
Colin Choweb584db2010-07-08 16:02:56 -070078}
79
Nick Sandersf11aca62011-01-28 17:39:55 -080080# Look for a device with our GPT ID.
81wait_for_gpt_root() {
82 [ -z "$KERN_ARG_KERN_GUID" ] && return 1
83 dlog -n "Looking for rootfs using kern_guid..."
84 for try in $(seq 20); do
85 plog " ."
86 kern=$(cgpt find -1 -u $KERN_ARG_KERN_GUID)
87 # We always try ROOT-A in recovery.
88 newroot="${kern%[0-9]*}3"
89 if [ -b "$newroot" ]; then
90 USB_DEV="$newroot"
91 dlog "Found $USB_DEV"
92 return 0
93 fi
94 sleep 1
95 done
96 dlog "Failed waiting for kern_guid"
97 return 1
Colin Choweb584db2010-07-08 16:02:56 -070098}
99
Nick Sandersf11aca62011-01-28 17:39:55 -0800100# Look for any USB device.
Colin Choweb584db2010-07-08 16:02:56 -0700101wait_for_root() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800102 dlog -n "Waiting for $USB_DEVS to appear"
Colin Choweb584db2010-07-08 16:02:56 -0700103 for try in $(seq 20); do
Nick Sandersf11aca62011-01-28 17:39:55 -0800104 plog " ."
105 for dev in $USB_DEVS; do
106 if [ -b "/dev/${dev}" ]; then
107 USB_DEV="/dev/${dev}"
108 dlog "Found $USB_DEV"
109 return 0
110 fi
111 done
Colin Choweb584db2010-07-08 16:02:56 -0700112 sleep 1
113 done
Nick Sandersf11aca62011-01-28 17:39:55 -0800114 dlog "Failed waiting for root!"
Colin Choweb584db2010-07-08 16:02:56 -0700115 return 1
116}
117
Nick Sandersf11aca62011-01-28 17:39:55 -0800118# Wait for dm-0 to come up.
119wait_for_dm_control() {
120 MAPPER_CONTROL=/dev/mapper/control
121 dlog -n "Waiting for $MAPPER_CONTROL to appear"
Colin Choweb584db2010-07-08 16:02:56 -0700122 for try in $(seq 20); do
Nick Sandersf11aca62011-01-28 17:39:55 -0800123 plog " ."
124 if [ -c "$MAPPER_CONTROL" ]; then
Colin Choweb584db2010-07-08 16:02:56 -0700125 return 0
126 fi
127 sleep 1
128 done
Nick Sandersf11aca62011-01-28 17:39:55 -0800129 dlog "Failed waiting for $MAPPER_CONTROL!"
Colin Choweb584db2010-07-08 16:02:56 -0700130 return 1
131}
132
Nick Sandersf11aca62011-01-28 17:39:55 -0800133check_if_dm_root() {
134 [ "$KERN_ARG_ROOT" = "/dev/dm-0" ]
135}
136
137# Attempt to find the root defined in the signed recovery
138# kernel we're booted into to. Exports REAL_USB_DEV if there
139# is a root partition that may be used - on succes or failure.
140find_official_root() {
141 plog "Checking for an official recovery image . . ."
142
143 # Check for a kernel selected root device or one in a well known location.
144 wait_for_gpt_root || wait_for_root || return 1
145
146 # Now see if it has a Chrome OS rootfs partition.
147 cgpt find -t rootfs "$(strip_partition "$USB_DEV")" || return 1
148 REAL_USB_DEV="$USB_DEV"
149
150 LOG_DEV="$(strip_partition "$USB_DEV")"1 # Default to stateful.
151
152 # Now see if the root should be integrity checked.
153 if check_if_dm_root; then
154 setup_dm_root || return 1
155 fi
156
157 mount_usb || return 1
158 return 0
159}
160
161find_developer_root() {
162 is_developer_mode || return 1
163 # Lock the TPM prior to using an untrusted root.
164 lock_tpm || return 1
165 plog "\nSearching for developer root . . ."
166 # If an official root could not be mounted, free up the underlying device
167 # if it is claimed by verity.
168 dmsetup remove "$DM_NAME"
169
170 # If we found a valid rootfs earlier, then we're done.
171 USB_DEV="$REAL_USB_DEV"
172 [ -z "$USB_DEV" ] && return 1
173 set_unofficial_root || return 1
174 mount_usb || return 1
175 return 0
176}
177
178# If this kernel image has been placed on a drive with only a
179# stateful partition, root detection will rightly fail. However,
180# we can still run a developer supplied script so we will pretend
181# stateful is the root (USB_DEV).
182find_shim_root() {
183 # Lock the TPM prior to using an untrusted root.
184 lock_tpm || return 1
185 plog "\nSearching for an alternate recovery image . . ."
186 dlog -n "Waiting for $USB_SHIM_DEVS to appear"
187 for try in $(seq 20); do
188 plog " ."
189 for dev in $USB_SHIM_DEVS; do
190 if [ -b "/dev/${dev}" ]; then
191 USB_DEV="/dev/${dev}"
192 REAL_USB_DEV="$USB_DEV"
193 dlog "Found $USB_DEV"
194 set_unofficial_root || on_error
195 mount_usb || return 1
196 return 0
197 fi
198 done
199 sleep 1
200 done
201 return 1
202}
203
204# If we have a verified recovery root, ensure all blocks are valid before
205# handing it off to the installer.
206validate_recovery_root() {
207 # Allow test recovery roots that are unverified.
208 [ "$USB_DEV" = "/dev/dm-0" ] || return 0
209 is_unofficial_root && return 0
210
211 plog "Validating official recovery image . . . "
212 # Ensure the verified rootfs is fully intact or fail with no USB_DEV.
213 # REAL_USB_DEV is left intact.
214 # Correctness wins over speed for this.
215 if ! dd if="$USB_DEV" of=/dev/null bs=$((16 * 1024 * 1024)); then
216 dlog "Included root filesystem could not be verified."
217 log " failed!"
218 dmsetup remove "$DM_NAME" # Free up the real root for use.
219 USB_DEV=
220 return 1
221 fi
222 log " completed."
223 return 0
224}
225
226setup_dm_root() {
227 dlog -n "Extracting the device mapper configuration..."
228 # export_args can't handle dm="..." at present.
229 DMARG=$(cat /proc/cmdline | sed -e 's/.*dm="\([^"\]*\)".*/\1/g')
230 DM_NAME=$(echo "$DMARG" | cut -f1 -d' ')
231 # We override the reboot-to-recovery error behavior so that we can fail
232 # gracefully on invalid rootfs.
233 DM_TABLE="$(echo "$DMARG" | cut -f2 -d,) eio"
234
235 # Don't attempt to call dmsetup if the root device isn't one that was
236 # discovered as the creation process will hang.
Will Drewrye7bc1dc2011-02-28 16:28:34 -0600237 # TODO(wad) extract the UUID and use it with cgpt find instead.
238 if [ -n "$KERN_ARG_KERN_GUID" ]; then
239 [ "${DM_TABLE%$KERN_ARG_KERN_GUID*}" = "${DM_TABLE}" ] && return 1
240 elif [ -n "${USB_DEV}" ]; then
Nick Sandersf11aca62011-01-28 17:39:55 -0800241 [ "${DM_TABLE%$USB_DEV*}" = "${DM_TABLE}" ] && return 1
242 fi
243
244 if ! dmsetup create -r "$DM_NAME" --major 254 --minor 0 --table "$DM_TABLE"
245 then
246 dlog "Failed to configure device mapper root"
247 return 1
248 fi
249 USB_DEV="/dev/dm-0"
250 if [ ! -b "$USB_DEV" ]; then
251 mknod -m 0600 "$USB_DEV" b 254 0
252 fi
253 dlog "Created device mapper root $DM_NAME."
254 return 0
255}
256
257mount_usb() {
258 dlog -n "Mounting usb"
259 for try in $(seq 20); do
260 plog " ."
261 if mount -n -o ro "$USB_DEV" "$USB_MNT"; then
262 dlog "ok"
263 return 0
264 fi
265 sleep 1
266 done
267 dlog "Failed to mount usb!"
268 return 1
269}
270
271check_if_factory_install() {
272 if is_unofficial_root; then
273 dlog "Skipping factory install check."
274 return 1
275 fi
276
277 if [ -e "${USB_MNT}/root/.factory_installer" ]; then
278 log "Detected factory install."
279 return 0
280 fi
281 return 1
282}
283
284get_stateful_dev() {
285 STATE_DEV=${REAL_USB_DEV%[0-9]*}1
286 if [ ! -b "$STATE_DEV" ]; then
287 dlog "Failed to determine stateful device"
288 return 1
289 fi
290 return 0
291}
292
Colin Choweb584db2010-07-08 16:02:56 -0700293mount_tmpfs() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800294 dlog "Mounting tmpfs..."
295 mount -n -t tmpfs tmpfs "$NEWROOT_MNT" -o "size=$TMPFS_SIZE"
Colin Choweb584db2010-07-08 16:02:56 -0700296 return $?
297}
298
299copy_contents() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800300 log "Copying usb->tmpfs..."
301 (cd "${USB_MNT}" ; tar cf - . | (cd "${NEWROOT_MNT}" && tar xf -))
302 RES=$?
303 log "Copy returned with result $RES"
304 return $RES
305}
306
307copy_lsb() {
308 STATEFUL_LSB="dev_image/etc/lsb-factory"
309 mkdir -p "${NEWROOT_MNT}/mnt/stateful_partition/dev_image/etc"
310 # Mounting ext3 as ext2 since the journal is unneeded in ro.
311 mount -n -o ro -t ext2 "$STATE_DEV" "$STATEFUL_MNT"
312 if [ -f "${STATEFUL_MNT}/${STATEFUL_LSB}" ]; then
313 log "Found ${STATEFUL_MNT}/${STATEFUL_LSB}"
314 cp -a "${STATEFUL_MNT}/${STATEFUL_LSB}" \
315 "${NEWROOT_MNT}/mnt/stateful_partition/${STATEFUL_LSB}"
316 fi
317 umount "$STATEFUL_MNT"
318 rmdir "$STATEFUL_MNT"
Colin Choweb584db2010-07-08 16:02:56 -0700319}
320
321move_mounts() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800322 dlog "Moving sys. proc, dev to $NEWROOT_MNT"
323 for mnt in $MOVE_MOUNTS; do
324 mkdir -p "$NEWROOT_MNT$mnt"
325 mount -n -o move "$mnt" "$NEWROOT_MNT$mnt"
Colin Choweb584db2010-07-08 16:02:56 -0700326 done
Nick Sandersf11aca62011-01-28 17:39:55 -0800327 TTY_PATH="$NEWROOT_MNT/dev/tty"
328 dlog "Done."
Colin Choweb584db2010-07-08 16:02:56 -0700329 return 0
330}
331
Nick Sandersf11aca62011-01-28 17:39:55 -0800332unmove_mounts() {
333 dlog "Moving sys. proc, dev to $NEWROOT_MNT"
334 for mnt in $MOVE_MOUNTS; do
335 mount -n -o move "$NEWROOT_MNT$mnt" "$mnt"
336 done
337 TTY_PATH="/dev/tty"
338 dlog "Done."
339 return 0
Colin Choweb584db2010-07-08 16:02:56 -0700340}
341
Nick Sandersf11aca62011-01-28 17:39:55 -0800342unmount_usb() {
343 dlog "Unmounting $USB_MNT"
344 umount "$USB_MNT"
345 # Make sure we clean up a device-mapper root.
346 if [ "$USB_DEV" = "/dev/dm-0" ]; then
347 dlog "Removing dm-verity target"
348 dmsetup remove "$DM_NAME"
349 fi
350 dlog
351 dlog "$REAL_USB_DEV can now be safely removed"
352 dlog
353 return 0
354}
355
356strip_partition() {
357 local dev="${1%[0-9]*}"
358 # handle mmcblk0p case as well
359 echo "${dev%p*}"
360}
361
362# Accomodate sd* or mmcblk* devices
363get_dst() {
364 DST=$(echo ${REAL_USB_DEV%[0-9]*} | \
365 tr -s '[0-9]' '0' | \
366 sed -e 's/sd[b-z]/sda/g')
367}
368
369dev_wait_or_error() {
370 is_developer_mode || on_error # terminal if we get here in regular mode.
371 log ""
372 log "A developer key change is required to proceed."
373 plog "Please wait 300 seconds . . ."
374 # TODO(wad) Divvy the total up into a few different prompts.
375 make_user_wait 300
376 log ""
377}
378
379recovery_wait() {
380 log ""
381 log "Preparing to recover your system image."
382 plog "If you do not wish to proceed, please reboot in the next 10 seconds ."
383 make_user_wait 10
384 log ""
385 log "System recovery is beginning."
386 log "Please do not disconnect from a power source or power down."
387 log ""
388}
389
390make_user_wait() {
391 local delay_in_sec="${1-300}"
392 while [ $delay_in_sec -gt 0 ]; do
393 plog " ."
394 sleep 1
395 delay_in_sec=$((delay_in_sec - 1))
396 done
397 log ""
398}
399
400check_key_or_wait() {
401 plog "Searching the system disk for a matching kernel key . . ."
402 if ! cgpt find -t kernel -M "$1" "$DST"; then
403 log " failed."
404 dev_wait_or_error
405 return 0
406 fi
407 log " found."
408
409 plog "Validating matching signature(s) . . ."
410 # If we found a keyblock, at the right offset, make sure it actually signed
411 # the subsequent payload.
412 local kdev=
413 for kdev in $(cgpt find -t kernel -M "$1" "$DST"); do
414 plog " ."
415 verify_kernel_signature "$kdev" "/tmp/kern.keyblock" || continue
416 log " done."
417 return 0
418 done
419
420 log " failed."
421 dev_wait_or_error
422 return 0
423}
424
425# Never returns on success.
426attempt_shim_script() {
427 # TODO(wad) Add static root of trust validation then remove the next line.
428 # http://crosbug/8390
429 is_developer_mode || return 1
430
431 # Now we will either install a colocated Chromium OS image by
432 # checking the keys on KERN-B against any on disk (KERN-[ABC])
433 # or by checking a signed script on stateful.
434 dlog "Checking for a shim script . . ."
435 [ -x "$SHIM_SCRIPT" ] || return 1
436 [ -f "$SHIM_VBLOCK" ] || return 1
437 log "Shim script and signing file found!"
438
439 plog "Verifying the signature on the script . . ."
440 # Extract pubkey and check signature
441 if ! dev_sign_file --verify "$SHIM_SCRIPT" \
442 --vblock "$SHIM_VBLOCK" \
443 --keyblock /tmp/shim.keyblock; then
444 log " failed."
445 fi
446 log " done."
447
448 # If we're not in developer mode, this will be terminal on failure.
449 check_key_or_wait /tmp/shim.keyblock
450
451 # Run the user supplied script. It is done in the current environment
452 # to avoid needing anything other than the script/program on the partition.
453 log "Executing shim script . . ."
454
455 dlog "calling $SHIM_SCRIPT with exec"
456 # Fix up the input/output
457 stop_log_file
458 set +x
459 exec &> "$TTY_PATH"1
460 exec < "$TTY_PATH"1
461 # Call the script!
462 exec "$SHIM_SCRIPT"
463
464 # Never reached.
465 save_log_file
466 return 0
467}
468
469get_kern_b_device() {
470 # TODO(wad) By changing boot priority, could we end up
471 # checking the recovery image or the recovery image could not
472 # be in slot A. In that case, it should fail in normal mode.
473 KERN_B_DEV=${REAL_USB_DEV%[0-9]*}4
474 if [ ! -b "${KERN_B_DEV}" ]; then
475 return 1
476 fi
477 return 0
478}
479
480get_real_kern_b_hash() {
481 REAL_KERN_B_HASH=$(dd if="${KERN_B_DEV}" | \
482 sha1sum | \
483 cut -f1 -d' ')
484 [ -n "$REAL_KERN_B_HASH" ]
485}
486
487verify_kernel_signature() {
488 local kern_dev="$1"
489 local keyblock="$2"
490
491 if ! dd if="$kern_dev" of="/tmp/kern.bin"; then
492 return 1
493 fi
494
495 # Validates the signature and outputs a keyblock.
496 if ! vbutil_kernel --verify "/tmp/kern.bin" \
497 --keyblock "$keyblock"; then
498 return 1
499 fi
500 return 0
501}
502
503verify_install_kernel() {
504 get_kern_b_device || return 1
505 get_real_kern_b_hash || return 1
506
507 # TODO(wad) check signatures from stateful on kern b using the
508 # root of trust instead of using a baked in cmdline.
509 if [ "$REAL_KERN_B_HASH" != "$KERN_ARG_KERN_B_HASH" ]; then
510 if ! is_developer_mode; then
511 log "The recovery image cannot be verified."
512 return 1
513 fi
514
515 # Extract the kernel so that vbutil_kernel will happily consume it.
516 log "Checking the install kernel for a valid developer signature . . ."
517 verify_kernel_signature "$KERN_B_DEV" "/tmp/kern_b.keyblock" || return 1
518 check_key_or_wait /tmp/kern_b.keyblock
519 fi
520 return 0
521}
522
523touch_developer_mode_file() {
524 is_developer_mode || return 1
525 mount -n -o rw -t ext3 "$DST"1 "$STATEFUL_MNT" || return 1
526 touch "$STATEFUL_MNT/.developer_mode" || return 1
527 umount "$STATEFUL_MNT" || return 1
528 return 0
529}
530
531call_image_recovery_script() {
532 mount -t tmpfs -o mode=1777 none "$USB_MNT/tmp" || return 1
533 move_mounts || return 1
534
535 # Start the copy.
536 log ""
537 log "Beginning system image copy. This will take some time . . ."
538 log ""
539 # Until images are built with the installer keyblock in KERN-B by
540 # default, we keep copying over the installer vblock from the
541 # stateful partition.
542 # TODO(wad) http://crosbug/8378
543 export IS_RECOVERY_INSTALL=1
544 chroot "$USB_MNT" \
545 /usr/sbin/chromeos-install --run_as_root --yes \
546 --payload_image="$1" \
547 --use_payload_kern_b
548 if [ $? -eq 0 ]; then
549 log "System copy completed."
550 else
551 log "Error performing system recovery!"
552 fi
553
554 # Clean up doesn't need to be successful.
555 umount "$USB_MNT/tmp"
556 unmove_mounts
557
558 # If we're in developer mode, touch the .developer_mode file on stateful
559 # to avoid a bonus wait period on reboot.
560 # Failure here is non-terminal and it may not succeed just because the
561 # partition table of the destination has not been synchronized.
562 dlog "Prepping destination stateful to avoid a secondary delay."
563 touch_developer_mode_file && log "Prepped image for developer use."
564
565 return 0
566}
567
568clear_tpm() {
569 plog "Resetting security device . . ."
570 # TODO(wad) should we fail on this?
571 tpmc ppon || dlog "tpmc ppon error: $?"
572 tpmc clear || dlog "tpmc clear error: $?"
573 tpmc enable || dlog "tpmc enable error: $?"
574 tpmc activate || dlog "tpmc activate error: $?"
575 tpmc pplock || dlog "tpmc pplock error: $?"
576 log " done."
577 return 0
578}
579
580save_log_file() {
581 local log_dev="${1:-$LOG_DEV}"
582 [ -z "$log_dev" ] && return 0
583 # The recovery stateful is usually too small for ext3.
584 # TODO(wad) We could also just write the data raw if needed.
585 # Should this also try to save
586 dlog "Attempting to save log file: $LOG_FILE -> $log_dev"
587 mount -n -o sync,rw -t ext2 "$log_dev" /tmp
588 cp "$LOG_FILE" /tmp
589 umount -n /tmp
590}
591
592stop_log_file() {
593 # Drop logging
594 exec &> "$TTY_PATH"3
595 [ -n "$TAIL_PID" ] && kill $TAIL_PID
596}
597
598is_unofficial_root() {
599 [ $UNOFFICIAL_ROOT -eq 1 ]
600}
601
602set_unofficial_root() {
603 UNOFFICIAL_ROOT=1
604 return 0
605}
606
607recover_system() {
608 local source=$(strip_partition "$REAL_USB_DEV")
609 dlog "Beginning system recovery from $source"
610
611 recovery_wait
612
613 if is_unofficial_root; then
614 dlog "Attempting to use shim . . ."
615 # Mounting read only so a journal is not needed.
616 # If it fails, we can still proceed on a normal recovery path.
617 mount -n -o ro -t ext2 "$STATE_DEV" "$STATEFUL_MNT"
618 attempt_shim_script # never returns on success.
619 umount "$STATEFUL_MNT"
620 fi
621
622 # If we're not running a developer script then we're either
623 # installing a developer image or an official one. If we're
624 # in normal recovery mode, then we require that the KERN-B
625 # on the recovery image matches the hash on the command line.
626 # In developer mode, we will just check the keys.
627 verify_install_kernel || return 1
628
629 # Only clear on full installs. Shim scripts can call tpmc if they
630 # like. Only bGlobalLock will be in place in advance.
631 clear_tpm || return 1
632
633 call_image_recovery_script "$source" || return 1
634
635 return 0
636}
637
638use_new_root() {
639 move_mounts || on_error
640
641 # Chroot into newroot, erase the contents of the old /, and exec real init.
642 log "About to switch root"
643 stop_log_file
644 exec switch_root -c /dev/console "$NEWROOT_MNT" /sbin/init
645
646 # This should not really happen.
647 log "Failed to switch root."
648 save_log_file
649 return 1
650}
651
652is_developer_mode() {
653 # See Firmware High-Level Spec for details on CHSW values
654 CHSW=$(cat /sys/devices/platform/chromeos_acpi/CHSW)
655 # If the switch is unsupported, treat as developer mode.
656 [ -z "$CHSW" ] && return 0
657 if [ $CHSW -gt 0 -a $((CHSW & 32)) -eq 32 ]; then
658 return 0
659 fi
660 return 1
661}
662
663lock_tpm() {
664 if [ -z "$TPM_B_LOCKED" ]; then
665 # Depending on the system, the tpm may need to be started.
666 # Don't fail if it doesn't work though.
667 tpmc startup
668 tpmc ctest
669 if ! tpmc block; then
670 log "An unrecoverable error occurred with your security device"
671 log "Please power down and try again."
672 dlog "Failed to lock bGlobalLock."
673 on_error
674 return 1 # Never reached.
675 fi
676 TPM_B_LOCKED=y
677 fi
678 if [ -z "$TPM_PP_LOCKED" ]; then
679 # TODO: tpmc pplock if appropriate
680 TPM_PP_LOCKED=y
681 fi
682 return 0
683}
684
685# Extract and export kernel arguments
686export_args() {
687 # We trust out kernel command line explicitly.
688 local arg=
689 local key=
690 local val=
691 local acceptable_set='[A-Za-z0-9]_'
692 for arg in "$@"; do
693 key=$(echo "${arg%=*}" | tr 'a-z' 'A-Z' | \
694 tr -dc "$acceptable_set" '_')
695 val="${arg#*=}"
696 export "KERN_ARG_$key"="$val"
697 dlog "Exporting kernel argument $key as KERN_ARG_$key"
698 done
699}
700
701dlog() {
702 echo "$@" | tee -a "$TTY_PATH"2 "$TTY_PATH"3
703}
704
705# User visible
706log() {
707 echo "$@" | tee -a "$TTY_PATH"1 "$TTY_PATH"2
708}
709
710plog() {
711 # plog doesn't go to /dev/tty3 or log file.
712 printf "$@" | tee "$TTY_PATH"1 "$TTY_PATH"2
713}
714
715main() {
716 exec &> "$LOG_FILE"
717
718 # Set up basic mounts, console.
719 initial_mounts
720
721 # Send all verbose output to tty3
722 (tail -f "$LOG_FILE" > "$TTY_PATH"3) &
723 TAIL_PID=$!
724
725 log "Recovery image booting . . ."
726 log ""
727 log "Press Ctrl + Alt + F1 - F3 for more detailed information."
728 log ""
729
730 # Export the kernel command line as a parsed blob prepending KERN_ARG_ to each
731 # argument.
732 export_args $(cat /proc/cmdline | sed -e 's/"[^"]*"/DROPPED/g')
733
734 if is_developer_mode; then
735 log "! Your computer's developer mode switch is in the ENABLED position."
736 log "!"
737 log "! If this is unintentional, you should power off and toggle it back "
738 log "! after recovery is completed."
739 log ""
740 fi
741
742 if find_official_root || find_developer_root || find_shim_root; then
743 log " found."
744 else
745 log " not found."
746 on_error
747 fi
748
749 # Extract the real boot source which may be masked by dm-verity.
750 get_stateful_dev || on_error
751
752 # Check if we want to run from RAM, in the factory.
753 if check_if_factory_install; then
754 is_developer_mode || on_error # factory install requires it.
755 # Copy rootfs contents to tmpfs, then unmount USB device.
756 NEWROOT_MNT=/newroot
757 mount_tmpfs || on_error
758 copy_contents || on_error
759 copy_lsb || on_error
760 # USB device is unmounted, we can remove it now.
761 unmount_usb || on_error
762 # Switch to the new root
763 use_new_root || on_error
764 on_error # !! Never reached. !!
765 fi
766
767 # If not, we must be a recovery kernel.
768 NEWROOT_MNT="$USB_MNT"
769
770 # Always lock the TPM. If a NVRAM reset is ever needed, we can change it.
771 lock_tpm || on_error
772
773 # Perform a full device mapper root validation to avoid any unexpected
774 # failures during postinst. It also allows us to detect if the root is
775 # intentionally mismatched - such as during Chromium OS recovery with a
776 # Chrome OS recovery kernel.
777 if ! validate_recovery_root; then
778 is_developer_mode || on_error
779 find_developer_root || find_shim_root || on_error
780 log " found."
781 # This logic is duplicated to avoid double validating factory media. It
782 # will only be hit if a verified root can be mounted but is actually not
783 # intact.
784 get_stateful_dev || on_error
785 fi
786
787 get_dst || on_error
788
789 recover_system || on_error
790
791 log "System recovery is complete!"
792 log "Please remove the recovery device and reboot."
793
794 stop_log_file
795 # Save the recovery log to the target on success and the USB.
796 save_log_file "$DST"1
797 save_log_file
798
799 unmount_usb
800
801 log ""
802 log ""
803 plog "The system will automatically reboot in 2 minutes"
804 make_user_wait 120
805 reboot -f
806 exit 0
807}
808
809# Make this source-able for testing.
810if [ "$0" = "/init" ]; then
811 main "$@"
812 # Should never reach here.
813 exit 1
814fi
815