blob: d78242d08b4de9269ee8e94a114951f4df981131 [file] [log] [blame]
#!/bin/sh
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Script to generate an universal factory install shim image, by merging
# multiple images signed by different keys.
# CAUTION: Recovery shim images are not supported yet because they require the
# kernel partitions to be laid out in a special way
. "$(dirname "$(readlink -f "$0")")/factory_common.sh" || exit 1
# CGPT Header: PMBR, header, table; sec_table, sec_header
CGPT_START_SIZE=$((1 + 1 + 32))
CGPT_END_SIZE=$((32 + 1))
CGPT_BS="512"
# Alignment of partition sectors
PARTITION_SECTOR_ALIGNMENT=256
# Temp file to store layout, will be removed at exit.
LAYOUT_FILE="$(mktemp --tmpdir)"
# Special name for creating dummy partitions in layout file.
DUMMY_FILE_NAME="@DUMMYFILE@"
alert() {
echo "$*" >&2
}
die() {
alert "ERROR: $*"
exit 1
}
on_exit() {
rm -f "$LAYOUT_FILE"
}
# Returns offset aligned to alignment.
# If size is given, only align if size >= alignment.
image_alignment() {
local offset="$1"
local alignment="$2"
local size="$3"
# If size is assigned, align only if the new size is larger then alignment.
if [ "$((offset % alignment))" != "0" ]; then
if [ -z "$size" -o "$size" -ge "$alignment" ]; then
offset=$((offset + alignment - (offset % alignment)))
fi
fi
echo "$((offset))"
}
# Processes a logical disk image layout description file.
# Each entry in layout is a "file:partnum" entry (:partnum is optional),
# referring to the #partnum partition in file.
# The index starts at one, referring to the first partition in layout.
image_process_layout() {
local layout_file="$1"
local callback="$2"
shift
shift
local param="$@"
local index=0
while read layout; do
local image_file="${layout%:*}"
local part_num="${layout#*:}"
index="$((index + 1))"
[ "$image_file" != "$layout" ] || part_num=""
"$callback" "$image_file" "$part_num" "$index" "$param"
done <"$layout_file"
}
# Processes a list of disk geometry sectors into aligned (offset, sectors) form.
# The index starts at zero, referring to the partition table object itself.
image_process_geometry() {
local sectors_list="$1"
local callback="$2"
shift
shift
local param="$@"
local offset=0 sectors
local index=0
for sectors in $sectors_list; do
offset="$(image_alignment $offset $PARTITION_SECTOR_ALIGNMENT $sectors)"
"$callback" "$offset" "$sectors" "$index" "$param"
offset="$((offset + sectors))"
index="$((index + 1))"
done
}
# Callback of image_process_layout. Returns the size (in sectors) of given
# object (partition in image or file).
layout_get_sectors() {
local image_file="$1"
local part_num="$2"
if [ "$image_file" = "$DUMMY_FILE_NAME" ]; then
echo 1
elif [ -n "$part_num" ]; then
image_part_size "$image_file" "$part_num"
else
image_alignment "$(stat -c"%s" "$image_file")" $CGPT_BS ""
fi
}
# Callback of image_process_layout. Copies an input source object (file or
# partition) into specified partition on output file.
layout_copy_partition() {
local input_file="$1"
local input_part="$2"
local output_part="$3"
local output_file="$4"
alert "$(basename "$input_file"):$input_part =>" \
"$(basename "$output_file"):$output_part"
if [ "$input_file" = "$DUMMY_FILE_NAME" ]; then
true # do nothing
elif [ -n "$input_part" ]; then
image_partition_copy "$input_file" "$input_part" \
"$output_file" "$output_part"
# Update partition type information
local partition_type="$(cgpt show -q -n -t -i "$input_part" "$input_file")"
local partition_attr="$(cgpt show -q -n -A -i "$input_part" "$input_file")"
local partition_label="$(cgpt show -q -n -l -i "$input_part" "$input_file")"
cgpt add -t "$partition_type" -l "$partition_label" -A "$partition_attr" \
-i "$output_part" "$output_file"
else
image_update_partition "$output_file" "$output_part" <"$input_file"
fi
}
# Callback of image_process_geometry. Creates a partition by give offset,
# size(sectors), and index.
geometry_create_partition() {
local offset="$1"
local sectors="$2"
local index="$3"
local output_file="$4"
if [ "$offset" = "0" ]; then
# first entry is CGPT; ignore.
return
fi
cgpt add -b $offset -s $sectors -i $index -t reserved "$output_file"
}
# Callback of image_process_geometry. Prints the proper offset of current
# partition by give offset and size.
geometry_get_partition_offset() {
local offset="$1"
local sectors="$2"
local index="$3"
image_alignment "$offset" "$PARTITION_SECTOR_ALIGNMENT" "$sectors"
}
build_image_file() {
local layout_file="$1"
local output_file="$2"
local output_file_size=0
local sectors_list partition_offsets
# Check and obtain size information from input sources
sectors_list="$(image_process_layout "$layout_file" layout_get_sectors)"
# Calculate output image file size
partition_offsets="$(image_process_geometry \
"$CGPT_START_SIZE $sectors_list $CGPT_END_SIZE 1" \
geometry_get_partition_offset)"
output_file_size="$(echo "$partition_offsets" | tail -n 1)"
# Create empty image file
truncate -s "0" "$output_file" # starting with a new file is much faster.
truncate -s "$((output_file_size * CGPT_BS))" "$output_file"
# Initialize partition table (GPT)
cgpt create "$output_file"
cgpt boot -p "$output_file" >/dev/null
# Create partition tables
image_process_geometry "$CGPT_START_SIZE $sectors_list" \
geometry_create_partition \
"$output_file"
# Copy partitions content
image_process_layout "$layout_file" layout_copy_partition "$output_file"
}
# Add kernel-rootfs pair from image source to layout file.
add_kernel_rootfs_pair() {
local layout_file="$1"
local image_source="$2"
local kernel_source="$image_source:2"
local rootfs_source="$image_source:3"
echo "$kernel_source" >>"$layout_file"
echo "$rootfs_source" >>"$layout_file"
}
add_dummy_kernel_rootfs_pair() {
local layout_file="$1"
local kernel_source="$DUMMY_FILE_NAME"
local rootfs_source="$DUMMY_FILE_NAME"
echo "$kernel_source" >>"$layout_file"
echo "$rootfs_source" >>"$layout_file"
}
# Creates standard multiple image layout
create_standard_layout() {
local main_source="$1"
local layout_file="$2"
local image index
shift
shift
for image in "$main_source" "$@"; do
if [ ! -f "$image" ]; then
die "Cannot find input file $image."
fi
done
echo "$main_source:1" >>"$layout_file" # stateful partition
# Adding must-have kernel-rootfs pairs. There are 3 must-have pairs, namely:
# partition 2-3, 4-5, and 6-7.
local must_have_kernel_rootfs_pairs="3"
for index in $(seq 1 $must_have_kernel_rootfs_pairs); do
if [ "$#" -eq 0 ]; then
add_dummy_kernel_rootfs_pair "$layout_file"
else
# TODO(hungte) detect if input source is a recovery/USB image
add_kernel_rootfs_pair "$layout_file" "$1"
shift
fi
done
# Adding legacy partitions 8 to 12. They're OEM, reserved, reserved, RWFW,
# EFI respectively.
local legacy_partitions="$(seq 8 12)"
for index in $legacy_partitions; do
local partition_source="$DUMMY_FILE_NAME"
local size="$(cgpt show -s -i $index "$main_source")"
if [ "$size" -ne "0" ]; then
partition_source="$main_source:$index"
fi
echo "$partition_source" >>"$layout_file"
done
# Adding additional partitions if needed.
while [ "$#" -gt 0 ]; do
add_kernel_rootfs_pair "$layout_file" "$1"
shift
done
}
usage_die() {
alert "Usage: $SCRIPT [-m master] [-f] output shim1 [shim2 shim3 ...]"
alert " or $SCRIPT -l layout [-f] output"
exit 1
}
main() {
local force=""
local image=""
local output=""
local main_source=""
local index=""
local slots="0"
local layout_mode=""
while [ "$#" -gt 1 ]; do
case "$1" in
"-f" )
force="True"
shift
;;
"-m" )
main_source="$2"
shift
shift
;;
"-l" )
cat "$2" >"$LAYOUT_FILE"
layout_mode="TRUE"
shift
shift
;;
* )
break
esac
done
if [ -n "$layout_mode" ]; then
[ "$#" = 1 ] || usage_die
elif [ "$#" -lt 2 ]; then
alert "ERROR: invalid number of parameters ($#)."
usage_die
fi
if [ -z "$main_source" ]; then
main_source="$2"
fi
output="$1"
shift
if [ -f "$output" -a -z "$force" ]; then
die "Output file $output already exists. To overwrite the file, add -f."
fi
if [ -z "$layout_mode" ]; then
create_standard_layout "$main_source" "$LAYOUT_FILE" "$@"
fi
build_image_file "$LAYOUT_FILE" "$output"
echo ""
echo "Image created: $output"
}
set -e
trap on_exit EXIT
main "$@"