blob: 63b16a382350b6d24fa1d01476589cce0b41bee3 [file] [edit]
#!/bin/bash -eux
# Copyright 2018 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
me=${0##*/}
TMP="$me.tmp"
# Test --sys_props (primitive test needed for future updating tests).
test_sys_props() {
! "${FUTILITY}" --debug update --sys_props "$*" 2>&1 |
sed -n 's/.*property\[\(.*\)].value = \(.*\)/\1,\2,/p' |
tr '\n' ' '
}
test "$(test_sys_props "1,2,3")" = "0,1, 1,2, 2,3, "
test "$(test_sys_props "1 2 3")" = "0,1, 1,2, 2,3, "
test "$(test_sys_props "1, 2,3 ")" = "0,1, 1,2, 2,3, "
test "$(test_sys_props " 1,, 2")" = "0,1, 2,2, "
test "$(test_sys_props " , 4,")" = "1,4, "
test_quirks() {
! "${FUTILITY}" --debug update --quirks "$*" 2>&1 |
sed -n 's/.*Set quirk \(.*\) to \(.*\)./\1,\2/p' |
tr '\n' ' '
}
test "$(test_quirks "enlarge_image")" = "enlarge_image,1 "
test "$(test_quirks "enlarge_image=2")" = "enlarge_image,2 "
test "$(test_quirks " enlarge_image, enlarge_image=2")" = \
"enlarge_image,1 enlarge_image,2 "
# Test data files
DATA_DIR="${SCRIPT_DIR}/futility/data"
GERALT_BIOS="${DATA_DIR}/bios_geralt_cbfs.bin"
EVE_BIOS="${DATA_DIR}/bios_eve.bin"
LINK_BIOS="${DATA_DIR}/bios_link_mp.bin"
TRULO_800_BIOS="${DATA_DIR}/bios_trulo.15217.800.bin"
TRULO_900_BIOS="${DATA_DIR}/bios_trulo.15217.900.bin"
RO_VPD_BLOB="${DATA_DIR}/ro_vpd.bin"
SIGNER_CONFIG="${DATA_DIR}/signer_config.csv"
IDENTITY_CSV="${DATA_DIR}/identity.csv"
# Work in scratch directory
cd "${OUTDIR}"
set -o pipefail
# Re-create the temp folders
TMP_FROM="${TMP}/from"
TMP_TO="${TMP}/to"
EXPECTED="${TMP}/expected"
rm -rf "${TMP}"
mkdir -p "${TMP_FROM}" "${TMP_TO}" "${EXPECTED}"
FROM_IMAGE="${TMP}/src.trulo.800"
TO_IMAGE="${TMP}/src.trulo.900"
cp -f "${TRULO_800_BIOS}" "${FROM_IMAGE}"
cp -f "${TRULO_900_BIOS}" "${TO_IMAGE}"
"${FUTILITY}" load_fmap "${FROM_IMAGE}" \
RO_VPD:"${RO_VPD_BLOB}" RW_VPD:"${RO_VPD_BLOB}"
LINK_MP_IMAGE="${TMP}/src.link"
cp -f "${LINK_BIOS}" "${LINK_MP_IMAGE}"
patch_file() {
local file="$1"
local section="$2"
local section_offset="$3"
local data="$4"
# NAME OFFSET SIZE
local fmap_info
local base
local offset
fmap_info="$("${FUTILITY}" dump_fmap -p "${file}" "${section}")"
base="$(echo "${fmap_info}" | sed 's/^[^ ]* //; s/ [^ ]*$//')"
offset=$((base + section_offset))
echo "offset: ${offset}"
printf "%b" "${data}" | dd of="${file}" bs=1 seek="${offset}" \
conv=notrunc
}
erase_file() {
local file="$1"
local size
size="$(stat -c %s "${file}")"
# Write 0xff (\377) bytes.
head -c "${size}" /dev/zero | LC_ALL=C tr '\000' '\377' > "${file}"
}
get_section() {
local file="$1"
local section="$2"
local section_offset="$3"
local fmap_info
local base
local offset
local size
fmap_info="$("${FUTILITY}" dump_fmap -p "${file}" "${section}")"
base="$(echo "${fmap_info}" | sed 's/^[^ ]* //; s/ [^ ]*$//')"
size="$(echo "${fmap_info}" | sed 's/^[^ ]* //; s/^[^ ]* //')"
offset=$((base + section_offset))
dd if="${file}" bs=1 skip="${offset}" count="${size}"
}
unpack_image() {
local folder="${TMP}/$1"
local image="$2"
mkdir -p "${folder}"
(cd "${folder}" && "${FUTILITY}" dump_fmap -x "../../${image}")
"${FUTILITY}" gbb -g --rootkey="${folder}/rootkey" "${image}"
}
change_key() {
local image="$1"
local unpack_dir="$2"
"${FUTILITY}" gbb -s --rootkey="${unpack_dir}/rootkey" "${image}"
"${FUTILITY}" load_fmap "${image}" \
"VBLOCK_A:${unpack_dir}/VBLOCK_A" \
"VBLOCK_B:${unpack_dir}/VBLOCK_B"
}
# Unpack images so we can prepare expected results by individual sections.
unpack_image "from" "${FROM_IMAGE}"
unpack_image "to" "${TO_IMAGE}"
unpack_image "link_mp" "${LINK_MP_IMAGE}"
# Hack FROM_IMAGE so it has same root key as TO_IMAGE (for RW update).
FROM_DIFFERENT_ROOTKEY_IMAGE="${FROM_IMAGE}.diff_rootkey"
cp -f "${FROM_IMAGE}" "${FROM_DIFFERENT_ROOTKEY_IMAGE}"
"${FUTILITY}" gbb -s --rootkey="${TMP}/link_mp/rootkey" "${FROM_DIFFERENT_ROOTKEY_IMAGE}"
# Hack for quirks
FROM_IMAGE_SIZE="$(stat -c %s "${FROM_IMAGE}")"
cp -f "${FROM_IMAGE}" "${FROM_IMAGE}.large"
truncate -s "$(("${FROM_IMAGE_SIZE}" * 2))" "${FROM_IMAGE}.large"
# Create the FROM_SAME_RO_IMAGE using the RO from TO_IMAGE."
FROM_SAME_RO_IMAGE="${FROM_IMAGE}.same_ro"
cp -f "${FROM_IMAGE}" "${FROM_SAME_RO_IMAGE}"
"${FUTILITY}" load_fmap "${FROM_SAME_RO_IMAGE}" \
"RO_SECTION:${TMP_TO}/RO_SECTION"
# Create FROM_INCOMPAT_PLATFORM_IMAGE.
FROM_INCOMPAT_PLATFORM_IMAGE="${FROM_IMAGE}.incompat_plat"
cp -f "${FROM_IMAGE}" "${FROM_INCOMPAT_PLATFORM_IMAGE}"
INCOMPAT_FWID="Google_Incompatible.11111.222.0"
patch_file "${FROM_INCOMPAT_PLATFORM_IMAGE}" RO_FRID 0x0 \
"${INCOMPAT_FWID}"
# Create TO_INCOMPAT_FMAP_IMAGE.
TO_INCOMPAT_FMAP_IMAGE="${TO_IMAGE}.incompat_fmap"
cp -f "${GERALT_BIOS}" "${TO_INCOMPAT_FMAP_IMAGE}"
"${FUTILITY}" load_fmap "${TO_INCOMPAT_FMAP_IMAGE}" \
"RO_FRID:${TMP_FROM}/RO_FRID"
# Generate expected results.
cp -f "${TO_IMAGE}" "${EXPECTED}/full"
cp -f "${FROM_IMAGE}" "${EXPECTED}/rw"
cp -f "${FROM_IMAGE}" "${EXPECTED}/a"
cp -f "${FROM_IMAGE}" "${EXPECTED}/b"
cp -f "${FROM_SAME_RO_IMAGE}" "${EXPECTED}/FROM_SAME_RO_IMAGE.b"
cp -f "${FROM_IMAGE}" "${EXPECTED}/legacy"
"${FUTILITY}" load_fmap "${EXPECTED}/full" \
"RW_VPD:${TMP_FROM}/RW_VPD" \
"RO_VPD:${TMP_FROM}/RO_VPD"
"${FUTILITY}" load_fmap "${EXPECTED}/rw" \
"RW_SECTION_A:${TMP_TO}/RW_SECTION_A" \
"RW_SECTION_B:${TMP_TO}/RW_SECTION_B" \
"RW_SHARED:${TMP_TO}/RW_SHARED" \
"RW_LEGACY:${TMP_TO}/RW_LEGACY"
"${FUTILITY}" load_fmap "${EXPECTED}/a" \
"RW_SECTION_A:${TMP_TO}/RW_SECTION_A" \
"RW_LEGACY:${TMP_TO}/RW_LEGACY"
"${FUTILITY}" load_fmap "${EXPECTED}/b" \
"RW_SECTION_B:${TMP_TO}/RW_SECTION_B" \
"RW_LEGACY:${TMP_TO}/RW_LEGACY"
"${FUTILITY}" load_fmap "${EXPECTED}/FROM_SAME_RO_IMAGE.b" \
"RW_SECTION_B:${TMP_TO}/RW_SECTION_B" \
"RW_LEGACY:${TMP_TO}/RW_LEGACY"
"${FUTILITY}" load_fmap "${EXPECTED}/legacy" \
"RW_LEGACY:${TMP_TO}/RW_LEGACY"
cp -f "${EXPECTED}/full" "${EXPECTED}/full.gbb0"
"${FUTILITY}" gbb -s --flags=0 "${EXPECTED}/full.gbb0"
cp -f "${FROM_IMAGE}" "${FROM_IMAGE}.gbb0"
"${FUTILITY}" gbb -s --flags=0 "${FROM_IMAGE}.gbb0"
cp -f "${EXPECTED}/full" "${EXPECTED}/full.gbb0x27"
"${FUTILITY}" gbb -s --flags=0x27 "${EXPECTED}/full.gbb0x27"
cp -f "${EXPECTED}/full" "${EXPECTED}/large"
dd if=/dev/zero bs="${FROM_IMAGE_SIZE}" count=1 | tr '\000' '\377' >>"${EXPECTED}/large"
# A special image that doesn't preserve RW_VPD (FMAP_AREA_PRESERVE=0x08).
# RW_VPD FmapAreaHeader is at FMAP offset 0x302, so area_flags is at 0x32a.
TO_IMAGE_WIPE_RW_VPD="${TO_IMAGE}.wipe_rw_vpd"
cp -f "${TO_IMAGE}" "${TO_IMAGE_WIPE_RW_VPD}"
patch_file "${TO_IMAGE_WIPE_RW_VPD}" FMAP 0x32a "\x00"
cp -f "${EXPECTED}/full" "${EXPECTED}/full.empty_rw_vpd"
"${FUTILITY}" load_fmap "${EXPECTED}/full.empty_rw_vpd" \
RW_VPD:"${TMP_TO}/RW_VPD"
patch_file "${EXPECTED}/full.empty_rw_vpd" FMAP 0x32a "\x00"
# Special "from" image with different ME.
FROM_DIFFERENT_ME_IMAGE="${FROM_IMAGE}.diff_me"
cp -f "${FROM_IMAGE}" "${FROM_DIFFERENT_ME_IMAGE}"
patch_file "${FROM_DIFFERENT_ME_IMAGE}" SI_ME 0 "corrupted"
cp -f "${EXPECTED}/full" "${EXPECTED}/me_preserved"
patch_file "${EXPECTED}/me_preserved" SI_ME 0 "corrupted"
# Special "to" image with changed fwid to match "from" image
FROM_IMAGE_FWID_RW_A="$( get_section "${FROM_IMAGE}" RW_FWID_A 0 | sed 's/\x00/\\0/g' )"
FROM_IMAGE_FWID_RO="$( get_section "${FROM_IMAGE}" RO_FRID 0 | sed 's/\x00/\\0/g' )"
TO_IMAGE_SAME_FWID="${TO_IMAGE}.diff_rw_fwid"
cp -f "${TO_IMAGE}" "${TO_IMAGE_SAME_FWID}"
patch_file "${TO_IMAGE_SAME_FWID}" RW_FWID_A 0 "${FROM_IMAGE_FWID_RW_A}"
patch_file "${TO_IMAGE_SAME_FWID}" RW_FWID_B 0 "${FROM_IMAGE_FWID_RW_A}"
patch_file "${TO_IMAGE_SAME_FWID}" RO_FRID 0 "${FROM_IMAGE_FWID_RO}"
# Has 3 modes:
# 1. $3 = "!something", run command, expect failure,
# grep for something in log, fail if it is not present
# 2. $3 = "something", run command, expect success,
# cmp output to file named $3, fail if they are not the same
# 3. $3 = "!", run command, expect success, fail to find a file named !
test_update() {
local test_name="$1"
local emu_src="$2"
local expected="$3"
local error_msg="${expected#!}"
local emu="${TMP}/emu"
local msg
shift 3
cp -f "${emu_src}" "${emu}"
echo "*** Test Item: ${test_name}"
if [ "${error_msg}" != "${expected}" ] && [ -n "${error_msg}" ]; then
msg="$(! "${FUTILITY}" update --emulate "${emu}" "$@" 2>&1)"
grep -qF -- "${error_msg}" <<<"${msg}"
else
"${FUTILITY}" update --emulate "${emu}" "$@"
cmp "${emu}" "${expected}"
fi
}
# --sys_props: mainfw_act, tpm_fwver, platform_ver, [wp_hw, wp_sw]
# tpm_fwver = <data key version:16><firmware version:16>.
# TO_IMAGE is signed with data key version = 1, firmware version = 4 => 0x10004.
# Test Full update.
test_update "Full update" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-i "${TO_IMAGE}" --wp=0
test_update "Full update (check fwid)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-i "${TO_IMAGE}" --wp=0 --check-fwid
test_update "Full update (check fwid, same fwid)" \
"${FROM_IMAGE}" "${FROM_IMAGE}" \
-i "${TO_IMAGE_SAME_FWID}" --wp=0 --check-fwid
test_update "Full update (incompatible platform)" \
"${FROM_INCOMPAT_PLATFORM_IMAGE}" "!platform is not compatible" \
-i "${TO_IMAGE}" --wp=0
test_update "Full update (--quirks no_check_platform)" \
"${FROM_INCOMPAT_PLATFORM_IMAGE}" "${EXPECTED}/full" \
-i "${TO_IMAGE}" --wp=0 \
--quirks no_check_platform
test_update "Full update (TPM Anti-rollback: data key)" \
"${FROM_IMAGE}" "!Data key version rollback detected" \
-i "${TO_IMAGE}" --wp=0 --sys_props 1,0x20001
test_update "Full update (TPM Anti-rollback: kernel key)" \
"${FROM_IMAGE}" "!Firmware version rollback detected" \
-i "${TO_IMAGE}" --wp=0 --sys_props 1,0x10005
test_update "Full update (TPM Anti-rollback: 0 as tpm_fwver)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-i "${TO_IMAGE}" --wp=0 --sys_props ,0x0
test_update "Full update (TPM check failure due to invalid tpm_fwver)" \
"${FROM_IMAGE}" "!Invalid tpm_fwver: -1" \
-i "${TO_IMAGE}" --wp=0 --sys_props ,-1
test_update "Full update (Skip TPM check with --force)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-i "${TO_IMAGE}" --wp=0 --sys_props ,-1 --force
test_update "Full update (from stdin)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-i - --wp=0 --sys_props ,-1 --force <"${TO_IMAGE}"
test_update "Full update (GBB=0 -> 0)" \
"${FROM_IMAGE}.gbb0" "${EXPECTED}/full.gbb0" \
-i "${TO_IMAGE}" --wp=0
test_update "Full update (GBB flags -> 0x27)" \
"${FROM_IMAGE}" "${EXPECTED}/full.gbb0x27" \
-i "${TO_IMAGE}" --gbb_flags=0x27 --wp=0
test_update "Full update (--host_only)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-i "${TO_IMAGE}" --wp=0 --host_only --ec_image non-exist.bin
test_update "Full update (Preserve VPD using FMAP_AREA_PRESERVE)" \
"${FROM_IMAGE}" "${EXPECTED}/full.empty_rw_vpd" \
-i "${TO_IMAGE_WIPE_RW_VPD}" --wp=0
# Test RW-only update.
test_update "RW update" \
"${FROM_IMAGE}" "${EXPECTED}/rw" \
-i "${TO_IMAGE}" --wp=1
test_update "RW update (check fwid)" \
"${FROM_IMAGE}" "${EXPECTED}/rw" \
-i "${TO_IMAGE}" --wp=1 --check-fwid
test_update "RW update (check fwid, same fwid)" \
"${FROM_IMAGE}" "${FROM_IMAGE}" \
-i "${TO_IMAGE_SAME_FWID}" --wp=1 --check-fwid
test_update "RW update (incompatible platform)" \
"${FROM_INCOMPAT_PLATFORM_IMAGE}" "!platform is not compatible" \
-i "${TO_IMAGE}" --wp=1
test_update "RW update (incompatible FMAP)" \
"${FROM_IMAGE}" "!New image's FMAP is incompatible with system image" \
-i "${TO_INCOMPAT_FMAP_IMAGE}" --wp=1
test_update "RW update (incompatible rootkey)" \
"${FROM_DIFFERENT_ROOTKEY_IMAGE}" "!RW signed by incompatible root key" \
-i "${TO_IMAGE}" --wp=1
test_update "RW update (TPM Anti-rollback: data key)" \
"${FROM_IMAGE}" "!Data key version rollback detected" \
-i "${TO_IMAGE}" --wp=1 --sys_props 1,0x20001
test_update "RW update (TPM Anti-rollback: kernel key)" \
"${FROM_IMAGE}" "!Firmware version rollback detected" \
-i "${TO_IMAGE}" --wp=1 --sys_props 1,0x10005
# Test Try-RW update (vboot2).
test_update "RW update (A->B)" \
"${FROM_IMAGE}" "${EXPECTED}/b" \
-i "${TO_IMAGE}" -t --wp=1 --sys_props 0
test_update "RW update (B->A)" \
"${FROM_IMAGE}" "${EXPECTED}/a" \
-i "${TO_IMAGE}" -t --wp=1 --sys_props 1
test_update "RW update, same RO, wp=0 (A->B)" \
"${FROM_SAME_RO_IMAGE}" "${EXPECTED}/FROM_SAME_RO_IMAGE.b" \
-i "${TO_IMAGE}" -t --wp=0 --sys_props 0
test_update "RW update, same RO, wp=1 (A->B)" \
"${FROM_SAME_RO_IMAGE}" "${EXPECTED}/FROM_SAME_RO_IMAGE.b" \
-i "${TO_IMAGE}" -t --wp=1 --sys_props 0
test_update "RW update -> fallback to RO+RW Full update" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-i "${TO_IMAGE}" -t --wp=0 --sys_props 1,0x10001
test_update "RW update (incompatible platform)" \
"${FROM_INCOMPAT_PLATFORM_IMAGE}" "!platform is not compatible" \
-i "${TO_IMAGE}" -t --wp=1
test_update "RW update (incompatible FMAP)" \
"${FROM_IMAGE}" "!New image's FMAP is incompatible with system image" \
-i "${TO_INCOMPAT_FMAP_IMAGE}" -t --wp=1
test_update "RW update (incompatible rootkey)" \
"${FROM_DIFFERENT_ROOTKEY_IMAGE}" "!RW signed by incompatible root key" \
-i "${TO_IMAGE}" -t --wp=1
test_update "RW update (TPM Anti-rollback: data key)" \
"${FROM_IMAGE}" "!Data key version rollback detected" \
-i "${TO_IMAGE}" -t --wp=1 --sys_props 1,0x20001
test_update "RW update (TPM Anti-rollback: kernel key)" \
"${FROM_IMAGE}" "!Firmware version rollback detected" \
-i "${TO_IMAGE}" -t --wp=1 --sys_props 1,0x10005
test_update "RW update -> fallback to RO+RW Full update (TPM Anti-rollback)" \
"${FROM_IMAGE}" "!Firmware version rollback detected" \
-i "${TO_IMAGE}" -t --wp=0 --sys_props 1,0x10005
# Test 'factory mode'
test_update "Factory mode update (WP=0)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-i "${TO_IMAGE}" --wp=0 --mode=factory
test_update "Factory mode update (WP=0)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
--factory -i "${TO_IMAGE}" --wp=0
test_update "Factory mode update (WP=1)" \
"${FROM_IMAGE}" "!remove write protection for factory mode" \
-i "${TO_IMAGE}" --wp=1 --mode=factory
test_update "Factory mode update (WP=1)" \
"${FROM_IMAGE}" "!remove write protection for factory mode" \
--factory -i "${TO_IMAGE}" --wp=1
test_update "Factory mode update (GBB=0 -> 0x39)" \
"${FROM_IMAGE}.gbb0" "${EXPECTED}/full" \
--factory -i "${TO_IMAGE}" --wp=0
# Test legacy update
test_update "Legacy update" \
"${FROM_IMAGE}" "${EXPECTED}/legacy" \
-i "${TO_IMAGE}" --mode=legacy
# Test quirks
test_update "Full update (--quirks enlarge_image)" \
"${FROM_IMAGE}.large" "${EXPECTED}/large" --quirks enlarge_image \
-i "${TO_IMAGE}" --wp=0
test_update "Full update (multi-line --quirks enlarge_image)" \
"${FROM_IMAGE}.large" "${EXPECTED}/large" --quirks '
enlarge_image
' -i "${TO_IMAGE}" --wp=0
test_update "Full update (failure by --quirks min_platform_version)" \
"${FROM_IMAGE}" "!Need platform version >= 3 (current is 2)" \
--quirks min_platform_version=3 \
-i "${TO_IMAGE}" --wp=0 --sys_props ,,2
test_update "Full update (--quirks min_platform_version)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
--quirks min_platform_version=3 \
-i "${TO_IMAGE}" --wp=0 --sys_props ,,3
test_update "Full update (--quirks preserve_me with non-host programmer)" \
"${FROM_DIFFERENT_ME_IMAGE}" "${EXPECTED}/full" \
--quirks preserve_me \
-i "${TO_IMAGE}" --wp=0 \
-p raiden_debug_spi:target=AP
test_update "Full update (--quirks preserve_me)" \
"${FROM_DIFFERENT_ME_IMAGE}" "${EXPECTED}/full" \
--quirks preserve_me \
-i "${TO_IMAGE}" --wp=0
test_update "Full update (--quirks preserve_me, autoupdate)" \
"${FROM_DIFFERENT_ME_IMAGE}" "${EXPECTED}/me_preserved" \
--quirks preserve_me -m autoupdate \
-i "${TO_IMAGE}" --wp=0
test_update "Full update (--quirks preserve_me, deferupdate_hold)" \
"${FROM_DIFFERENT_ME_IMAGE}" "${EXPECTED}/me_preserved" \
--quirks preserve_me -m deferupdate_hold \
-i "${TO_IMAGE}" --wp=0
test_update "Full update (--quirks preserve_me, factory)" \
"${FROM_DIFFERENT_ME_IMAGE}" "${EXPECTED}/full" \
--quirks preserve_me -m factory \
-i "${TO_IMAGE}" --wp=0
# Test manifest.
TMP_JSON_OUT="${TMP}/json.out"
echo "TEST: Manifest (--manifest, --image)"
cp -f "${GERALT_BIOS}" "${TMP}/image.bin"
(cd "${TMP}" &&
"${FUTILITY}" update -i image.bin --manifest) >"${TMP_JSON_OUT}"
cmp \
<(jq -S <"${TMP_JSON_OUT}") \
<(jq -S <"${SCRIPT_DIR}/futility/bios_geralt_cbfs.manifest.json")
TMP_PARSEABLE_OUT="${TMP}/manifest.parseable"
echo "TEST: Manifest parseable (--parseable-manifest, --image)"
(cd "${TMP}" &&
"${FUTILITY}" update -i image.bin --parseable-manifest) >"${TMP_PARSEABLE_OUT}"
cmp \
<(sort "${TMP_PARSEABLE_OUT}") \
<(sort "${SCRIPT_DIR}/futility/bios_geralt_cbfs.manifest.parseable")
# Test archive and manifest. CL_TAG is for custom_label_tag.
A="${TMP}/archive"
mkdir -p "${A}/bin"
echo 'echo "${CL_TAG}"' >"${A}/bin/vpd"
chmod +x "${A}/bin/vpd"
cp -f "${LINK_BIOS}" "${A}/bios.bin"
echo "TEST: Manifest (--manifest, -a, bios.bin)"
"${FUTILITY}" update -a "${A}" --manifest >"${TMP_JSON_OUT}"
cmp \
<(jq -S <"${TMP_JSON_OUT}") \
<(jq -S <"${SCRIPT_DIR}/futility/link_bios.manifest.json")
echo "TEST: Manifest parseable (--parseable-manifest, -a, bios.bin)"
"${FUTILITY}" update -a "${A}" --parseable-manifest >"${TMP_PARSEABLE_OUT}"
diff -u \
<(sort "${TMP_PARSEABLE_OUT}") \
<(sort "${SCRIPT_DIR}/futility/link_bios.manifest.parseable")
mv -f "${A}/bios.bin" "${A}/image.bin"
echo "TEST: Manifest (--manifest, -a, image.bin)"
"${FUTILITY}" update -a "${A}" --manifest >"${TMP_JSON_OUT}"
cmp \
<(jq -S <"${TMP_JSON_OUT}") \
<(jq -S <"${SCRIPT_DIR}/futility/link_image.manifest.json")
echo "TEST: Manifest parseable (--parseable-manifest, -a, image.bin)"
"${FUTILITY}" update -a "${A}" --parseable-manifest >"${TMP_PARSEABLE_OUT}"
diff -u \
<(sort "${TMP_PARSEABLE_OUT}") \
<(sort "${SCRIPT_DIR}/futility/link_image.manifest.parseable")
cp -f "${TO_IMAGE}" "${A}/image.bin"
test_update "Full update (--archive, single package)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-a "${A}" --wp=0 --sys_props ,,3
echo "TEST: Output (--archive, --mode=output)"
TMP_OUTPUT="${TMP}/out_archive" && mkdir -p "${TMP_OUTPUT}"
"${FUTILITY}" update -a "${A}" --mode=output \
--output_dir="${TMP_OUTPUT}"
cmp "${TMP_OUTPUT}/image.bin" "${TO_IMAGE}"
# Test Unified Build archives.
FROM_MP_IMAGE="${FROM_IMAGE}.mp"
cp -f "${FROM_IMAGE}" "${FROM_MP_IMAGE}"
change_key "${FROM_MP_IMAGE}" "${TMP}/link_mp"
TO_MP_IMAGE="${TO_IMAGE}.mp"
cp -f "${TO_IMAGE}" "${TO_MP_IMAGE}"
change_key "${TO_MP_IMAGE}" "${TMP}/link_mp"
cp -f "${EXPECTED}/full" "${EXPECTED}/full_mp"
change_key "${EXPECTED}/full_mp" "${TMP}/link_mp"
mkdir -p "${A}/keyset" "${A}/images"
cp -f "${SIGNER_CONFIG}" "${A}/"
rm -f "${A}/*.bin"
cp -f "${LINK_BIOS}" "${A}/images/bios_link.bin"
cp -f "${TRULO_900_BIOS}" "${A}/images/bios_trulo_900.bin"
cp -f "${GERALT_BIOS}" "${A}/images/bios_geralt.bin"
# TRULO_900_BIOS is dev-signed. Generate rootkey/VBLOCK patches with MP key
# to simulate a different custom label key.
unpack_image "to_mp" "${TO_MP_IMAGE}"
cp -f "${TMP}/to_mp/rootkey" "${A}/keyset/rootkey.customtip-mp"
cp -f "${TMP}/to_mp/VBLOCK_A" "${A}/keyset/vblock_A.customtip-mp"
cp -f "${TMP}/to_mp/VBLOCK_B" "${A}/keyset/vblock_B.customtip-mp"
cp -f "${LINK_MP_IMAGE}" "${LINK_MP_IMAGE}.corrupted"
cp -f "${LINK_MP_IMAGE}" "${LINK_MP_IMAGE}.erased"
patch_file "${LINK_MP_IMAGE}.corrupted" FW_MAIN_A 0 "corrupted"
erase_file "${LINK_MP_IMAGE}.erased"
test_update "Full update (--archive, model=link)" \
"${LINK_MP_IMAGE}.corrupted" "${LINK_MP_IMAGE}" \
-a "${A}" --wp=0 --sys_props 0,0x10001,3 --model=link
test_update "Full update (--archive, model=unknown)" \
"${LINK_MP_IMAGE}.corrupted" "!Unsupported model: 'unknown'" \
-a "${A}" --wp=0 --sys_props 0,0x10001,3 --model=unknown
# Test archives with identity.csv
cp -f "${IDENTITY_CSV}" "${A}/"
LINK_SKU_ID=0x1000
test_update "Full update (--archive, identity.csv with SKU ${LINK_SKU_ID})" \
"${LINK_MP_IMAGE}.corrupted" "${LINK_MP_IMAGE}" \
-a "${A}" --wp=0 --sys_props "0,0x10001,3,,,,${LINK_SKU_ID}"
WRONG_SKU_ID="0x999"
test_update "Full update (--archive, identity.csv with wrong SKU)" \
"${LINK_MP_IMAGE}.corrupted" "!Failed to get device identity from identity.csv" \
-a "${A}" --wp=0 --sys_props "0,0x10001,3,,,,${WRONG_SKU_ID}"
test_update "Full update (--archive, identity.csv with --sku-id)" \
"${LINK_MP_IMAGE}.corrupted" "${LINK_MP_IMAGE}" \
-a "${A}" --wp=0 --sys_props "0,0x10001,3,,,,${WRONG_SKU_ID}" \
--sku-id "${LINK_SKU_ID}"
# Remotely flash over a completely erased system flash.
# The FRID matching is case-insensitive, so passing "google_link" is fine.
# `--programmer` is for simulating remote flashing (via servo).
# `--force` is required to ignore the system firmware parsing error.
test_update "Full update (--archive, remote, identity.csv with --frid/--sku-id)" \
"${LINK_MP_IMAGE}.erased" "${LINK_MP_IMAGE}" \
-a "${A}" --wp=0 --sys_props 0,0x10001,3 \
--programmer raiden_debug_spi:target=AP \
--frid "google_link" --sku-id "${LINK_SKU_ID}" --force
# Test --detect-model-only on a remote DUT.
echo "*** Test Item: Detect model (--archive, remote, --detect-model-only)"
"${FUTILITY}" update -a "${A}" \
--emulate "${LINK_MP_IMAGE}.corrupted" --programmer raiden_debug_spi:target=AP \
--detect-model-only >"${TMP}/model.out"
cmp "${TMP}/model.out" <(echo link)
cp -f "${LINK_MP_IMAGE}" "${LINK_MP_IMAGE}.incompat"
patch_file "${LINK_MP_IMAGE}.incompat" RO_FRID 0x0 \
"Google_Incompatible.11111.222.0"
test_update "Detect model (--archive, remote, --detect-model-only, unsupported FRID)" \
"${LINK_MP_IMAGE}.incompat" "!Unsupported model: 'Google_Incompatible'" \
-a "${A}" --wp=0 --sys_props 0,0x10001,3 \
--programmer raiden_debug_spi:target=AP \
--detect-model-only
test_update "Full update (--archive, custom label with tag specified)" \
"${FROM_MP_IMAGE}" "${EXPECTED}/full_mp" \
-a "${A}" --wp=0 --sys_props 0,0x10001,3 --model=customtip-mp
CL_TAG="mp" PATH="${A}/bin:${PATH}" \
test_update "Full update (--archive, custom label, fake VPD)" \
"${FROM_MP_IMAGE}" "${EXPECTED}/full_mp" \
-a "${A}" --wp=0 --sys_props 0,0x10001,3 --model=customtip
CL_TAG="bad" PATH="${A}/bin:${PATH}" \
test_update "Full update (--archive, custom label, wrong image)" \
"${FROM_MP_IMAGE}" "!The firmware image for custom label" \
-a "${A}" --wp=0 --sys_props 0,0x10001,3 --debug --model=customtip
# The output mode (without specifying signature id) for custom label would still
# need a source (emulate) image to decide the VPD, which is not a real use case.
echo "TEST: Output (--archive, --mode=output, custom label with tag specified)"
TMP_OUTPUT="${TMP}/out_custom_label" && mkdir -p "${TMP_OUTPUT}"
"${FUTILITY}" update -a "${A}" --mode=output \
--output_dir="${TMP_OUTPUT}" --model=customtip-mp
cmp "${TMP_OUTPUT}/image.bin" "${TO_MP_IMAGE}"
# Custom label + Unibuild with default keys as model name
cp -f "${TMP_TO}/rootkey" "${A}/keyset/rootkey.customtip"
cp -f "${TMP_TO}/VBLOCK_A" "${A}/keyset/vblock_A.customtip"
cp -f "${TMP_TO}/VBLOCK_B" "${A}/keyset/vblock_B.customtip"
test_update "Full update (--archive, custom label, no VPD, default keys)" \
"${FROM_IMAGE}" "${EXPECTED}/full" \
-a "${A}" --wp=0 --sys_props 0,0x10001,3 --model=customtip
# Test special programmer
test_flashrom() {
echo "TEST: Full update (dummy programmer)"
local emu="${TMP}/emu"
cp -f "${FROM_IMAGE}" "${emu}"
"${FUTILITY}" update --programmer \
dummy:emulate=VARIABLE_SIZE,image="${emu}",size="${FROM_IMAGE_SIZE}" \
-i "${TO_IMAGE}" --wp=0 --sys_props 0,0x10001,3 >&2
cmp "${emu}" "${EXPECTED}/full"
}
type flashrom >/dev/null 2>&1 && test_flashrom
test_cbfstool() {
echo "TEST: Update with cbsfstool"
local smm="${TMP}/smm"
local cbfs="${TMP}/cbfs"
local quirk="${TMP}/quirk"
local size
cp -f "${EVE_BIOS}" "${TMP_FROM}.eve.locked"
patch_file "${TMP_FROM}.eve.locked" SI_DESC 0x80 \
"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
cp -f "${EVE_BIOS}" "${EXPECTED}/eve.unlocked"
patch_file "${EXPECTED}/eve.unlocked" SI_DESC 0x80 \
"\x00\xff\xff\xff\x00\xff\xff\xff\x00\xff\xff\xff"
echo "SMM STORE" >"${smm}"
truncate -s 262144 "${smm}"
cbfstool "${TMP_FROM}.eve.locked" add -r RW_LEGACY -n "smm_store" \
-f "${smm}" -t raw
cbfstool "${EXPECTED}/eve.unlocked" add -r RW_LEGACY -n "smm_store" \
-f "${smm}" -t raw -b 0x1bf000
# Both unlock_csme_eve and eve_smm_store are implicitly enabled.
test_update "Full update (--quirks unlock_csme_eve)" \
"${TMP_FROM}.eve.locked" "${EXPECTED}/eve.unlocked" \
-i "${EVE_BIOS}" --wp=0 \
--quirks unlock_csme_eve
test_update "Legacy update (--quirks eve_smm_store)" \
"${TMP_FROM}.eve.locked" "${EXPECTED}/eve.unlocked" \
-i "${EVE_BIOS}" --wp=0 \
--quirks eve_smm_store
cp "${TMP_FROM}.eve.locked" "${TMP_FROM}.eve.large"
size="$(stat -c %s "${TMP_FROM}.eve.large")"
truncate -s "$(("${size}" * 2))" "${TMP_FROM}.eve.large"
test_update "Full update (wrong size)" \
"${TMP_FROM}.eve.large" "!Failed writing firmware" \
-i "${EVE_BIOS}" --wp=0 \
--quirks unlock_csme_eve,eve_smm_store
echo "min_platform_version=3" >"${quirk}"
cp -f "${TO_IMAGE}" "${TO_IMAGE}.quirk"
# Remove existing 'updater_quirks' if any.
cbfstool "${TO_IMAGE}.quirk" remove -r FW_MAIN_A -n updater_quirks || true
cbfstool "${TO_IMAGE}.quirk" add -r FW_MAIN_A -n updater_quirks \
-f "${quirk}" -t raw
test_update "Full update (failure by CBFS quirks)" \
"${FROM_IMAGE}" "!Need platform version >= 3 (current is 2)" \
-i "${TO_IMAGE}.quirk" --wp=0 --sys_props 0,0x10001,2
}
type cbfstool >/dev/null 2>&1 && test_cbfstool
# Add the given line to the config file in CBFS.
add_config() {
local image="$1"
local config_line="$2"
local config_file="${TMP}/config"
cbfstool "${image}" extract -n config -f "${config_file}"
echo "${config_line}" >>"${config_file}"
cbfstool "${image}" remove -n config
cbfstool "${image}" add -n config -f "${config_file}" -t raw
}
# Remove lines of the given pattern from the config file in CBFS.
remove_config() {
local image="$1"
local config_pattern="$2"
local config_file="${TMP}/config"
cbfstool "${image}" extract -n config -f "${config_file}"
sed -i "/${config_pattern}/d" "${config_file}"
cbfstool "${image}" remove -n config
cbfstool "${image}" add -n config -f "${config_file}" -t raw
}
lock_me() {
local image="$1"
ifdtool -p adl --lock "${image}" -O "${image}.tmp"
ifdtool -p adl --gpr0-enable "${image}.tmp" -O "${image}"
}
unlock_me() {
local image="$1"
ifdtool -p adl --unlock "${image}" -O "${image}.tmp"
ifdtool -p adl --gpr0-disable "${image}.tmp" -O "${image}"
}
test_ifdtool() {
cp -f "${FROM_IMAGE}" "${FROM_IMAGE}.locked"
lock_me "${FROM_IMAGE}.locked"
cp -f "${FROM_IMAGE}" "${FROM_IMAGE}.unlocked"
unlock_me "${FROM_IMAGE}.unlocked"
cp -f "${TO_IMAGE}" "${TO_IMAGE}.locked"
lock_me "${TO_IMAGE}.locked"
cp -f "${TO_IMAGE}" "${TO_IMAGE}.unlocked"
unlock_me "${TO_IMAGE}.unlocked"
cp -f "${EXPECTED}/full" "${EXPECTED}/full.locked"
lock_me "${EXPECTED}/full.locked"
cp -f "${EXPECTED}/rw" "${EXPECTED}/rw.locked"
lock_me "${EXPECTED}/rw.locked"
# Test 'AP RO locked with verification turned on'.
test_update "AP RO locked update (locked, SI_DESC is different)" \
"${FROM_IMAGE}.locked" "${EXPECTED}/rw.locked" \
-i "${TO_IMAGE}.unlocked" --wp=0 --debug
test_update "AP RO locked update (locked, SI_DESC is the same)" \
"${FROM_IMAGE}.locked" "${EXPECTED}/full.locked" \
-i "${TO_IMAGE}.locked" --wp=0 --debug
test_update "AP RO locked update (unlocked)" \
"${FROM_IMAGE}.unlocked" "${EXPECTED}/full" \
-i "${TO_IMAGE}" --wp=0 --debug
# Generate images for testing --unlock_me.
# There are two ways to detect the platform:
# 1. Read CONFIG_IFD_CHIPSET from config file in CBFS
# 2. Fallback for nissa: check if CONFIG_IFD_BIN_PATH contains 'nissa'
local config_ifd_chipset='CONFIG_IFD_CHIPSET="adl"'
local config_ifd_path=\
'CONFIG_IFD_BIN_PATH="3rdparty/blobs/mainboard/google/nissa/descriptor-craask.bin"'
cp -f "${TO_IMAGE}" "${TO_IMAGE}.ifd"
cp -f "${EXPECTED}/full" "${EXPECTED}/full.ifd"
remove_config "${TO_IMAGE}.ifd" "CONFIG_IFD_CHIPSET="
remove_config "${TO_IMAGE}.ifd" "CONFIG_IFD_BIN_PATH="
remove_config "${EXPECTED}/full.ifd" "CONFIG_IFD_CHIPSET="
remove_config "${EXPECTED}/full.ifd" "CONFIG_IFD_BIN_PATH="
cp -f "${TO_IMAGE}.ifd" "${TO_IMAGE}.ifd_chipset"
cp -f "${TO_IMAGE}.ifd" "${TO_IMAGE}.ifd_path"
cp -f "${EXPECTED}/full.ifd" "${EXPECTED}/ifd_chipset"
cp -f "${EXPECTED}/full.ifd" "${EXPECTED}/ifd_path"
add_config "${TO_IMAGE}.ifd_chipset" "${config_ifd_chipset}"
add_config "${TO_IMAGE}.ifd_path" "${config_ifd_path}"
add_config "${EXPECTED}/ifd_chipset" "${config_ifd_chipset}"
add_config "${EXPECTED}/ifd_path" "${config_ifd_path}"
cp -f "${EXPECTED}/ifd_chipset" "${EXPECTED}/me_unlocked.ifd_chipset"
cp -f "${EXPECTED}/ifd_path" "${EXPECTED}/me_unlocked.ifd_path"
unlock_me "${EXPECTED}/me_unlocked.ifd_chipset"
unlock_me "${EXPECTED}/me_unlocked.ifd_path"
test_update "Full update (--quirks unlock_csme, IFD chipset)" \
"${FROM_IMAGE}" "${EXPECTED}/me_unlocked.ifd_chipset" \
--quirks unlock_csme -i "${TO_IMAGE}.ifd_chipset" --wp=0
test_update "Full update (--quirks unlock_csme, IFD bin path)" \
"${FROM_IMAGE}" "${EXPECTED}/me_unlocked.ifd_path" \
--quirks unlock_csme -i "${TO_IMAGE}.ifd_path" --wp=0
test_update "Full update (--unlock_me)" \
"${FROM_IMAGE}" "${EXPECTED}/me_unlocked.ifd_chipset" \
--unlock_me -i "${TO_IMAGE}.ifd_chipset" --wp=0
echo "TEST: Output (--mode=output, --quirks unlock_csme)"
TMP_OUTPUT="${TMP}/out_csme" && mkdir -p "${TMP_OUTPUT}"
mkdir -p "${TMP_OUTPUT}"
"${FUTILITY}" update -i "${EXPECTED}/ifd_chipset" --mode=output \
--output_dir="${TMP_OUTPUT}" --quirks unlock_csme
cmp "${TMP_OUTPUT}/image.bin" "${EXPECTED}/me_unlocked.ifd_chipset"
}
type ifdtool >/dev/null 2>&1 && test_ifdtool
rm -rf "${TMP}"
exit 0