Empty merge of sc-v2-dev-plus-aosp-without-vendor@8084891

Bug: 214455710
Merged-In: Iebcf0e826ae0f61a00e2ec522ceeb0e8a6ceb8e8
Change-Id: I5a6fbace443c2140a1fe614a5be1119f4f2ab5fc
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..1c66c51
--- /dev/null
+++ b/gki/Android.bp
@@ -0,0 +1,80 @@
+// 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.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+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",
+    ],
+}
+
+genrule {
+    name: "gki_retrofitting_tools",
+    tools: [
+        "soong_zip",
+        "retrofit_gki",
+        "mkbootimg",
+        "unpack_bootimg",
+    ],
+    srcs: [
+        "README.md",
+    ],
+    cmd: "STAGE_DIR=$(genDir)/gki_retrofitting_tools && " +
+         "rm -rf $${STAGE_DIR} && mkdir -p $${STAGE_DIR} && " +
+         "cp $(location retrofit_gki) $${STAGE_DIR} && " +
+         "cp $(location mkbootimg) $${STAGE_DIR} && " +
+         "cp $(location unpack_bootimg) $${STAGE_DIR} && " +
+         "cp $(in) $${STAGE_DIR} && " +
+         "$(location soong_zip) -o $(out) -C $(genDir) -D $${STAGE_DIR}",
+    out: [
+        "gki_retrofitting_tools.zip",
+    ],
+    dist: {
+        targets: [
+            "gki_retrofitting_tools",
+        ],
+    },
+}
diff --git a/gki/Android.mk b/gki/Android.mk
new file mode 100644
index 0000000..c0af5ef
--- /dev/null
+++ b/gki/Android.mk
@@ -0,0 +1,36 @@
+#
+# 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.
+#
+
+_gsi_gki_product_names := \
+  aosp_arm \
+  aosp_arm64 \
+  aosp_x86 \
+  aosp_x86_64 \
+  gsi_arm \
+  gsi_arm64 \
+  gsi_x86 \
+  gsi_x86_64 \
+  gki_arm64 \
+  gki_x86_64 \
+
+# Add gki_retrofitting_tools to `m dist` of GSI/GKI for easy pickup.
+ifneq (,$(filter $(_gsi_gki_product_names),$(TARGET_PRODUCT)))
+
+droidcore-unbundled: gki_retrofitting_tools
+
+endif
+
+_gsi_gki_product_names :=
diff --git a/gki/README.md b/gki/README.md
new file mode 100644
index 0000000..d080f53
--- /dev/null
+++ b/gki/README.md
@@ -0,0 +1,115 @@
+# Android T GKI boot image retrofitting tools
+
+Starting from Android T the GKI boot images consist of the generic `boot.img`
+and `init_boot.img`. The `boot.img` contains the generic kernel, and
+`init_boot.img` contains the generic ramdisk.
+For upgrading devices whose `init_boot` partition is non-existent, this tool
+(or spec) can be used to retrofit a set of Android T GKI `boot` and `init_boot`
+partition images back into a single *retrofitted GKI boot image*.
+
+## Retrofitting the boot images
+
+1. Download the certified GKI `boot.img` and `init_boot.img`.
+2. Go to the build artifacts page of `aosp_arm64` on `aosp-master` branch on
+   https://ci.android.com/ and download `gki_retrofitting_tools.zip`.
+3. Unzip and make sure the tool is in `${PATH}`.
+
+   ```bash
+   unzip gki_retrofitting_tools.zip
+   export PATH="$(pwd)/gki_retrofitting_tools:${PATH}"
+   # See tool usage:
+   retrofit_gki --help
+   ```
+
+4. Create the retrofitted image. The `--version` argument lets you choose the
+   boot image header version of the retrofitted image. You would have to choose
+   this value according to the bootloader capabilities.
+
+   ```bash
+   # Retrofit to boot v4:
+   retrofit_gki --boot boot.img --init_boot init_boot.img \
+     --version 4 -o boot.retrofitted.img
+   # Retrofit to boot v2:
+   retrofit_gki --boot boot.img --init_boot init_boot.img \
+     --vendor_boot vendor_boot.img --version 2 -o boot.retrofitted.img
+   ```
+
+## Spec of the retrofitted images
+
+* The SOURCE `boot.img` and `init_boot.img` must be officially certified Android
+  T (or later) GKI.
+* The DEST retrofitted boot image must not set the security patch level in its
+  header. This is because the SOURCE `boot.img` and `init_boot.img` might have
+  different SPL value, thus making the boot header SPL of the retrofitted image
+  ill-defined. The SPL value must be defined by the chained vbmeta image of the
+  `boot` partition.
+* The `boot signature` of the DEST image is the concatenation of the
+  `boot signature` from the DEST `boot.img` and `init_boot.img`.
+* The DEST retrofitted boot image must pass the `vts_gki_compliance_test`
+  testcase.
+
+### Retrofit to boot image V4
+
+* The `kernel` of the DEST image must be from the SOURCE `boot.img`.
+* The `ramdisk` of the DEST image must be from the SOURCE `init_boot.img`.
+
+```
+  +---------------------+
+  | boot header         | 4096 bytes
+  +---------------------+
+  | kernel              | m pages
+  +---------------------+
+  | ramdisk             | n pages
+  +---------------------+
+  | boot signature      | g pages
+  +---------------------+
+```
+
+### Retrofit to boot image V3
+
+* The `kernel` of the DEST image must be from the SOURCE `boot.img`.
+* The `ramdisk` of the DEST image must be from the SOURCE `init_boot.img`.
+* The `boot_signature` section must be appended to the end of the boot image,
+  and its size is zero-padded to 16KiB.
+
+```
+  +---------------------+
+  | boot header         | 4096 bytes
+  +---------------------+
+  | kernel              | m pages
+  +---------------------+
+  | ramdisk             | n pages
+  +---------------------+
+  | * boot signature    | 16384 (16K) bytes
+  +---------------------+
+```
+
+### Retrofit to boot image V2
+
+* The `kernel` of the DEST image must be from the SOURCE `boot.img`.
+* The `ramdisk` of the DEST image must be from the SOURCE `vendor_boot.img` and
+  `init_boot.img`. The DEST `ramdisk` is the ramdisk concatenation of the vendor
+  ramdisk and generic ramdisk.
+* The `recovery dtbo / acpio` must be empty.
+* The `dtb` of the DEST image must be from the SOURCE `vendor_boot.img`.
+* The `boot_signature` section must be appended to the end of the boot image,
+  and its size is zero-padded to 16KiB.
+
+```
+  +---------------------+
+  | boot header         | 1 page
+  +---------------------+
+  | kernel              | n pages
+  +---------------------+
+  | * vendor ramdisk    |
+  |  +generic ramdisk   | m pages
+  +---------------------+
+  | second stage        | o pages
+  +---------------------+
+  | recovery dtbo/acpio | 0 byte
+  +---------------------+
+  | dtb                 | q pages
+  +---------------------+
+  | * boot signature    | 16384 (16K) bytes
+  +---------------------+
+```
diff --git a/gki/boot_signature_info.sh b/gki/boot_signature_info.sh
new file mode 100755
index 0000000..67ea0c9
--- /dev/null
+++ b/gki/boot_signature_info.sh
@@ -0,0 +1,97 @@
+#!/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.
+#
+
+#
+# Dump boot signature info of a GKI boot image.
+#
+
+set -eo errtrace
+
+die() {
+  echo >&2 "ERROR:" "${@}"
+  exit 1
+}
+
+TEMP_DIR="$(mktemp -d)"
+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
+
+get_arg() {
+  local arg="$1"
+  shift
+  while [[ "$#" -gt 0 ]]; do
+    if [[ "$1" == "${arg}" ]]; then
+      shift
+      echo "$1"
+      return
+    fi
+    shift
+  done
+}
+
+readonly VBMETA_IMAGE="${TEMP_DIR}/boot.boot_signature"
+readonly VBMETA_IMAGE_TEMP="${VBMETA_IMAGE}.temp"
+readonly VBMETA_INFO="${VBMETA_IMAGE}.info"
+readonly BOOT_IMAGE="${TEMP_DIR}/boot.img"
+readonly BOOT_IMAGE_DIR="${TEMP_DIR}/boot.unpack_dir"
+readonly BOOT_IMAGE_ARGS="${TEMP_DIR}/boot.mkbootimg_args"
+
+[[ -f "$1" ]] ||
+  die "expected one input image"
+cp "$1" "${BOOT_IMAGE}"
+
+# This could fail if there already is no AVB footer.
+avbtool erase_footer --image "${BOOT_IMAGE}" 2>/dev/null ||:
+
+unpack_bootimg --boot_img "${BOOT_IMAGE}" --out "${BOOT_IMAGE_DIR}" \
+  --format=mkbootimg -0 > "${BOOT_IMAGE_ARGS}"
+
+declare -a boot_args=()
+while IFS= read -r -d '' ARG; do
+  boot_args+=("${ARG}")
+done < "${BOOT_IMAGE_ARGS}"
+
+BOOT_IMAGE_VERSION="$(get_arg --header_version "${boot_args[@]}")"
+if [[ "${BOOT_IMAGE_VERSION}" -ge 4 ]]; then
+  cp "${BOOT_IMAGE_DIR}/boot_signature" "${VBMETA_IMAGE}"
+else
+  tail -c "$(( 16 << 10 ))" "${BOOT_IMAGE}" > "${VBMETA_IMAGE}"
+fi
+
+# Keep carving out vbmeta image from the boot signature until we fail or EOF.
+# Failing is fine because there could be padding trailing the boot signature.
+while avbtool info_image --image "${VBMETA_IMAGE}" --output "${VBMETA_INFO}" 2>/dev/null; do
+  cat "${VBMETA_INFO}"
+  echo
+
+  declare -i H A X
+  H="$(cat "${VBMETA_INFO}" | grep 'Header Block:' | awk '{print $3}')"
+  A="$(cat "${VBMETA_INFO}" | grep 'Authentication Block:' | awk '{print $3}')"
+  X="$(cat "${VBMETA_INFO}" | grep 'Auxiliary Block:' | awk '{print $3}')"
+  vbmeta_size="$(( ${H} + ${A} + ${X} ))"
+
+  tail -c "+$(( ${vbmeta_size} + 1 ))" "${VBMETA_IMAGE}" > "${VBMETA_IMAGE_TEMP}"
+  cp "${VBMETA_IMAGE_TEMP}" "${VBMETA_IMAGE}"
+done
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"
diff --git a/unpack_bootimg.py b/unpack_bootimg.py
index ae59429..2357e70 100755
--- a/unpack_bootimg.py
+++ b/unpack_bootimg.py
@@ -407,6 +407,9 @@
     ramdisk_offset_base = page_size * num_boot_header_pages
     image_info_list = []
 
+    image_info_list.append(
+        (ramdisk_offset_base, info.vendor_ramdisk_size, 'vendor_ramdisk'))
+
     if info.header_version > 3:
         info.vendor_ramdisk_table_size = unpack('I', args.boot_img.read(4))[0]
         vendor_ramdisk_table_entry_num = unpack('I', args.boot_img.read(4))[0]
@@ -449,9 +452,6 @@
             + num_vendor_ramdisk_table_pages)
         image_info_list.append((bootconfig_offset, info.vendor_bootconfig_size,
             'bootconfig'))
-    else:
-        image_info_list.append(
-            (ramdisk_offset_base, info.vendor_ramdisk_size, 'vendor_ramdisk'))
 
     dtb_offset = page_size * (num_boot_header_pages + num_boot_ramdisk_pages
                              ) # header + vendor_ramdisk