summaryrefslogtreecommitdiff
path: root/scripts/image_signing/ensure_amd_psp_flags.sh
blob: 0a18c14ec96f7d4ebb7738941209b7f1ad8e966d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#!/bin/bash
# Copyright 2022 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Load common constants and variables.
. "$(dirname "$0")/common.sh"

# Abort on error and uninitialized variables.
set -eu

declare -A -r REQUIRED_BIT_MASKS=(
  # Bit 58 - PSP_S0I3_RESUME_VERSTAGE - Run PSP verstage during S0i3 resume.
  # Checks that FW images have not been tampered with when exiting S0i3.
  [guybrush]="$((1 << 58))"
  [zork]="0x0"
)

declare -A -r FORBIDDEN_BIT_MASKS=(
  [guybrush]="0x0"
  [zork]="0x0"
)

# Grunt uses an old firmware format that amdfwread cannot read.
# See b/233787191 for skyrim.
BOARD_IGNORE_LIST=(grunt skyrim)

usage() {
  echo "$0: Validate AMD PSP soft-fuse flags contained in a ChromeOS image." \
    "These flags can have security implications and control debug features."
  echo "Usage $0 <image> <board>"
}

main() {
  if [[ $# -ne 2 ]]; then
    usage
    exit 1
  fi

  local image="$1"
  local board="$2"

  # Check the ignore list.
  if [[ " ${BOARD_IGNORE_LIST[*]} " == *" ${board} "* ]]; then
   echo "Skipping ignore-listed board ${board}"
   exit 0
  fi

  # Mount the image.
  local loopdev rootfs
  if [[ -d "${image}" ]]; then
    rootfs="${image}"
  else
    rootfs="$(make_temp_dir)"
    loopdev="$(loopback_partscan "${image}")"
    mount_loop_image_partition_ro "${loopdev}" 3 "${rootfs}"
  fi

  local firmware_bundle shellball_dir
  firmware_bundle="${rootfs}/usr/sbin/chromeos-firmwareupdate"
  shellball_dir="$(make_temp_dir)"

  # Extract our firmware.
  if ! extract_firmware_bundle "${firmware_bundle}" "${shellball_dir}"; then
    die "Failed to extract firmware bundle"
  fi

  # Find our images.
  declare -a images
  readarray -t images < <(find "${shellball_dir}" -iname 'bios-*')

  # Validate that all our AP FW images are AMD images.
  local image
  for image in "${images[@]}"; do
    # With no args, amdfwread will just attempt to validate the FW header.
    # On non-AMD FW this will fail, allowing us to skip non-AMD FW images.
    if ! amdfwread "${image}" &> /dev/null; then
      if [[ ! -v "REQUIRED_BIT_MASKS[${board}]" &&
            ! -v "FORBIDDEN_BIT_MASKS[${board}]" ]]; then
        # If we have an invalid FW image and don't have bitsets for this board
        # then this isn't an AMD board, exit successfully.
        exit 0
      else
        die "Found invalid AMD AP FW image"
      fi
    fi
  done

  # Get the board specific bit masks.
  local required_bit_mask forbidden_bit_mask

  if [[ ! -v "REQUIRED_BIT_MASKS[${board}]" ]]; then
    die "Missing PSP required bit mask set for ${board}"
  fi

  if [[ ! -v "FORBIDDEN_BIT_MASKS[${board}]" ]]; then
    die "Missing PSP forbidden bit mask set for ${board}"
  fi

  required_bit_mask="${REQUIRED_BIT_MASKS[${board}]}"
  forbidden_bit_mask="${FORBIDDEN_BIT_MASKS[${board}]}"

  # Check the soft-fuse bits
  for image in "${images[@]}"; do
    local soft_fuse soft_fuse_output forbidden_set missing_set
    if ! soft_fuse_output="$(amdfwread --soft-fuse "${image}")"; then
      die "'amdfwread --soft-fuse ${image}' failed"
    fi

    # Output format from amdfwread is Soft-fuse:value, where value is in hex.
    soft_fuse="$(echo "${soft_fuse_output}" | \
      sed -E -n 's/Soft-fuse:(0[xX][0-9a-fA-F]+)/\1/p')"
    if [[ -z "${soft_fuse}" ]]; then
      die "Could not parse Soft-fuse value from output: '${soft_fuse_output}'"
    fi

    forbidden_set="$((soft_fuse & forbidden_bit_mask))"
    if [[ "${forbidden_set}" != 0 ]]; then
      local forbidden_hex
      forbidden_hex="$(printf %#x "${forbidden_set}")"
      die "${image}: Forbidden AMD PSP soft-fuse bits set: ${forbidden_hex}"
    fi

    missing_set="$((~soft_fuse & required_bit_mask))"
    if [[ "${missing_set}" != 0 ]]; then
      local missing_hex
      missing_hex="$(printf %#x "${missing_set}")"
      die "${image}: Required AMD PSP soft-fuse bits not set: ${missing_hex}"
    fi
  done
}
main "$@"