3 # Copyright (c) 2012 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.
7 # This script can change key (usually developer keys) and kernel config
8 # of kernels on an disk image (usually for SSD but also works for USB).
10 SCRIPT_BASE="$(dirname "$0")"
11 . "$SCRIPT_BASE/common_minimal.sh"
12 load_shflags || exit 1
14 # Constants used by DEFINE_*
15 VBOOT_BASE='/usr/share/vboot'
16 DEFAULT_KEYS_FOLDER="$VBOOT_BASE/devkeys"
17 DEFAULT_BACKUP_FOLDER='/mnt/stateful_partition/backups'
18 DEFAULT_PARTITIONS='2 4'
20 # TODO(hungte) The default image selection is no longer a SSD, so the script
21 # works more like "make_dev_image". We may change the file name in future.
22 ROOTDEV="$(rootdev -s 2>/dev/null)"
23 ROOTDEV_PARTITION="$(echo $ROOTDEV | sed -n 's/.*\([0-9][0-9]*\)$/\1/p')"
24 ROOTDEV_DISK="$(rootdev -s -d 2>/dev/null)"
25 ROOTDEV_KERNEL="$((ROOTDEV_PARTITION - 1))"
27 # DEFINE_string name default_value description flag
28 DEFINE_string image "$ROOTDEV_DISK" "Path to device or image file" "i"
29 DEFINE_string keys "$DEFAULT_KEYS_FOLDER" "Path to folder of dev keys" "k"
30 DEFINE_boolean remove_rootfs_verification \
31 $FLAGS_FALSE "Modify kernel boot config to disable rootfs verification" ""
32 DEFINE_string backup_dir \
33 "$DEFAULT_BACKUP_FOLDER" "Path of directory to store kernel backups" ""
34 DEFINE_string save_config "" \
35 "Base filename to store kernel configs to, instead of resigning." ""
36 DEFINE_string set_config "" \
37 "Base filename to load kernel configs from" ""
38 DEFINE_string partitions "" \
39 "List of partitions to examine (default: $DEFAULT_PARTITIONS)" ""
40 DEFINE_boolean recovery_key "$FLAGS_FALSE" \
41 "Use recovery key to sign image (to boot from USB" ""
42 DEFINE_boolean force "$FLAGS_FALSE" "Skip sanity checks and make the change" "f"
47 eval set -- "$FLAGS_ARGV"
48 ORIGINAL_PARTITIONS="$FLAGS_partitions"
49 : ${FLAGS_partitions:=$DEFAULT_PARTITIONS}
52 # ----------------------------------------------------------------------------
55 # a log file to keep the output results of executed command
56 EXEC_LOG="$(make_temp_file)"
59 # ----------------------------------------------------------------------------
61 # Removes rootfs verification from kernel boot parameter
62 remove_rootfs_verification() {
63 local new_root="PARTUUID=%U/PARTNROFF=1"
65 s| root=/dev/dm-[0-9] | root='"$new_root"' |
66 s| dm_verity[^=]*=[-0-9]*||g
71 remove_legacy_boot_rootfs_verification() {
72 # See src/scripts/create_legacy_bootloader_templates
74 local mount_point="$(make_temp_dir)"
76 debug_msg "Removing rootfs verification for legacy boot configuration."
77 mount_image_partition "$image" 12 "$mount_point" || return $FLAGS_FALSE
78 config_file="$mount_point/efi/boot/grub.cfg"
79 [ ! -f "$config_file" ] ||
80 sudo sed -i 's/^ *set default=2 *$/set default=0/g' "$config_file"
81 config_file="$mount_point/syslinux/default.cfg"
82 [ ! -f "$config_file" ] ||
83 sudo sed -i 's/-vusb/-usb/g; s/-vhd/-hd/g' "$config_file"
84 sudo umount "$mount_point"
87 # Wrapped version of dd
89 # oflag=sync is safer, but since we need bs=512, syncing every block would be
91 dd "$@" >"$EXEC_LOG" 2>&1 ||
92 err_die "Failed in [dd $@], Message: $(cat "$EXEC_LOG")"
95 # Prints a more friendly name from kernel index number
112 find_valid_kernel_partitions() {
114 local valid_partitions=""
115 for part_id in $*; do
116 local name="$(cros_kernel_name $part_id)"
117 local kernel_part="$(make_partition_dev "$FLAGS_image" "$part_id")"
118 if [ -z "$(dump_kernel_config "$kernel_part" 2>"$EXEC_LOG")" ]; then
119 echo "INFO: $name: no kernel boot information, ignored." >&2
121 [ -z "$valid_partitions" ] &&
122 valid_partitions="$part_id" ||
123 valid_partitions="$valid_partitions $part_id"
127 debug_msg "find_valid_kernel_partitions: [$*] -> [$valid_partitions]"
128 echo "$valid_partitions"
131 # Resigns a kernel on SSD or image.
132 resign_ssd_kernel() {
133 # bs=512 is the fixed block size for dd and cgpt
135 local ssd_device="$1"
137 # reasonable size for current kernel partition
138 local min_kernel_size=32000
139 local max_kernel_size=65536
140 local resigned_kernels=0
142 for kernel_index in $FLAGS_partitions; do
143 local old_blob="$(make_temp_file)"
144 local new_blob="$(make_temp_file)"
145 local name="$(cros_kernel_name $kernel_index)"
146 local rootfs_index="$(($kernel_index + 1))"
148 debug_msg "Probing $name information"
150 offset="$(partoffset "$ssd_device" "$kernel_index")" ||
151 err_die "Failed to get partition $kernel_index offset from $ssd_device"
152 size="$(partsize "$ssd_device" "$kernel_index")" ||
153 err_die "Failed to get partition $kernel_index size from $ssd_device"
154 if [ ! $size -gt $min_kernel_size ]; then
155 echo "INFO: $name seems too small ($size), ignored."
158 if [ ! $size -le $max_kernel_size ]; then
159 echo "INFO: $name seems too large ($size), ignored."
163 debug_msg "Reading $name from partition $kernel_index"
164 mydd if="$ssd_device" of="$old_blob" bs=$bs skip=$offset count=$size
166 debug_msg "Checking if $name is valid"
168 if ! kernel_config="$(dump_kernel_config "$old_blob" 2>"$EXEC_LOG")"; then
169 debug_msg "dump_kernel_config error message: $(cat "$EXEC_LOG")"
170 echo "INFO: $name: no kernel boot information, ignored."
174 if [ -n "${FLAGS_save_config}" ]; then
175 # Save current kernel config
176 local old_config_file
177 old_config_file="${FLAGS_save_config}.$kernel_index"
178 echo "Saving $name config to $old_config_file"
179 echo "$kernel_config" > "$old_config_file"
180 # Just save; don't resign
184 if [ -n "${FLAGS_set_config}" ]; then
185 # Set new kernel config from file
186 local new_config_file
187 new_config_file="${FLAGS_set_config}.$kernel_index"
188 kernel_config="$(cat "$new_config_file")" ||
189 err_die "Failed to read new kernel config from $new_config_file"
190 debug_msg "New kernel config: $kernel_config)"
191 echo "$name: Replaced config from $new_config_file"
194 if [ ${FLAGS_remove_rootfs_verification} = $FLAGS_FALSE ]; then
195 debug_msg "Bypassing rootfs verification check"
197 debug_msg "Changing boot parameter to remove rootfs verification"
198 kernel_config="$(remove_rootfs_verification "$kernel_config")"
199 debug_msg "New kernel config: $kernel_config"
200 echo "$name: Disabled rootfs verification."
201 remove_legacy_boot_rootfs_verification "$ssd_device"
204 local new_kernel_config_file="$(make_temp_file)"
205 echo -n "$kernel_config" >"$new_kernel_config_file"
207 debug_msg "Re-signing $name from $old_blob to $new_blob"
208 debug_msg "Using key: $KERNEL_DATAKEY"
210 --repack "$new_blob" \
211 --keyblock "$KERNEL_KEYBLOCK" \
212 --config "$new_kernel_config_file" \
213 --signprivate "$KERNEL_DATAKEY" \
214 --oldblob "$old_blob" >"$EXEC_LOG" 2>&1 ||
215 err_die "Failed to resign $name. Message: $(cat "$EXEC_LOG")"
217 debug_msg "Creating new kernel image (vboot+code+config)"
218 local new_kern="$(make_temp_file)"
219 cp "$old_blob" "$new_kern"
220 mydd if="$new_blob" of="$new_kern" conv=notrunc
222 if is_debug_mode; then
223 debug_msg "for debug purposes, check *.dbgbin"
224 cp "$old_blob" old_blob.dbgbin
225 cp "$new_blob" new_blob.dbgbin
226 cp "$new_kern" new_kern.dbgbin
229 debug_msg "Verifying new kernel and keys"
231 --verify "$new_kern" \
232 --signpubkey "$KERNEL_PUBKEY" --verbose >"$EXEC_LOG" 2>&1 ||
233 err_die "Failed to verify new $name. Message: $(cat "$EXEC_LOG")"
235 debug_msg "Backup old kernel blob"
236 local backup_date_time="$(date +'%Y%m%d_%H%M%S')"
237 local backup_name="$(echo "$name" | sed 's/ /_/g; s/^K/k/')"
238 local backup_file_name="${backup_name}_${backup_date_time}.bin"
239 local backup_file_path="$FLAGS_backup_dir/$backup_file_name"
240 if mkdir -p "$FLAGS_backup_dir" &&
241 cp -f "$old_blob" "$backup_file_path"; then
242 echo "Backup of $name is stored in: $backup_file_path"
244 echo "WARNING: Cannot create file in $FLAGS_backup_dir... Ignore backups."
247 debug_msg "Writing $name to partition $kernel_index"
255 resigned_kernels=$(($resigned_kernels + 1))
257 debug_msg "Make the root file system writable if needed."
258 # TODO(hungte) for safety concern, a more robust way would be to:
259 # (1) change kernel config to ro
260 # (2) check if we can enable rw mount
261 # (3) change kernel config to rw
262 if [ ${FLAGS_remove_rootfs_verification} = $FLAGS_TRUE ]; then
263 local root_offset_sector=$(partoffset "$ssd_device" $rootfs_index)
264 local root_offset_bytes=$((root_offset_sector * 512))
265 if ! is_ext2 "$ssd_device" "$root_offset_bytes"; then
266 debug_msg "Non-ext2 partition: $ssd_device$rootfs_index, skip."
267 elif ! rw_mount_disabled "$ssd_device" "$root_offset_bytes"; then
268 debug_msg "Root file system is writable. No need to modify."
270 # disable the RO ext2 hack
271 debug_msg "Disabling rootfs ext2 RO bit hack"
272 enable_rw_mount "$ssd_device" "$root_offset_bytes" >"$EXEC_LOG" 2>&1 ||
273 err_die "Failed turning off rootfs RO bit. OS may be corrupted. " \
274 "Message: $(cat "$EXEC_LOG")"
278 # Sometimes doing "dump_kernel_config" or other I/O now (or after return to
279 # shell) will get the data before modification. Not a problem now, but for
280 # safety, let's try to sync more.
283 echo "$name: Re-signed with developer keys successfully."
286 # If we saved the kernel config, exit now so we don't print an error
287 if [ -n "${FLAGS_save_config}" ]; then
288 echo "(Kernels have not been resigned.)"
292 return $resigned_kernels
295 sanity_check_live_partitions() {
296 debug_msg "Partition sanity check"
297 if [ "$FLAGS_partitions" = "$ROOTDEV_KERNEL" ]; then
298 debug_msg "only for current active partition - safe."
301 if [ "$ORIGINAL_PARTITIONS" != "" ]; then
302 debug_msg "user has assigned partitions - provide more info."
303 echo "INFO: Making change to $FLAGS_partitions on $FLAGS_image."
307 ERROR: YOU ARE TRYING TO MODIFY THE LIVE SYSTEM IMAGE $FLAGS_image.
309 The system may become unusable after that change, especially when you have
310 some auto updates in progress. To make it safer, we suggest you to only
311 change the partition you have booted with. To do that, re-execute this command
314 sudo ./make_dev_ssd.sh $ORIGINAL_PARAMS --partitions $ROOTDEV_KERNEL
316 If you are sure to modify other partition, please invoke the command again and
317 explicitly assign only one target partition for each time (--partitions N )
322 sanity_check_live_firmware() {
323 debug_msg "Firmware compatibility sanity check"
324 if [ "$(crossystem mainfw_type)" = "developer" ]; then
325 debug_msg "developer type firmware in active."
328 debug_msg "Loading firmware to check root key..."
329 local bios_image="$(make_temp_file)"
330 local rootkey_file="$(make_temp_file)"
331 echo "INFO: checking system firmware..."
332 sudo flashrom -p internal:bus=spi -i GBB -r "$bios_image" >/dev/null 2>&1
333 gbb_utility -g --rootkey="$rootkey_file" "$bios_image" >/dev/null 2>&1
334 if [ ! -s "$rootkey_file" ]; then
335 debug_msg "failed to read root key from system firmware..."
337 # The magic 130 is counted by "od dev-rootkey" for the lines until the body
338 # of key is reached. Trailing bytes (0x00 or 0xFF - both may appear, and
339 # that's why we need to skip them) are started at line 131.
340 # TODO(hungte) compare with rootkey in $VBOOT_BASE directly.
341 local rootkey_hash="$(od "$rootkey_file" |
344 if [ "$rootkey_hash" = "a13642246ef93daaf75bd791446fec9b" ]; then
345 debug_msg "detected DEV root key in firmware."
348 debug_msg "non-devkey hash: $rootkey_hash"
353 ERROR: YOU ARE NOT USING DEVELOPER FIRMWARE, AND RUNNING THIS COMMAND MAY
354 THROW YOUR CHROMEOS DEVICE INTO UN-BOOTABLE STATE.
356 You need to either install developer firmware, or change system root key.
358 - To install developer firmware: type command
359 sudo chromeos-firmwareupdate --mode=todev
361 - To change system rootkey: disable firmware write protection (a hardware
362 switch) and then type command:
363 sudo ./make_dev_firmware.sh
365 If you are sure that you want to make such image without developer
366 firmware or you've already changed system root keys, please run this
367 command again with --force paramemeter:
369 sudo ./make_dev_ssd.sh --force $ORIGINAL_PARAMS
375 # ----------------------------------------------------------------------------
378 local num_given=$(echo "$FLAGS_partitions" | wc -w)
380 if [ "$FLAGS_recovery_key" = "$FLAGS_TRUE" ]; then
381 KERNEL_KEYBLOCK="$FLAGS_keys/recovery_kernel.keyblock"
382 KERNEL_DATAKEY="$FLAGS_keys/recovery_kernel_data_key.vbprivk"
383 KERNEL_PUBKEY="$FLAGS_keys/recovery_key.vbpubk"
385 KERNEL_KEYBLOCK="$FLAGS_keys/kernel.keyblock"
386 KERNEL_DATAKEY="$FLAGS_keys/kernel_data_key.vbprivk"
387 KERNEL_PUBKEY="$FLAGS_keys/kernel_subkey.vbpubk"
390 debug_msg "Prerequisite check"
398 # checks for running on a live system image.
399 if [ "$FLAGS_image" = "$ROOTDEV_DISK" ]; then
400 debug_msg "check valid kernel partitions for live system"
401 local valid_partitions="$(find_valid_kernel_partitions $FLAGS_partitions)"
402 [ -n "$valid_partitions" ] ||
403 err_die "No valid kernel partitions on $FLAGS_image ($FLAGS_partitions)."
404 FLAGS_partitions="$valid_partitions"
407 if [ "$FLAGS_force" = "$FLAGS_TRUE" ]; then
409 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
410 ! INFO: ALL SANITY CHECKS WERE BYPASSED. YOU ARE ON YOUR OWN. !
411 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
414 for i in $(seq 5 -1 1); do
415 echo -n "\rStart in $i second(s) (^C to abort)... " >&2
419 elif ! sanity_check_live_firmware ||
420 ! sanity_check_live_partitions; then
421 err_die "IMAGE $FLAGS_image IS NOT MODIFIED."
425 resign_ssd_kernel "$FLAGS_image" || num_signed=$?
427 debug_msg "Complete."
428 if [ $num_signed -gt 0 -a $num_signed -le $num_given ]; then
429 # signed something at least
430 echo "Successfully re-signed $num_signed of $num_given kernel(s)" \
431 " on device $FLAGS_image".
433 err_die "Failed re-signing kernels."
437 # People using this to process images may forget to add "-i",
438 # so adding parameter check is safer.
439 if [ "$#" -gt 0 ]; then
441 err_die "Unknown parameters: $@"