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

Side by Side Diff: mod_image_for_recovery.sh

Issue 4107003: mod_image_for_recovery: supprt new recovery model (Closed) Base URL: http://git.chromium.org/git/crosutils.git
Patch Set: fix up comments from anush 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/bash 1 #!/bin/bash
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 # Script to modify a pristine/dev Chrome OS image to be used for recovery 7 # This script modifies a base image to act as a recovery installer.
8 # If no kernel image is supplied, it will build a devkeys signed recovery
9 # kernel. Alternatively, a signed recovery kernel can be used to
10 # create a Chromium OS recovery image.
8 11
12 # Load common constants. This should be the first executable line.
13 # The path to common.sh should be relative to your script's location.
14 . "$(dirname "$0")/common.sh"
15
16 # Load functions and constants for chromeos-install
17 . "$(dirname "$0")/chromeos-common.sh"
18
19 # For update_partition_table
9 . "$(dirname "$0")/resize_stateful_partition.sh" 20 . "$(dirname "$0")/resize_stateful_partition.sh"
10 21
11 # Script must be run inside the chroot. 22
12 restart_in_chroot_if_needed $* 23 # We need to be in the chroot to emerge test packages.
24 assert_inside_chroot
13 25
14 get_default_board 26 get_default_board
15 27
16 # Constants 28 DEFINE_string board "$DEFAULT_BOARD" "Board for which the image was built" b
17 TEMP_IMG=$(mktemp "/tmp/temp_img.XXXXXX") 29 DEFINE_integer statefulfs_sectors 4096 \
18 RECOVERY_IMAGE="recovery_image.bin" 30 "Number of sectors to use for the stateful filesystem"
31 # Skips the build steps and just does the kernel swap.
32 DEFINE_string kernel_image "" \
33 "Path to a pre-built recovery kernel"
34 DEFINE_string kernel_outfile "" \
35 "Filename and path to emit the kernel outfile to. \
36 If empty, emits to IMAGE_DIR."
37 DEFINE_string image "" "Path to the image to use"
38 DEFINE_string to "" \
39 "Path to the image to create. If empty, defaults to \
40 IMAGE_DIR/recovery_image.bin."
41 DEFINE_boolean kernel_image_only $FLAGS_FALSE \
42 "Emit the recovery kernel image only"
43 DEFINE_boolean sync_keys $FLAGS_TRUE \
44 "Update the kernel to be installed with the vblock from stateful"
45 DEFINE_integer jobs -1 \
46 "How many packages to build in parallel at maximum." j
47 DEFINE_string build_root "/build" \
48 "The root location for board sysroots."
19 49
20 DEFINE_string board "$DEFAULT_BOARD" "Board for which the image was built" 50 DEFINE_string rootfs_hash "/tmp/rootfs.hash" \
21 DEFINE_string image "" "Location of the rootfs raw image file" 51 "Path where the rootfs hash should be stored."
22 DEFINE_string output "${RECOVERY_IMAGE}" \ 52
23 "(optional) output image name. Default: ${RECOVERY_IMAGE}" 53 # Keep in sync with build_image.
54 DEFINE_string keys_dir "/usr/share/vboot/devkeys" \
55 "Directory containing the signing keys."
24 56
25 # Parse command line 57 # Parse command line
26 FLAGS "$@" || exit 1 58 FLAGS "$@" || exit 1
27 eval set -- "${FLAGS_ARGV}" 59 eval set -- "${FLAGS_ARGV}"
28 60
61 EMERGE_CMD="emerge"
62 EMERGE_BOARD_CMD="emerge-${FLAGS_board}"
63
29 # No board, no default and no image set then we can't find the image 64 # No board, no default and no image set then we can't find the image
30 if [ -z $FLAGS_image ] && [ -z $FLAGS_board ] ; then 65 if [ -z $FLAGS_image ] && [ -z $FLAGS_board ] ; then
31 setup_board_warning 66 setup_board_warning
32 die "mod_image_for_recovery failed. No board set and no image set" 67 die "mod_image_for_recovery failed. No board set and no image set"
33 fi 68 fi
34 69
35 # We have a board name but no image set. Use image at default location 70 # We have a board name but no image set. Use image at default location
36 if [ -z $FLAGS_image ] ; then 71 if [ -z $FLAGS_image ] ; then
37 IMAGES_DIR="${DEFAULT_BUILD_ROOT}/images/${FLAGS_board}" 72 IMAGES_DIR="${DEFAULT_BUILD_ROOT}/images/${FLAGS_board}"
38 FILENAME="chromiumos_image.bin" 73 FILENAME="chromiumos_image.bin"
39 FLAGS_image="${IMAGES_DIR}/$(ls -t $IMAGES_DIR 2>&-| head -1)/${FILENAME}" 74 FLAGS_image="${IMAGES_DIR}/$(ls -t $IMAGES_DIR 2>&-| head -1)/${FILENAME}"
40 fi 75 fi
41 76
42 # Turn path into an absolute path. 77 # Turn path into an absolute path.
43 FLAGS_image=$(eval readlink -f ${FLAGS_image}) 78 FLAGS_image=`eval readlink -f ${FLAGS_image}`
44 79
45 # Abort early if we can't find the image 80 # Abort early if we can't find the image
46 if [ ! -f $FLAGS_image ] ; then 81 if [ ! -f $FLAGS_image ] ; then
47 echo "No image found at $FLAGS_image" 82 echo "No image found at $FLAGS_image"
48 exit 1 83 exit 1
49 fi 84 fi
50 85
86 # What cross-build are we targeting?
87 . "${FLAGS_build_root}/${FLAGS_board}/etc/make.conf.board_setup"
88 # Figure out ARCH from the given toolchain.
89 # TODO: Move to common.sh as a function after scripts are switched over.
90 TC_ARCH=$(echo "${CHOST}" | awk -F'-' '{ print $1 }')
91 case "${TC_ARCH}" in
92 arm*)
93 ARCH="arm"
94 error "ARM recovery mode is still in the works. Use a normal image for now."
95 ;;
96 *86)
97 ARCH="x86"
98 ;;
99 *)
100 error "Unable to determine ARCH from toolchain: ${CHOST}"
101 exit 1
102 esac
103
104 get_install_vblock() {
105 # If it exists, we need to copy the vblock over to stateful
106 # This is the real vblock and not the recovery vblock.
107 local stateful_offset=$(partoffset "$FLAGS_image" 1)
108 local stateful_mnt=$(mktemp -d)
109 local out=$(mktemp)
110
111 set +e
112 sudo mount -o ro,loop,offset=$((stateful_offset * 512)) \
113 "$FLAGS_image" $stateful_mnt
114 sudo cp "$stateful_mnt/vmlinuz_hd.vblock" "$out"
115 sudo chown $USER "$out"
116
117 sudo umount -d "$stateful_mnt"
118 rmdir "$stateful_mnt"
119 set -e
120 echo "$out"
121 }
122
123 emerge_recovery_kernel() {
124 echo "Emerging custom recovery initramfs and kernel"
125 local emerge_flags="-uDNv1 --usepkg=n --selective=n"
126
127 $EMERGE_BOARD_CMD \
128 $emerge_flags --binpkg-respect-use=y \
129 chromeos-initramfs || die "no initramfs"
130 USE="initramfs" $EMERGE_BOARD_CMD \
131 $emerge_flags --binpkg-respect-use=y \
132 virtual/kernel
133 }
134
135 create_recovery_kernel_image() {
136 local sysroot="${FLAGS_build_root}/${FLAGS_board}"
137 local vmlinuz="$sysroot/boot/vmlinuz"
138 local root_dev=$(sudo losetup -f)
139 local root_offset=$(partoffset "$FLAGS_image" 3)
140 local root_size=$(partsize "$FLAGS_image" 3)
141
142 sudo losetup \
143 -o $((root_offset * 512)) \
144 --sizelimit $((root_size * 512)) \
145 "$root_dev" \
146 "$FLAGS_image"
147
148 trap "sudo losetup -d $root_dev" EXIT
149
150 cros_root=/dev/sd%D%P
151 if [[ "${ARCH}" = "arm" ]]; then
152 cros_root='/dev/${devname}${rootpart}'
153 fi
154 if grep -q enable_rootfs_verification "${IMAGE_DIR}/boot.desc"; then
155 cros_root=/dev/dm-0
156 fi
157 # TODO(wad) LOAD FROM IMAGE KERNEL AND NOT BOOT.DESC
158 local verity_args=$(grep -- '--verity_' "${IMAGE_DIR}/boot.desc")
159 # Convert the args to the right names and clean up extra quoting.
160 # TODO(wad) just update these everywhere
161 verity_args=$(echo $verity_args | sed \
162 -e 's/verity_algorithm/verity_hash_alg/g' \
163 -e 's/verity_depth/verity_tree_depth/g' \
164 -e 's/"//g')
165
166 # Tie the installed recovery kernel to the final kernel. If we don't
167 # do this, a normal recovery image could be used to drop an unsigned
168 # kernel on without a key-change check.
169 # Doing this here means that the kernel and initramfs creation can
170 # be done independently from the image to be modified as long as the
171 # chromeos-recovery interfaces are the same. It allows for the signer
172 # to just compute the new hash and update the kernel command line during
173 # recovery image generation. (Alternately, it means an image can be created,
174 # modified for recovery, then passed to a signer which can then sign both
175 # partitions appropriately without needing any external dependencies.)
176 local kern_offset=$(partoffset "$FLAGS_image" 2)
177 local kern_size=$(partsize "$FLAGS_image" 2)
178 local kern_tmp=$(mktemp)
179 local kern_hash=
180
181 dd if="$FLAGS_image" bs=512 count=$kern_size skip=$kern_offset of="$kern_tmp"
182 # We're going to use the real signing block.
183 if [ $FLAGS_sync_keys -eq $FLAGS_TRUE ]; then
184 dd if="$INSTALL_VBLOCK" of="$kern_tmp" conv=notrunc
185 fi
186 local kern_hash=$(sha1sum "$kern_tmp" | cut -f1 -d' ')
187 rm "$kern_tmp"
188
189 # TODO(wad) add FLAGS_boot_args support too.
190 ${SCRIPTS_DIR}/build_kernel_image.sh \
191 --arch="${ARCH}" \
192 --to="$RECOVERY_KERNEL_IMAGE" \
193 --hd_vblock="$RECOVERY_KERNEL_VBLOCK" \
194 --vmlinuz="$vmlinuz" \
195 --working_dir="${IMAGE_DIR}" \
196 --boot_args="panic=60 cros_recovery kern_b_hash=$kern_hash" \
197 --keep_work \
198 --rootfs_image=${root_dev} \
199 --rootfs_hash=${FLAGS_rootfs_hash} \
200 --root=${cros_root} \
201 --keys_dir="${FLAGS_keys_dir}" \
202 --nouse_dev_keys \
203 ${verity_args}
204 sudo rm "$FLAGS_rootfs_hash"
205 sudo losetup -d "$root_dev"
206 trap - RETURN
207
208 # Update the EFI System Partition configuration so that the kern_hash check
209 # passes.
210 local efi_dev=$(sudo losetup -f)
211 local efi_offset=$(partoffset "$FLAGS_image" 12)
212 local efi_size=$(partsize "$FLAGS_image" 12)
213
214 sudo losetup \
215 -o $((efi_offset * 512)) \
216 --sizelimit $((efi_size * 512)) \
217 "$efi_dev" \
218 "$FLAGS_image"
219 local efi_dir=$(mktemp -d)
220 trap "sudo losetup -d $efi_dev && rmdir \"$efi_dir\"" EXIT
221 sudo mount "$efi_dev" "$efi_dir"
222 sudo sed -i -e "s/cros_legacy/cros_legacy kern_b_hash=$kern_hash/g" \
223 "$efi_dir/syslinux/usb.A.cfg" || true
224 # This will leave the hash in the kernel for all boots, but that should be
225 # safe.
226 sudo sed -i -e "s/cros_efi/cros_efi kern_b_hash=$kern_hash/g" \
227 "$efi_dir/efi/boot/grub.cfg" || true
228 sudo umount "$efi_dir"
229 sudo losetup -d "$efi_dev"
230 rmdir "$efi_dir"
231 trap - EXIT
232 }
233
234 install_recovery_kernel() {
235 local kern_a_offset=$(partoffset "$RECOVERY_IMAGE" 2)
236 local kern_a_size=$(partsize "$RECOVERY_IMAGE" 2)
237 local kern_b_offset=$(partoffset "$RECOVERY_IMAGE" 4)
238 local kern_b_size=$(partsize "$RECOVERY_IMAGE" 4)
239 # Backup original kernel to KERN-B
240 dd if="$RECOVERY_IMAGE" of="$RECOVERY_IMAGE" bs=512 \
241 count=$kern_a_size \
242 skip=$kern_a_offset \
243 seek=$kern_b_offset \
244 conv=notrunc
245
246 # We're going to use the real signing block.
247 if [ $FLAGS_sync_keys -eq $FLAGS_TRUE ]; then
248 dd if="$INSTALL_VBLOCK" of="$RECOVERY_IMAGE" bs=512 \
249 seek=$kern_b_offset \
250 conv=notrunc
251 fi
252
253 # Install the recovery kernel as primary.
254 dd if="$RECOVERY_KERNEL_IMAGE" of="$RECOVERY_IMAGE" bs=512 \
255 seek=$kern_a_offset \
256 count=$kern_a_size \
257 conv=notrunc
258
259 # Repeat for the legacy bioses.
260 # Replace vmlinuz.A with the recovery version
261 local sysroot="${FLAGS_build_root}/${FLAGS_board}"
262 local vmlinuz="$sysroot/boot/vmlinuz"
263 local esp_offset=$(partoffset "$RECOVERY_IMAGE" 12)
264 local esp_mnt=$(mktemp -d)
265 set +e
266 local failed=0
267 sudo mount -o loop,offset=$((esp_offset * 512)) "$RECOVERY_IMAGE" "$esp_mnt"
268 sudo cp "$vmlinuz" "$esp_mnt/syslinux/vmlinuz.A" || failed=1
269 sudo umount -d "$esp_mnt"
270 rmdir "$esp_mnt"
271 set -e
272 if [ $failed -eq 1 ]; then
273 echo "Failed to copy recovery kernel to ESP"
274 return 1
275 fi
276 return 0
277 }
278
279 maybe_resize_stateful() {
280 # Rebuild the image with a 1 sector stateful partition
281 local err=0
282 local small_stateful=$(mktemp)
283 dd if=/dev/zero of="$small_stateful" bs=512 \
284 count=${FLAGS_statefulfs_sectors}
285 trap "rm $small_stateful" RETURN
286 # Don't bother with ext3 for such a small image.
287 /sbin/mkfs.ext2 -F -b 4096 "$small_stateful"
288
289 # If it exists, we need to copy the vblock over to stateful
290 # This is the real vblock and not the recovery vblock.
291 local new_stateful_mnt=$(mktemp -d)
292
293 set +e
294 sudo mount -o loop $small_stateful $new_stateful_mnt
295 sudo cp "$INSTALL_VBLOCK" "$new_stateful_mnt/vmlinuz_hd.vblock"
296 sudo mkdir "$new_stateful_mnt/var"
297 sudo umount -d "$new_stateful_mnt"
298 rmdir "$new_stateful_mnt"
299 set -e
300
301 # Create a recovery image of the right size
302 # TODO(wad) Make the developer script case create a custom GPT with
303 # just the kernel image and stateful.
304 update_partition_table "$FLAGS_image" "$small_stateful" 4096 "$RECOVERY_IMAGE"
305 return $err
306 }
307
308 # main process begins here.
309
310 # Make sure this is really what the user wants, before nuking the device
311 echo "Creating recovery image ${FLAGS_to} from ${FLAGS_image} . . . "
312
313 set -e
51 set -u 314 set -u
52 set -e 315
53
54 # Constants
55 IMAGE_DIR="$(dirname "$FLAGS_image")" 316 IMAGE_DIR="$(dirname "$FLAGS_image")"
56 317 IMAGE_NAME="$(basename "$FLAGS_image")"
57 # Creates a dev recovery image using an existing dev install shim 318 RECOVERY_IMAGE="${FLAGS_to:-$IMAGE_DIR/recovery_image.bin}"
58 # If successful, content of --payload_dir is copied to a directory named 319 RECOVERY_KERNEL_IMAGE=\
59 # "dev_payload" under the root of stateful partition. 320 "${FLAGS_kernel_outfile:-${IMAGE_DIR}/recovery_vmlinuz.image}"
60 create_recovery_image() { 321 RECOVERY_KERNEL_VBLOCK="${RECOVERY_KERNEL_IMAGE}.vblock"
61 local src_img=$1 # base image 322 STATEFUL_DIR="$IMAGE_DIR/stateful_partition"
62 local src_state=$(mktemp "/tmp/src_state.XXXXXX") 323 SCRIPTS_DIR=$(dirname "$0")
63 local stateful_offset=$(partoffset ${src_img} 1) 324
64 local stateful_count=$(partsize ${src_img} 1) 325 # Mounts gpt image and sets up var, /usr/local and symlinks.
65 326 # If there's a dev payload, mount stateful
66 dd if="${src_img}" of="${src_state}" conv=notrunc bs=512 \ 327 # offset=$(partoffset "${FLAGS_from}/${filename}" 1)
67 skip=${stateful_offset} count=${stateful_count} 328 # sudo mount ${ro_flag} -o loop,offset=$(( offset * 512 )) \
68 329 # "${FLAGS_from}/${filename}" "${FLAGS_stateful_mountpt}"
69 # Mount original stateful partition to figure out its actual size 330 # If not, resize stateful to 1 sector.
70 local src_loop_dev=$(get_loop_dev) 331 #
71 trap "cleanup_loop_dev ${src_loop_dev}" EXIT 332
72 333 if [ $FLAGS_kernel_image_only -eq $FLAGS_TRUE -a \
73 # Setup loop dev 334 -n "$FLAGS_kernel_image" ]; then
74 sudo losetup $src_loop_dev $src_state 335 die "Cannot use --kernel_image_only with --kernel_image"
75 local block_size=$(sudo /sbin/dumpe2fs $src_loop_dev | grep "Block size:" \ 336 fi
76 | tr -d ' ' | cut -f2 -d:) 337
77 echo "block_size = $block_size" 338 INSTALL_VBLOCK=$(get_install_vblock)
78 local min_size=$(sudo /sbin/resize2fs -P $src_loop_dev | tr -d ' ' \ 339 if [ -z "$INSTALL_VBLOCK" ]; then
79 | cut -f2 -d:) 340 die "Could not copy the vblock from stateful."
80 echo "min_size = $min_size $block_size blocks" 341 fi
81 342
82 # Add 20%, convert to 512-byte sectors and round up to 2Mb boundary 343 if [ -z "$FLAGS_kernel_image" ]; then
83 local min_sectors=$(roundup $(((min_size * block_size * 120) / (512 * 100)))) 344 emerge_recovery_kernel
84 echo "min_sectors = ${min_sectors} 512-byte blocks" 345 create_recovery_kernel_image
85 sudo e2fsck -fp "${src_loop_dev}"
86 # Resize using 512-byte sectors
87 sudo /sbin/resize2fs $src_loop_dev ${min_sectors}s
88
89 # Delete the loop
90 trap - EXIT
91 cleanup_loop_dev ${src_loop_dev}
92
93 # Truncate the image at the new size
94 dd if=/dev/zero of=$src_state bs=512 seek=$min_sectors count=0
95
96 # Mount and touch .recovery # Soon not to be needed :/
97 local new_mnt=$(mktemp -d "/tmp/src_mnt.XXXXXX")
98 mkdir -p "${new_mnt}"
99 local new_loop_dev=$(get_loop_dev)
100 trap "cleanup_loop_dev ${new_loop_dev} && rmdir ${new_mnt} && \
101 rm -f ${src_state}" EXIT
102 sudo mount -o loop=${new_loop_dev} "${src_state}" "${new_mnt}"
103 trap "umount_from_loop_dev ${new_mnt} && rm -f ${src_state}" EXIT
104 sudo touch "${new_mnt}/.recovery"
105
106 (update_partition_table $src_img $src_state $min_sectors $TEMP_IMG)
107 # trap handler will handle unmount and clean up of loop device and temp files
108 }
109
110 # Main
111 DST_PATH="${IMAGE_DIR}/${FLAGS_output}"
112 echo "Making a copy of original image ${FLAGS_image}"
113 (create_recovery_image $FLAGS_image)
114
115 if [ -n ${TEMP_IMG} ] && [ -f ${TEMP_IMG} ]; then
116 mv -f $TEMP_IMG $DST_PATH
117 echo "Recovery image created at ${DST_PATH}"
118 else 346 else
119 echo "Failed to create recovery image" 347 RECOVERY_KERNEL_IMAGE="$FLAGS_kernel_image"
120 fi 348 fi
349 echo "Kernel emitted: $RECOVERY_KERNEL_IMAGE."
350
351 if [ $FLAGS_kernel_image_only -eq $FLAGS_TRUE ]; then
352 echo "Kernel emitted. Stopping there."
353 rm "$INSTALL_VBLOCK"
354 exit 0
355 fi
356
357 rm "$RECOVERY_IMAGE" || true # Start fresh :)
358
359 trap "rm \"$RECOVERY_IMAGE\" && rm \"$INSTALL_VBLOCK\"" EXIT
360
361 maybe_resize_stateful # Also copies the image
362
363 install_recovery_kernel
364
365 print_time_elapsed
366 trap - EXIT
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698