/ util / chromeos / crosfirmware.sh
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