crosfirmware.sh
1 #!/usr/bin/env bash 2 # 3 # SPDX-License-Identifier: GPL-2.0-only 4 5 # On some systems, `parted` and `debugfs` are located in /sbin. 6 export PATH="$PATH:/sbin" 7 8 exit_if_uninstalled() { 9 local cmd_name="$1" 10 local deb_pkg_name="$2" 11 12 if type "$cmd_name" >/dev/null 2>&1; then 13 return 14 fi 15 16 printf '`%s` was not found. ' "$cmd_name" >&2 17 printf 'On Debian-based systems, it can be installed\n' >&2 18 printf 'by running `apt install %s`.\n' "$deb_pkg_name" >&2 19 20 exit 1 21 } 22 23 exit_if_dependencies_are_missing() { 24 exit_if_uninstalled "uudecode" "sharutils" 25 exit_if_uninstalled "debugfs" "e2fsprogs" 26 exit_if_uninstalled "parted" "parted" 27 exit_if_uninstalled "curl" "curl" 28 exit_if_uninstalled "unzip" "unzip" 29 } 30 31 get_inventory() { 32 _conf=$1 33 _url=https://dl.google.com/dl/edgedl/chromeos/recovery/recovery.conf 34 35 echo "Downloading recovery image inventory..." 36 37 curl -s "$_url" >$_conf 38 } 39 40 download_image() { 41 _url=$1 42 _file=$2 43 44 echo "Downloading recovery image" 45 curl "$_url" >"$_file.zip" 46 echo "Decompressing recovery image" 47 unzip -q "$_file.zip" 48 rm "$_file.zip" 49 } 50 51 extract_partition() { 52 NAME=$1 53 FILE=$2 54 ROOTFS=$3 55 _bs=1024 56 57 echo "Extracting ROOT-A partition" 58 ROOTP=$(printf "unit\nB\nprint\nquit\n" | 59 parted $FILE 2>/dev/null | grep $NAME) 60 61 if [ "$ROOTP" == "" ]; then 62 # Automatic extraction failed, likely due to parted detecting 63 # overlapping partitions. Fall back to using fdisk and assume 64 # ROOT-A is partition #3 65 echo "(Extracting via parted failed; falling back to fdisk)" 66 _ssize=$(printf "p q" | fdisk $FILE | grep "Sector size" | 67 cut -f2 -d: | cut -f2 -d ' ') 68 _start=$(printf "p q" | fdisk $FILE | grep "bin3" | tr -s ' ' | 69 cut -f2 -d ' ') 70 _nsec=$(printf "p q" | fdisk $FILE | grep "bin3" | tr -s ' ' | 71 cut -f4 -d ' ') 72 START=$(($_ssize * $_start)) 73 SIZE=$(($_ssize * $_nsec)) 74 else 75 START=$(($(echo $ROOTP | cut -f2 -d\ | tr -d "B"))) 76 SIZE=$(($(echo $ROOTP | cut -f4 -d\ | tr -d "B"))) 77 fi 78 79 dd if=$FILE of=$ROOTFS bs=$_bs skip=$(($START / $_bs)) \ 80 count=$(($SIZE / $_bs)) >/dev/null 2>&1 81 } 82 83 extract_shellball() { 84 ROOTFS=$1 85 SHELLBALL=$2 86 87 echo "Extracting chromeos-firmwareupdate" 88 printf "cd /usr/sbin\ndump chromeos-firmwareupdate $SHELLBALL\nquit" | 89 debugfs $ROOTFS >/dev/null 2>&1 90 } 91 92 extract_coreboot() { 93 _shellball=$1 94 _unpacked=$(mktemp -d) 95 96 echo "Extracting coreboot image" 97 if ! sh $_shellball --unpack $_unpacked >/dev/null 2>&1; then 98 sh $_shellball --sb_extract $_unpacked >/dev/null 2>&1 99 fi 100 101 if [ -d $_unpacked/models/ ]; then 102 _version=$(cat $_unpacked/VERSION | grep -m 1 -e Model.*$_board -A5 | 103 grep "BIOS (RW) version:" | cut -f2 -d: | tr -d \ ) 104 if [ "$_version" == "" ]; then 105 _version=$(cat $_unpacked/VERSION | grep -m 1 -e Model.*$_board -A5 | 106 grep "BIOS version:" | cut -f2 -d: | tr -d \ ) 107 fi 108 if [ -f $_unpacked/models/$_board/setvars.sh ]; then 109 _bios_image=$(grep "IMAGE_MAIN" $_unpacked/models/$_board/setvars.sh | 110 cut -f2 -d'"') 111 else 112 # special case for REEF, others? 113 _version=$(grep -m1 "host" "$_unpacked/manifest.json" | cut -f12 -d'"') 114 _bios_image=$(grep -m1 "image" "$_unpacked/manifest.json" | cut -f4 -d'"') 115 fi 116 elif [ -f "$_unpacked/manifest.json" ]; then 117 _version=$(grep -m1 -A4 "$BOARD\":" "$_unpacked/manifest.json" | grep -m1 "rw" | 118 sed 's/.*\(rw.*\)/\1/' | sed 's/.*\("Google.*\)/\1/' | cut -f2 -d'"') 119 _bios_image=$(grep -m1 -A7 "$BOARD\":" "$_unpacked/manifest.json" | grep -m1 "image" | 120 sed 's/.*"image": //' | cut -f2 -d'"') 121 else 122 _version=$(cat $_unpacked/VERSION | grep BIOS\ version: | 123 cut -f2 -d: | tr -d \ ) 124 _bios_image=bios.bin 125 fi 126 if cp $_unpacked/$_bios_image coreboot-$_version.bin; then 127 echo "Extracted coreboot-$_version.bin" 128 fi 129 rm -rf "$_unpacked" 130 rm $_shellball 131 } 132 133 do_one_board() { 134 _board=$1 135 _url=$2 136 _file=$3 137 138 download_image $_url $_file 139 140 extract_partition ROOT-A $_file root-a.ext2 141 extract_shellball root-a.ext2 chromeos-firmwareupdate-$_board 142 rm $_file root-a.ext2 143 144 extract_coreboot chromeos-firmwareupdate-$_board 145 } 146 147 # 148 # Main 149 # 150 151 BOARD=${1,,} 152 153 exit_if_dependencies_are_missing 154 155 if [ "$BOARD" == "all" ]; then 156 CONF=$(mktemp) 157 get_inventory $CONF 158 159 grep ^name= $CONF | while read _line; do 160 name=$(echo $_line | cut -f2 -d=) 161 echo Processing board $name 162 eval $(grep -v hwid= $CONF | grep -A11 "$_line" | 163 grep '\(url=\|file=\)') 164 BOARD=$(echo $url | cut -f3 -d_) 165 do_one_board $BOARD $url $file 166 done 167 168 rm "$CONF" 169 elif [ "$BOARD" != "" ]; then 170 CONF=$(mktemp) 171 get_inventory $CONF 172 173 echo Processing board $BOARD 174 eval $(grep -i $BOARD -A8 $CONF | grep '\(url=\|file=\)') 175 do_one_board $BOARD $url $file 176 177 rm "$CONF" 178 else 179 echo "Usage: $0 <boardname>" 180 echo " $0 all" 181 echo 182 exit 1 183 fi