1#!/bin/bash 2# Copyright 2022 The ChromiumOS Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6# Load common constants and variables. 7. "$(dirname "$0")/common.sh" 8 9# Abort on error and uninitialized variables. 10set -eu 11 12declare -A -r REQUIRED_BIT_MASKS=( 13 # Bit 58 - PSP_S0I3_RESUME_VERSTAGE - Run PSP verstage during S0i3 resume. 14 # Checks that FW images have not been tampered with when exiting S0i3. 15 [guybrush]="$((1 << 58))" 16 [zork]="0x0" 17) 18 19declare -A -r FORBIDDEN_BIT_MASKS=( 20 [guybrush]="0x0" 21 [zork]="0x0" 22) 23 24# Grunt uses an old firmware format that amdfwread cannot read. 25# See b/233787191 for skyrim. 26BOARD_IGNORE_LIST=(grunt skyrim) 27 28usage() { 29 echo "$0: Validate AMD PSP soft-fuse flags contained in a ChromeOS image." \ 30 "These flags can have security implications and control debug features." 31 echo "Usage $0 <image> <board>" 32} 33 34main() { 35 if [[ $# -ne 2 ]]; then 36 usage 37 exit 1 38 fi 39 40 local image="$1" 41 local board="$2" 42 43 # Check the ignore list. 44 if [[ " ${BOARD_IGNORE_LIST[*]} " == *" ${board} "* ]]; then 45 echo "Skipping ignore-listed board ${board}" 46 exit 0 47 fi 48 49 # Mount the image. 50 local loopdev rootfs 51 if [[ -d "${image}" ]]; then 52 rootfs="${image}" 53 else 54 rootfs="$(make_temp_dir)" 55 loopdev="$(loopback_partscan "${image}")" 56 mount_loop_image_partition_ro "${loopdev}" 3 "${rootfs}" 57 fi 58 59 local firmware_bundle shellball_dir 60 firmware_bundle="${rootfs}/usr/sbin/chromeos-firmwareupdate" 61 shellball_dir="$(make_temp_dir)" 62 63 # Extract our firmware. 64 if ! extract_firmware_bundle "${firmware_bundle}" "${shellball_dir}"; then 65 die "Failed to extract firmware bundle" 66 fi 67 68 # Find our images. 69 declare -a images 70 readarray -t images < <(find "${shellball_dir}" -iname 'bios-*') 71 72 # Validate that all our AP FW images are AMD images. 73 local image 74 for image in "${images[@]}"; do 75 # With no args, amdfwread will just attempt to validate the FW header. 76 # On non-AMD FW this will fail, allowing us to skip non-AMD FW images. 77 if ! amdfwread "${image}" &> /dev/null; then 78 if [[ ! -v "REQUIRED_BIT_MASKS[${board}]" && 79 ! -v "FORBIDDEN_BIT_MASKS[${board}]" ]]; then 80 # If we have an invalid FW image and don't have bitsets for this board 81 # then this isn't an AMD board, exit successfully. 82 exit 0 83 else 84 die "Found invalid AMD AP FW image" 85 fi 86 fi 87 done 88 89 # Get the board specific bit masks. 90 local required_bit_mask forbidden_bit_mask 91 92 if [[ ! -v "REQUIRED_BIT_MASKS[${board}]" ]]; then 93 die "Missing PSP required bit mask set for ${board}" 94 fi 95 96 if [[ ! -v "FORBIDDEN_BIT_MASKS[${board}]" ]]; then 97 die "Missing PSP forbidden bit mask set for ${board}" 98 fi 99 100 required_bit_mask="${REQUIRED_BIT_MASKS[${board}]}" 101 forbidden_bit_mask="${FORBIDDEN_BIT_MASKS[${board}]}" 102 103 # Check the soft-fuse bits 104 for image in "${images[@]}"; do 105 local soft_fuse soft_fuse_output forbidden_set missing_set 106 if ! soft_fuse_output="$(amdfwread --soft-fuse "${image}")"; then 107 die "'amdfwread --soft-fuse ${image}' failed" 108 fi 109 110 # Output format from amdfwread is Soft-fuse:value, where value is in hex. 111 soft_fuse="$(echo "${soft_fuse_output}" | \ 112 sed -E -n 's/Soft-fuse:(0[xX][0-9a-fA-F]+)/\1/p')" 113 if [[ -z "${soft_fuse}" ]]; then 114 die "Could not parse Soft-fuse value from output: '${soft_fuse_output}'" 115 fi 116 117 forbidden_set="$((soft_fuse & forbidden_bit_mask))" 118 if [[ "${forbidden_set}" != 0 ]]; then 119 local forbidden_hex 120 forbidden_hex="$(printf %#x "${forbidden_set}")" 121 die "${image}: Forbidden AMD PSP soft-fuse bits set: ${forbidden_hex}" 122 fi 123 124 missing_set="$((~soft_fuse & required_bit_mask))" 125 if [[ "${missing_set}" != 0 ]]; then 126 local missing_hex 127 missing_hex="$(printf %#x "${missing_set}")" 128 die "${image}: Required AMD PSP soft-fuse bits not set: ${missing_hex}" 129 fi 130 done 131} 132main "$@" 133