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

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