Add 'retrofit_gki' tool that retrofits GKIs
Bug: 210367929
Test: cd system/tools/mkbootimg/gki && atest --host-unit-test-only
Test: atest vts_gki_compliance_test
Change-Id: I972f98ec1c1c94a5df843a94d931eaf7c583a1f8
diff --git a/Android.bp b/Android.bp
index 5881443..77a5a32 100644
--- a/Android.bp
+++ b/Android.bp
@@ -54,18 +54,6 @@
}
python_binary_host {
- name: "generate_gki_certificate",
- defaults: ["mkbootimg_defaults"],
- main: "gki/generate_gki_certificate.py",
- srcs: [
- "gki/generate_gki_certificate.py",
- ],
- required: [
- "avbtool",
- ],
-}
-
-python_binary_host {
name: "mkbootimg",
defaults: ["mkbootimg_defaults"],
main: "mkbootimg.py",
diff --git a/gki/Android.bp b/gki/Android.bp
new file mode 100644
index 0000000..9ee7e39
--- /dev/null
+++ b/gki/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+python_binary_host {
+ name: "generate_gki_certificate",
+ defaults: ["mkbootimg_defaults"],
+ srcs: [
+ "generate_gki_certificate.py",
+ ],
+ required: [
+ "avbtool",
+ ],
+}
+
+sh_binary_host {
+ name: "retrofit_gki",
+ src: "retrofit_gki.sh",
+ required: [
+ "mkbootimg",
+ "unpack_bootimg",
+ ],
+}
+
+sh_test_host {
+ name: "retrofit_gki_test",
+ src: "retrofit_gki_test.sh",
+ data: [
+ "retrofit_gki.sh",
+ ],
+ data_bins: [
+ "mkbootimg",
+ "unpack_bootimg",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/gki/retrofit_gki.sh b/gki/retrofit_gki.sh
new file mode 100755
index 0000000..880831d
--- /dev/null
+++ b/gki/retrofit_gki.sh
@@ -0,0 +1,233 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Retrofits GKI boot images for upgrading devices.
+#
+
+set -eo errtrace
+
+usage() {
+ cat <<EOF
+Usage:
+ $0 --boot BOOT --init_boot INIT_BOOT --version {3,4} -o OUTPUT
+ $0 --boot BOOT --init_boot INIT_BOOT --vendor_boot VENDOR_BOOT --version 2 -o OUTPUT
+
+Options:
+ --boot FILE
+ Path to the generic boot image.
+ --init_boot FILE
+ Path to the generic init_boot image.
+ --vendor_boot FILE
+ Path to the vendor boot image.
+ --version {2,3,4}
+ Boot image header version to retrofit to.
+ -o, --output FILE
+ Path to the output boot image.
+ -v, --verbose
+ Show debug messages.
+ -h, --help, --usage
+ Show this help message.
+EOF
+}
+
+die() {
+ echo >&2 "ERROR:" "${@}"
+ exit 1
+}
+
+file_size() {
+ stat -c '%s' "$1"
+}
+
+get_arg() {
+ local arg="$1"
+ shift
+ while [[ "$#" -gt 0 ]]; do
+ if [[ "$1" == "${arg}" ]]; then
+ shift
+ echo "$1"
+ return
+ fi
+ shift
+ done
+}
+
+TEMP_DIR="$(mktemp -d --tmpdir retrofit_gki.XXXXXXXX)"
+readonly TEMP_DIR
+
+exit_handler() {
+ readonly EXIT_CODE="$?"
+ rm -rf "${TEMP_DIR}" ||:
+ exit "${EXIT_CODE}"
+}
+
+trap exit_handler EXIT
+trap 'die "line ${LINENO}, ${FUNCNAME:-<main>}(): \"${BASH_COMMAND}\" returned \"$?\"" ' ERR
+
+while [[ "$1" =~ ^- ]]; do
+ case "$1" in
+ --boot )
+ shift
+ BOOT_IMAGE="$1"
+ ;;
+ --init_boot )
+ shift
+ INIT_BOOT_IMAGE="$1"
+ ;;
+ --vendor_boot )
+ shift
+ VENDOR_BOOT_IMAGE="$1"
+ ;;
+ --version )
+ shift
+ OUTPUT_BOOT_IMAGE_VERSION="$1"
+ ;;
+ -o | --output )
+ shift
+ OUTPUT_BOOT_IMAGE="$1"
+ ;;
+ -v | --verbose )
+ VERBOSE=true
+ ;;
+ -- )
+ shift
+ break
+ ;;
+ -h | --help | --usage )
+ usage
+ exit 0
+ ;;
+ * )
+ echo >&2 "Unexpected flag: '$1'"
+ usage >&2
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+declare -ir OUTPUT_BOOT_IMAGE_VERSION
+readonly BOOT_IMAGE
+readonly INIT_BOOT_IMAGE
+readonly VENDOR_BOOT_IMAGE
+readonly OUTPUT_BOOT_IMAGE
+readonly VERBOSE
+
+# Make sure the input arguments make sense.
+[[ -f "${BOOT_IMAGE}" ]] ||
+ die "argument '--boot': not a regular file: '${BOOT_IMAGE}'"
+[[ -f "${INIT_BOOT_IMAGE}" ]] ||
+ die "argument '--init_boot': not a regular file: '${INIT_BOOT_IMAGE}'"
+if [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -lt 2 ]] || [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -gt 4 ]]; then
+ die "argument '--version': valid choices are {2, 3, 4}"
+elif [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 2 ]]; then
+ [[ -f "${VENDOR_BOOT_IMAGE}" ]] ||
+ die "argument '--vendor_boot': not a regular file: '${VENDOR_BOOT_IMAGE}'"
+fi
+[[ -z "${OUTPUT_BOOT_IMAGE}" ]] &&
+ die "argument '--output': cannot be empty"
+
+readonly BOOT_DIR="${TEMP_DIR}/boot"
+readonly INIT_BOOT_DIR="${TEMP_DIR}/init_boot"
+readonly VENDOR_BOOT_DIR="${TEMP_DIR}/vendor_boot"
+readonly VENDOR_BOOT_MKBOOTIMG_ARGS="${TEMP_DIR}/vendor_boot.mkbootimg_args"
+readonly OUTPUT_RAMDISK="${TEMP_DIR}/out.ramdisk"
+readonly OUTPUT_BOOT_SIGNATURE="${TEMP_DIR}/out.boot_signature"
+
+readonly MKBOOTIMG="${MKBOOTIMG:-mkbootimg}"
+readonly UNPACK_BOOTIMG="${UNPACK_BOOTIMG:-unpack_bootimg}"
+
+# Fixed boot signature size for boot v2 & v3 for easy discovery in VTS.
+readonly RETROFITTED_BOOT_SIGNATURE_SIZE=$(( 16 << 10 ))
+
+
+#
+# Preparations are done. Now begin the actual work.
+#
+( [[ -n "${VERBOSE}" ]] && set -x
+ "${UNPACK_BOOTIMG}" --boot_img "${BOOT_IMAGE}" --out "${BOOT_DIR}" >/dev/null
+ "${UNPACK_BOOTIMG}" --boot_img "${INIT_BOOT_IMAGE}" --out "${INIT_BOOT_DIR}" >/dev/null
+ cat "${BOOT_DIR}/boot_signature" "${INIT_BOOT_DIR}/boot_signature" > "${OUTPUT_BOOT_SIGNATURE}"
+)
+
+declare -a mkbootimg_args=()
+
+if [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 4 ]]; then
+ mkbootimg_args+=( \
+ --header_version 4 \
+ --kernel "${BOOT_DIR}/kernel" \
+ --ramdisk "${INIT_BOOT_DIR}/ramdisk" \
+ --boot_signature "${OUTPUT_BOOT_SIGNATURE}" \
+ )
+elif [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 3 ]]; then
+ mkbootimg_args+=( \
+ --header_version 3 \
+ --kernel "${BOOT_DIR}/kernel" \
+ --ramdisk "${INIT_BOOT_DIR}/ramdisk" \
+ )
+elif [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 2 ]]; then
+ ( [[ -n "${VERBOSE}" ]] && set -x
+ "${UNPACK_BOOTIMG}" --boot_img "${VENDOR_BOOT_IMAGE}" --out "${VENDOR_BOOT_DIR}" \
+ --format=mkbootimg -0 > "${VENDOR_BOOT_MKBOOTIMG_ARGS}"
+ cat "${VENDOR_BOOT_DIR}/vendor_ramdisk" "${INIT_BOOT_DIR}/ramdisk" > "${OUTPUT_RAMDISK}"
+ )
+
+ declare -a vendor_boot_args=()
+ while IFS= read -r -d '' ARG; do
+ vendor_boot_args+=("${ARG}")
+ done < "${VENDOR_BOOT_MKBOOTIMG_ARGS}"
+
+ pagesize="$(get_arg --pagesize "${vendor_boot_args[@]}")"
+ kernel_offset="$(get_arg --kernel_offset "${vendor_boot_args[@]}")"
+ ramdisk_offset="$(get_arg --ramdisk_offset "${vendor_boot_args[@]}")"
+ tags_offset="$(get_arg --tags_offset "${vendor_boot_args[@]}")"
+ dtb_offset="$(get_arg --dtb_offset "${vendor_boot_args[@]}")"
+ kernel_cmdline="$(get_arg --vendor_cmdline "${vendor_boot_args[@]}")"
+
+ mkbootimg_args+=( \
+ --header_version 2 \
+ --base 0 \
+ --kernel_offset "${kernel_offset}" \
+ --ramdisk_offset "${ramdisk_offset}" \
+ --second_offset 0 \
+ --tags_offset "${tags_offset}" \
+ --dtb_offset "${dtb_offset}" \
+ --cmdline "${kernel_cmdline}" \
+ --pagesize "${pagesize}" \
+ --kernel "${BOOT_DIR}/kernel" \
+ --ramdisk "${OUTPUT_RAMDISK}" \
+ )
+ if [[ -f "${VENDOR_BOOT_DIR}/dtb" ]]; then
+ mkbootimg_args+=(--dtb "${VENDOR_BOOT_DIR}/dtb")
+ fi
+fi
+
+( [[ -n "${VERBOSE}" ]] && set -x
+ "${MKBOOTIMG}" "${mkbootimg_args[@]}" --output "${OUTPUT_BOOT_IMAGE}"
+)
+
+if [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 2 ]] || [[ "${OUTPUT_BOOT_IMAGE_VERSION}" -eq 3 ]]; then
+ if [[ "$(file_size "${OUTPUT_BOOT_SIGNATURE}")" -gt "${RETROFITTED_BOOT_SIGNATURE_SIZE}" ]]; then
+ die "boot signature size is larger than ${RETROFITTED_BOOT_SIGNATURE_SIZE}"
+ fi
+ # Pad the boot signature and append it to the end.
+ ( [[ -n "${VERBOSE}" ]] && set -x
+ truncate -s "${RETROFITTED_BOOT_SIGNATURE_SIZE}" "${OUTPUT_BOOT_SIGNATURE}"
+ cat "${OUTPUT_BOOT_SIGNATURE}" >> "${OUTPUT_BOOT_IMAGE}"
+ )
+fi
diff --git a/gki/retrofit_gki_test.sh b/gki/retrofit_gki_test.sh
new file mode 100755
index 0000000..5810798
--- /dev/null
+++ b/gki/retrofit_gki_test.sh
@@ -0,0 +1,150 @@
+#!/bin/bash
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+set -eo errtrace
+
+die() {
+ echo >&2 "ERROR:" "${@}"
+ exit 1
+}
+
+trap 'die "line ${LINENO}, ${FUNCNAME:-<main>}(): \"${BASH_COMMAND}\" returned \"$?\"" ' ERR
+
+# Figure out where we are and where to look for test executables.
+cd "$(dirname "${BASH_SOURCE[0]}")"
+TEST_DIR="$(pwd)"
+readonly TEST_DIR
+readonly TEMP_DIR="${TEST_DIR}/stage.retrofit_gki_test"
+
+export PATH="${TEST_DIR}:${PATH}"
+rm -rf "${TEMP_DIR}"
+mkdir -p "${TEMP_DIR}"
+
+# Generate some test files.
+readonly TEST_DTB="${TEMP_DIR}/dtb"
+readonly TEST_KERNEL="${TEMP_DIR}/kernel"
+readonly TEST_RAMDISK="${TEMP_DIR}/ramdisk"
+readonly TEST_VENDOR_RAMDISK="${TEMP_DIR}/vendor_ramdisk"
+readonly TEST_KERNEL_SIGNATURE="${TEMP_DIR}/kernel.boot_signature"
+readonly TEST_RAMDISK_SIGNATURE="${TEMP_DIR}/ramdisk.boot_signature"
+
+readonly TEST_V2_RETROFITTED_RAMDISK="${TEMP_DIR}/retrofitted.ramdisk"
+readonly TEST_RETROFITTED_SIGNATURE="${TEMP_DIR}/retrofitted.boot_signature"
+readonly TEST_PADDED_RETROFITTED_SIGNATURE="${TEMP_DIR}/retrofitted.boot_signature.padded"
+
+readonly TEST_BOOT_IMAGE="${TEMP_DIR}/boot.img"
+readonly TEST_INIT_BOOT_IMAGE="${TEMP_DIR}/init_boot.img"
+readonly TEST_VENDOR_BOOT_IMAGE="${TEMP_DIR}/vendor_boot.img"
+
+( # Run these in subshell because dd is noisy.
+ dd if=/dev/urandom of="${TEST_KERNEL}" bs=1024 count=10
+ dd if=/dev/urandom of="${TEST_RAMDISK}" bs=1024 count=10
+ dd if=/dev/urandom of="${TEST_KERNEL_SIGNATURE}" bs=1024 count=1
+ dd if=/dev/urandom of="${TEST_RAMDISK_SIGNATURE}" bs=1024 count=1
+ dd if=/dev/urandom of="${TEST_DTB}" bs=1024 count=10
+ dd if=/dev/urandom of="${TEST_VENDOR_RAMDISK}" bs=1024 count=10
+) 2> /dev/null
+
+cat "${TEST_VENDOR_RAMDISK}" "${TEST_RAMDISK}" > "${TEST_V2_RETROFITTED_RAMDISK}"
+cat "${TEST_KERNEL_SIGNATURE}" "${TEST_RAMDISK_SIGNATURE}" > "${TEST_RETROFITTED_SIGNATURE}"
+cp "${TEST_RETROFITTED_SIGNATURE}" "${TEST_PADDED_RETROFITTED_SIGNATURE}"
+truncate -s $(( 16 << 10 )) "${TEST_PADDED_RETROFITTED_SIGNATURE}"
+
+mkbootimg \
+ --header_version 4 \
+ --kernel "${TEST_KERNEL}" \
+ --boot_signature "${TEST_KERNEL_SIGNATURE}" \
+ --output "${TEST_BOOT_IMAGE}"
+mkbootimg \
+ --header_version 4 \
+ --ramdisk "${TEST_RAMDISK}" \
+ --boot_signature "${TEST_RAMDISK_SIGNATURE}" \
+ --output "${TEST_INIT_BOOT_IMAGE}"
+mkbootimg \
+ --header_version 4 \
+ --pagesize 4096 \
+ --dtb "${TEST_DTB}" \
+ --vendor_ramdisk "${TEST_VENDOR_RAMDISK}" \
+ --vendor_boot "${TEST_VENDOR_BOOT_IMAGE}"
+
+readonly RETROFITTED_IMAGE="${TEMP_DIR}/retrofitted_boot.img"
+readonly RETROFITTED_IMAGE_DIR="${TEMP_DIR}/retrofitted_boot.img.unpack"
+
+
+#
+# Begin test.
+#
+echo >&2 "TEST: retrofit to boot v4"
+
+retrofit_gki.sh \
+ --boot "${TEST_BOOT_IMAGE}" \
+ --init_boot "${TEST_INIT_BOOT_IMAGE}" \
+ --version 4 \
+ --output "${RETROFITTED_IMAGE}"
+
+rm -rf "${RETROFITTED_IMAGE_DIR}"
+unpack_bootimg --boot_img "${RETROFITTED_IMAGE}" --out "${RETROFITTED_IMAGE_DIR}" > /dev/null
+
+cmp -s "${TEST_KERNEL}" "${RETROFITTED_IMAGE_DIR}/kernel" ||
+ die "unexpected diff: kernel"
+cmp -s "${TEST_RAMDISK}" "${RETROFITTED_IMAGE_DIR}/ramdisk" ||
+ die "unexpected diff: ramdisk"
+cmp -s "${TEST_RETROFITTED_SIGNATURE}" "${RETROFITTED_IMAGE_DIR}/boot_signature" ||
+ die "unexpected diff: boot signature"
+
+
+echo >&2 "TEST: retrofit to boot v3"
+
+retrofit_gki.sh \
+ --boot "${TEST_BOOT_IMAGE}" \
+ --init_boot "${TEST_INIT_BOOT_IMAGE}" \
+ --version 3 \
+ --output "${RETROFITTED_IMAGE}"
+
+rm -rf "${RETROFITTED_IMAGE_DIR}"
+unpack_bootimg --boot_img "${RETROFITTED_IMAGE}" --out "${RETROFITTED_IMAGE_DIR}" > /dev/null
+tail -c $(( 16 << 10 )) "${RETROFITTED_IMAGE}" > "${RETROFITTED_IMAGE_DIR}/boot_signature"
+
+cmp -s "${TEST_KERNEL}" "${RETROFITTED_IMAGE_DIR}/kernel" ||
+ die "unexpected diff: kernel"
+cmp -s "${TEST_RAMDISK}" "${RETROFITTED_IMAGE_DIR}/ramdisk" ||
+ die "unexpected diff: ramdisk"
+cmp -s "${TEST_PADDED_RETROFITTED_SIGNATURE}" "${RETROFITTED_IMAGE_DIR}/boot_signature" ||
+ die "unexpected diff: boot signature"
+
+
+echo >&2 "TEST: retrofit to boot v2"
+
+retrofit_gki.sh \
+ --boot "${TEST_BOOT_IMAGE}" \
+ --init_boot "${TEST_INIT_BOOT_IMAGE}" \
+ --vendor_boot "${TEST_VENDOR_BOOT_IMAGE}" \
+ --version 2 \
+ --output "${RETROFITTED_IMAGE}"
+
+rm -rf "${RETROFITTED_IMAGE_DIR}"
+unpack_bootimg --boot_img "${RETROFITTED_IMAGE}" --out "${RETROFITTED_IMAGE_DIR}" > /dev/null
+tail -c $(( 16 << 10 )) "${RETROFITTED_IMAGE}" > "${RETROFITTED_IMAGE_DIR}/boot_signature"
+
+cmp -s "${TEST_DTB}" "${RETROFITTED_IMAGE_DIR}/dtb" ||
+ die "unexpected diff: dtb"
+cmp -s "${TEST_KERNEL}" "${RETROFITTED_IMAGE_DIR}/kernel" ||
+ die "unexpected diff: kernel"
+cmp -s "${TEST_V2_RETROFITTED_RAMDISK}" "${RETROFITTED_IMAGE_DIR}/ramdisk" ||
+ die "unexpected diff: ramdisk"
+cmp -s "${TEST_PADDED_RETROFITTED_SIGNATURE}" "${RETROFITTED_IMAGE_DIR}/boot_signature" ||
+ die "unexpected diff: boot signature"