blob: e8fca7d893c8d74af3acc50d2cd204f44703fbfd [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.
237 # TODO(wad) once we pass the UUID this will be easier to robustly check.
238 if [ -n "${USB_DEV}" ]; then
239 [ "${DM_TABLE%$USB_DEV*}" = "${DM_TABLE}" ] && return 1
240 fi
241
242 if ! dmsetup create -r "$DM_NAME" --major 254 --minor 0 --table "$DM_TABLE"
243 then
244 dlog "Failed to configure device mapper root"
245 return 1
246 fi
247 USB_DEV="/dev/dm-0"
248 if [ ! -b "$USB_DEV" ]; then
249 mknod -m 0600 "$USB_DEV" b 254 0
250 fi
251 dlog "Created device mapper root $DM_NAME."
252 return 0
253}
254
255mount_usb() {
256 dlog -n "Mounting usb"
257 for try in $(seq 20); do
258 plog " ."
259 if mount -n -o ro "$USB_DEV" "$USB_MNT"; then
260 dlog "ok"
261 return 0
262 fi
263 sleep 1
264 done
265 dlog "Failed to mount usb!"
266 return 1
267}
268
269check_if_factory_install() {
270 if is_unofficial_root; then
271 dlog "Skipping factory install check."
272 return 1
273 fi
274
275 if [ -e "${USB_MNT}/root/.factory_installer" ]; then
276 log "Detected factory install."
277 return 0
278 fi
279 return 1
280}
281
282get_stateful_dev() {
283 STATE_DEV=${REAL_USB_DEV%[0-9]*}1
284 if [ ! -b "$STATE_DEV" ]; then
285 dlog "Failed to determine stateful device"
286 return 1
287 fi
288 return 0
289}
290
Colin Choweb584db2010-07-08 16:02:56 -0700291mount_tmpfs() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800292 dlog "Mounting tmpfs..."
293 mount -n -t tmpfs tmpfs "$NEWROOT_MNT" -o "size=$TMPFS_SIZE"
Colin Choweb584db2010-07-08 16:02:56 -0700294 return $?
295}
296
297copy_contents() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800298 log "Copying usb->tmpfs..."
299 (cd "${USB_MNT}" ; tar cf - . | (cd "${NEWROOT_MNT}" && tar xf -))
300 RES=$?
301 log "Copy returned with result $RES"
302 return $RES
303}
304
305copy_lsb() {
306 STATEFUL_LSB="dev_image/etc/lsb-factory"
307 mkdir -p "${NEWROOT_MNT}/mnt/stateful_partition/dev_image/etc"
308 # Mounting ext3 as ext2 since the journal is unneeded in ro.
309 mount -n -o ro -t ext2 "$STATE_DEV" "$STATEFUL_MNT"
310 if [ -f "${STATEFUL_MNT}/${STATEFUL_LSB}" ]; then
311 log "Found ${STATEFUL_MNT}/${STATEFUL_LSB}"
312 cp -a "${STATEFUL_MNT}/${STATEFUL_LSB}" \
313 "${NEWROOT_MNT}/mnt/stateful_partition/${STATEFUL_LSB}"
314 fi
315 umount "$STATEFUL_MNT"
316 rmdir "$STATEFUL_MNT"
Colin Choweb584db2010-07-08 16:02:56 -0700317}
318
319move_mounts() {
Nick Sandersf11aca62011-01-28 17:39:55 -0800320 dlog "Moving sys. proc, dev to $NEWROOT_MNT"
321 for mnt in $MOVE_MOUNTS; do
322 mkdir -p "$NEWROOT_MNT$mnt"
323 mount -n -o move "$mnt" "$NEWROOT_MNT$mnt"
Colin Choweb584db2010-07-08 16:02:56 -0700324 done
Nick Sandersf11aca62011-01-28 17:39:55 -0800325 TTY_PATH="$NEWROOT_MNT/dev/tty"
326 dlog "Done."
Colin Choweb584db2010-07-08 16:02:56 -0700327 return 0
328}
329
Nick Sandersf11aca62011-01-28 17:39:55 -0800330unmove_mounts() {
331 dlog "Moving sys. proc, dev to $NEWROOT_MNT"
332 for mnt in $MOVE_MOUNTS; do
333 mount -n -o move "$NEWROOT_MNT$mnt" "$mnt"
334 done
335 TTY_PATH="/dev/tty"
336 dlog "Done."
337 return 0
Colin Choweb584db2010-07-08 16:02:56 -0700338}
339
Nick Sandersf11aca62011-01-28 17:39:55 -0800340unmount_usb() {
341 dlog "Unmounting $USB_MNT"
342 umount "$USB_MNT"
343 # Make sure we clean up a device-mapper root.
344 if [ "$USB_DEV" = "/dev/dm-0" ]; then
345 dlog "Removing dm-verity target"
346 dmsetup remove "$DM_NAME"
347 fi
348 dlog
349 dlog "$REAL_USB_DEV can now be safely removed"
350 dlog
351 return 0
352}
353
354strip_partition() {
355 local dev="${1%[0-9]*}"
356 # handle mmcblk0p case as well
357 echo "${dev%p*}"
358}
359
360# Accomodate sd* or mmcblk* devices
361get_dst() {
362 DST=$(echo ${REAL_USB_DEV%[0-9]*} | \
363 tr -s '[0-9]' '0' | \
364 sed -e 's/sd[b-z]/sda/g')
365}
366
367dev_wait_or_error() {
368 is_developer_mode || on_error # terminal if we get here in regular mode.
369 log ""
370 log "A developer key change is required to proceed."
371 plog "Please wait 300 seconds . . ."
372 # TODO(wad) Divvy the total up into a few different prompts.
373 make_user_wait 300
374 log ""
375}
376
377recovery_wait() {
378 log ""
379 log "Preparing to recover your system image."
380 plog "If you do not wish to proceed, please reboot in the next 10 seconds ."
381 make_user_wait 10
382 log ""
383 log "System recovery is beginning."
384 log "Please do not disconnect from a power source or power down."
385 log ""
386}
387
388make_user_wait() {
389 local delay_in_sec="${1-300}"
390 while [ $delay_in_sec -gt 0 ]; do
391 plog " ."
392 sleep 1
393 delay_in_sec=$((delay_in_sec - 1))
394 done
395 log ""
396}
397
398check_key_or_wait() {
399 plog "Searching the system disk for a matching kernel key . . ."
400 if ! cgpt find -t kernel -M "$1" "$DST"; then
401 log " failed."
402 dev_wait_or_error
403 return 0
404 fi
405 log " found."
406
407 plog "Validating matching signature(s) . . ."
408 # If we found a keyblock, at the right offset, make sure it actually signed
409 # the subsequent payload.
410 local kdev=
411 for kdev in $(cgpt find -t kernel -M "$1" "$DST"); do
412 plog " ."
413 verify_kernel_signature "$kdev" "/tmp/kern.keyblock" || continue
414 log " done."
415 return 0
416 done
417
418 log " failed."
419 dev_wait_or_error
420 return 0
421}
422
423# Never returns on success.
424attempt_shim_script() {
425 # TODO(wad) Add static root of trust validation then remove the next line.
426 # http://crosbug/8390
427 is_developer_mode || return 1
428
429 # Now we will either install a colocated Chromium OS image by
430 # checking the keys on KERN-B against any on disk (KERN-[ABC])
431 # or by checking a signed script on stateful.
432 dlog "Checking for a shim script . . ."
433 [ -x "$SHIM_SCRIPT" ] || return 1
434 [ -f "$SHIM_VBLOCK" ] || return 1
435 log "Shim script and signing file found!"
436
437 plog "Verifying the signature on the script . . ."
438 # Extract pubkey and check signature
439 if ! dev_sign_file --verify "$SHIM_SCRIPT" \
440 --vblock "$SHIM_VBLOCK" \
441 --keyblock /tmp/shim.keyblock; then
442 log " failed."
443 fi
444 log " done."
445
446 # If we're not in developer mode, this will be terminal on failure.
447 check_key_or_wait /tmp/shim.keyblock
448
449 # Run the user supplied script. It is done in the current environment
450 # to avoid needing anything other than the script/program on the partition.
451 log "Executing shim script . . ."
452
453 dlog "calling $SHIM_SCRIPT with exec"
454 # Fix up the input/output
455 stop_log_file
456 set +x
457 exec &> "$TTY_PATH"1
458 exec < "$TTY_PATH"1
459 # Call the script!
460 exec "$SHIM_SCRIPT"
461
462 # Never reached.
463 save_log_file
464 return 0
465}
466
467get_kern_b_device() {
468 # TODO(wad) By changing boot priority, could we end up
469 # checking the recovery image or the recovery image could not
470 # be in slot A. In that case, it should fail in normal mode.
471 KERN_B_DEV=${REAL_USB_DEV%[0-9]*}4
472 if [ ! -b "${KERN_B_DEV}" ]; then
473 return 1
474 fi
475 return 0
476}
477
478get_real_kern_b_hash() {
479 REAL_KERN_B_HASH=$(dd if="${KERN_B_DEV}" | \
480 sha1sum | \
481 cut -f1 -d' ')
482 [ -n "$REAL_KERN_B_HASH" ]
483}
484
485verify_kernel_signature() {
486 local kern_dev="$1"
487 local keyblock="$2"
488
489 if ! dd if="$kern_dev" of="/tmp/kern.bin"; then
490 return 1
491 fi
492
493 # Validates the signature and outputs a keyblock.
494 if ! vbutil_kernel --verify "/tmp/kern.bin" \
495 --keyblock "$keyblock"; then
496 return 1
497 fi
498 return 0
499}
500
501verify_install_kernel() {
502 get_kern_b_device || return 1
503 get_real_kern_b_hash || return 1
504
505 # TODO(wad) check signatures from stateful on kern b using the
506 # root of trust instead of using a baked in cmdline.
507 if [ "$REAL_KERN_B_HASH" != "$KERN_ARG_KERN_B_HASH" ]; then
508 if ! is_developer_mode; then
509 log "The recovery image cannot be verified."
510 return 1
511 fi
512
513 # Extract the kernel so that vbutil_kernel will happily consume it.
514 log "Checking the install kernel for a valid developer signature . . ."
515 verify_kernel_signature "$KERN_B_DEV" "/tmp/kern_b.keyblock" || return 1
516 check_key_or_wait /tmp/kern_b.keyblock
517 fi
518 return 0
519}
520
521touch_developer_mode_file() {
522 is_developer_mode || return 1
523 mount -n -o rw -t ext3 "$DST"1 "$STATEFUL_MNT" || return 1
524 touch "$STATEFUL_MNT/.developer_mode" || return 1
525 umount "$STATEFUL_MNT" || return 1
526 return 0
527}
528
529call_image_recovery_script() {
530 mount -t tmpfs -o mode=1777 none "$USB_MNT/tmp" || return 1
531 move_mounts || return 1
532
533 # Start the copy.
534 log ""
535 log "Beginning system image copy. This will take some time . . ."
536 log ""
537 # Until images are built with the installer keyblock in KERN-B by
538 # default, we keep copying over the installer vblock from the
539 # stateful partition.
540 # TODO(wad) http://crosbug/8378
541 export IS_RECOVERY_INSTALL=1
542 chroot "$USB_MNT" \
543 /usr/sbin/chromeos-install --run_as_root --yes \
544 --payload_image="$1" \
545 --use_payload_kern_b
546 if [ $? -eq 0 ]; then
547 log "System copy completed."
548 else
549 log "Error performing system recovery!"
550 fi
551
552 # Clean up doesn't need to be successful.
553 umount "$USB_MNT/tmp"
554 unmove_mounts
555
556 # If we're in developer mode, touch the .developer_mode file on stateful
557 # to avoid a bonus wait period on reboot.
558 # Failure here is non-terminal and it may not succeed just because the
559 # partition table of the destination has not been synchronized.
560 dlog "Prepping destination stateful to avoid a secondary delay."
561 touch_developer_mode_file && log "Prepped image for developer use."
562
563 return 0
564}
565
566clear_tpm() {
567 plog "Resetting security device . . ."
568 # TODO(wad) should we fail on this?
569 tpmc ppon || dlog "tpmc ppon error: $?"
570 tpmc clear || dlog "tpmc clear error: $?"
571 tpmc enable || dlog "tpmc enable error: $?"
572 tpmc activate || dlog "tpmc activate error: $?"
573 tpmc pplock || dlog "tpmc pplock error: $?"
574 log " done."
575 return 0
576}
577
578save_log_file() {
579 local log_dev="${1:-$LOG_DEV}"
580 [ -z "$log_dev" ] && return 0
581 # The recovery stateful is usually too small for ext3.
582 # TODO(wad) We could also just write the data raw if needed.
583 # Should this also try to save
584 dlog "Attempting to save log file: $LOG_FILE -> $log_dev"
585 mount -n -o sync,rw -t ext2 "$log_dev" /tmp
586 cp "$LOG_FILE" /tmp
587 umount -n /tmp
588}
589
590stop_log_file() {
591 # Drop logging
592 exec &> "$TTY_PATH"3
593 [ -n "$TAIL_PID" ] && kill $TAIL_PID
594}
595
596is_unofficial_root() {
597 [ $UNOFFICIAL_ROOT -eq 1 ]
598}
599
600set_unofficial_root() {
601 UNOFFICIAL_ROOT=1
602 return 0
603}
604
605recover_system() {
606 local source=$(strip_partition "$REAL_USB_DEV")
607 dlog "Beginning system recovery from $source"
608
609 recovery_wait
610
611 if is_unofficial_root; then
612 dlog "Attempting to use shim . . ."
613 # Mounting read only so a journal is not needed.
614 # If it fails, we can still proceed on a normal recovery path.
615 mount -n -o ro -t ext2 "$STATE_DEV" "$STATEFUL_MNT"
616 attempt_shim_script # never returns on success.
617 umount "$STATEFUL_MNT"
618 fi
619
620 # If we're not running a developer script then we're either
621 # installing a developer image or an official one. If we're
622 # in normal recovery mode, then we require that the KERN-B
623 # on the recovery image matches the hash on the command line.
624 # In developer mode, we will just check the keys.
625 verify_install_kernel || return 1
626
627 # Only clear on full installs. Shim scripts can call tpmc if they
628 # like. Only bGlobalLock will be in place in advance.
629 clear_tpm || return 1
630
631 call_image_recovery_script "$source" || return 1
632
633 return 0
634}
635
636use_new_root() {
637 move_mounts || on_error
638
639 # Chroot into newroot, erase the contents of the old /, and exec real init.
640 log "About to switch root"
641 stop_log_file
642 exec switch_root -c /dev/console "$NEWROOT_MNT" /sbin/init
643
644 # This should not really happen.
645 log "Failed to switch root."
646 save_log_file
647 return 1
648}
649
650is_developer_mode() {
651 # See Firmware High-Level Spec for details on CHSW values
652 CHSW=$(cat /sys/devices/platform/chromeos_acpi/CHSW)
653 # If the switch is unsupported, treat as developer mode.
654 [ -z "$CHSW" ] && return 0
655 if [ $CHSW -gt 0 -a $((CHSW & 32)) -eq 32 ]; then
656 return 0
657 fi
658 return 1
659}
660
661lock_tpm() {
662 if [ -z "$TPM_B_LOCKED" ]; then
663 # Depending on the system, the tpm may need to be started.
664 # Don't fail if it doesn't work though.
665 tpmc startup
666 tpmc ctest
667 if ! tpmc block; then
668 log "An unrecoverable error occurred with your security device"
669 log "Please power down and try again."
670 dlog "Failed to lock bGlobalLock."
671 on_error
672 return 1 # Never reached.
673 fi
674 TPM_B_LOCKED=y
675 fi
676 if [ -z "$TPM_PP_LOCKED" ]; then
677 # TODO: tpmc pplock if appropriate
678 TPM_PP_LOCKED=y
679 fi
680 return 0
681}
682
683# Extract and export kernel arguments
684export_args() {
685 # We trust out kernel command line explicitly.
686 local arg=
687 local key=
688 local val=
689 local acceptable_set='[A-Za-z0-9]_'
690 for arg in "$@"; do
691 key=$(echo "${arg%=*}" | tr 'a-z' 'A-Z' | \
692 tr -dc "$acceptable_set" '_')
693 val="${arg#*=}"
694 export "KERN_ARG_$key"="$val"
695 dlog "Exporting kernel argument $key as KERN_ARG_$key"
696 done
697}
698
699dlog() {
700 echo "$@" | tee -a "$TTY_PATH"2 "$TTY_PATH"3
701}
702
703# User visible
704log() {
705 echo "$@" | tee -a "$TTY_PATH"1 "$TTY_PATH"2
706}
707
708plog() {
709 # plog doesn't go to /dev/tty3 or log file.
710 printf "$@" | tee "$TTY_PATH"1 "$TTY_PATH"2
711}
712
713main() {
714 exec &> "$LOG_FILE"
715
716 # Set up basic mounts, console.
717 initial_mounts
718
719 # Send all verbose output to tty3
720 (tail -f "$LOG_FILE" > "$TTY_PATH"3) &
721 TAIL_PID=$!
722
723 log "Recovery image booting . . ."
724 log ""
725 log "Press Ctrl + Alt + F1 - F3 for more detailed information."
726 log ""
727
728 # Export the kernel command line as a parsed blob prepending KERN_ARG_ to each
729 # argument.
730 export_args $(cat /proc/cmdline | sed -e 's/"[^"]*"/DROPPED/g')
731
732 if is_developer_mode; then
733 log "! Your computer's developer mode switch is in the ENABLED position."
734 log "!"
735 log "! If this is unintentional, you should power off and toggle it back "
736 log "! after recovery is completed."
737 log ""
738 fi
739
740 if find_official_root || find_developer_root || find_shim_root; then
741 log " found."
742 else
743 log " not found."
744 on_error
745 fi
746
747 # Extract the real boot source which may be masked by dm-verity.
748 get_stateful_dev || on_error
749
750 # Check if we want to run from RAM, in the factory.
751 if check_if_factory_install; then
752 is_developer_mode || on_error # factory install requires it.
753 # Copy rootfs contents to tmpfs, then unmount USB device.
754 NEWROOT_MNT=/newroot
755 mount_tmpfs || on_error
756 copy_contents || on_error
757 copy_lsb || on_error
758 # USB device is unmounted, we can remove it now.
759 unmount_usb || on_error
760 # Switch to the new root
761 use_new_root || on_error
762 on_error # !! Never reached. !!
763 fi
764
765 # If not, we must be a recovery kernel.
766 NEWROOT_MNT="$USB_MNT"
767
768 # Always lock the TPM. If a NVRAM reset is ever needed, we can change it.
769 lock_tpm || on_error
770
771 # Perform a full device mapper root validation to avoid any unexpected
772 # failures during postinst. It also allows us to detect if the root is
773 # intentionally mismatched - such as during Chromium OS recovery with a
774 # Chrome OS recovery kernel.
775 if ! validate_recovery_root; then
776 is_developer_mode || on_error
777 find_developer_root || find_shim_root || on_error
778 log " found."
779 # This logic is duplicated to avoid double validating factory media. It
780 # will only be hit if a verified root can be mounted but is actually not
781 # intact.
782 get_stateful_dev || on_error
783 fi
784
785 get_dst || on_error
786
787 recover_system || on_error
788
789 log "System recovery is complete!"
790 log "Please remove the recovery device and reboot."
791
792 stop_log_file
793 # Save the recovery log to the target on success and the USB.
794 save_log_file "$DST"1
795 save_log_file
796
797 unmount_usb
798
799 log ""
800 log ""
801 plog "The system will automatically reboot in 2 minutes"
802 make_user_wait 120
803 reboot -f
804 exit 0
805}
806
807# Make this source-able for testing.
808if [ "$0" = "/init" ]; then
809 main "$@"
810 # Should never reach here.
811 exit 1
812fi
813