#!/bin/bash # # shellcheck disable=SC2164,SC2155 ######################################## # Global Variables and Configuration # ######################################## # USB device handling export usb_devs="" export usb_device_count=0 # System detection export isChromeOS=true isChromiumOS=false isMusl=false # Firmware tool paths export flashromcmd="" export flashrom_params="" export flashrom_programmer="-p internal:boardmismatch=force" export cbfstoolcmd="" export gbbutilitycmd="" export ectoolcmd="" export tpmccmd="" # Firmware state export firmwareType="" export isStock=true export isFullRom=false export isUEFI=false export wpEnabled=false # Terminal color codes for UI NORMAL=$(echo "\033[m") MENU=$(echo "\033[36m") #Blue NUMBER=$(echo "\033[33m") #yellow FGRED=$(echo "\033[41m") RED_TEXT=$(echo "\033[31m") GRAY_TEXT=$(echo "\033[1;30m") GREEN_TEXT=$(echo "\033[1;32m") ENTER_LINE=$(echo "\033[33m") ######################################## # Utility Functions # ######################################## # Print text in red color function echo_red() { echo -e "\E[0;31m$1" echo -e '\e[0m' } # Print text in green color function echo_green() { echo -e "\E[0;32m$1" echo -e '\e[0m' } # Print text in yellow color function echo_yellow() { echo -e "\E[1;33m$1" echo -e '\e[0m' } # Print error message and wait for user input function exit_red() { echo_red "$@" read -rep "Press [Enter] to return to the main menu." } # Print error message and exit script function die() { echo_red "$@" exit 1 } ######################################## # Device Detection Functions # ######################################## # List all available USB devices with vendor/model info function list_usb_devices() { # Enumerate USB-backed block devices via lsblk TRAN field lsblk -dnpo NAME,TRAN 2>/dev/null | awk '$2=="usb"{print $1}' > /tmp/usb_block_devices # Fallback: none -> try sysfs heuristic (older systems without TRAN) if [ ! -s /tmp/usb_block_devices ]; then stat -c %N /sys/block/sd* 2>/dev/null | grep usb | cut -f1 -d ' ' \ | sed "s/[']//g;s|/sys/block|/dev|" > /tmp/usb_block_devices fi mapfile -t usb_devs < /tmp/usb_block_devices [ ${#usb_devs[@]} -gt 0 ] || return 1 echo -e "\nDevices available:\n" usb_device_count=0 for dev in "${usb_devs[@]}" do ((usb_device_count+=1)) # Resolve base device name for sysfs dev_base=$(basename "${dev}") # Read vendor/model from sysfs (most reliable) vendor=$(sed 's/^ *//;s/ *$//' "/sys/block/${dev_base}/device/vendor" 2>/dev/null) model=$(sed 's/^ *//;s/ *$//' "/sys/block/${dev_base}/device/model" 2>/dev/null) # Fallbacks for vendor/model if [ -z "${vendor}" ] || [ -z "${model}" ]; then # Try lsblk fields [ -z "${vendor}" ] && vendor=$(lsblk -dno VENDOR "${dev}" 2>/dev/null) [ -z "${model}" ] && model=$(lsblk -dno MODEL "${dev}" 2>/dev/null) # Try udevadm properties as last resort if [ -z "${vendor}" ] || [ -z "${model}" ]; then dev_info=$(udevadm info --query=property --name="${dev}" 2>/dev/null) [ -z "${vendor}" ] && vendor=$(echo "$dev_info" | grep "^ID_VENDOR=" | cut -d'=' -f2) [ -z "${model}" ] && model=$(echo "$dev_info" | grep "^ID_MODEL=" | cut -d'=' -f2) fi fi # Normalize whitespace: trim ends and squeeze internal spaces vendor=$(echo -n "${vendor}" | sed 's/^\s\+//;s/\s\+$//' | tr -s ' ') model=$(echo -n "${model}" | sed 's/^\s\+//;s/\s\+$//' | tr -s ' ') # Determine size (bytes -> human GB with 1 decimal) bytes=$(lsblk -dnbo SIZE "${dev}" 2>/dev/null) if [ -z "${bytes}" ]; then sectors=$(cat "/sys/block/${dev_base}/size" 2>/dev/null) if [ -n "${sectors}" ]; then bytes=$((sectors * 512)) fi fi if [ -n "${bytes}" ] && [ "${bytes}" -gt 0 ] 2>/dev/null; then # Use awk for floating point division sz=$(awk -v b="${bytes}" 'BEGIN { printf "%.1f GB", b/1024/1024/1024 }') else sz="" fi label="" [ -n "${vendor}" ] && label="${vendor}" if [ -n "${model}" ]; then if [ -n "${label}" ]; then label="${label} ${model}" else label="${model}" fi fi echo -n "$usb_device_count)" [ -n "${label}" ] && echo -n " ${label}" [ -n "${sz}" ] && echo -e " (${sz})" || echo -e "" done echo -e "" } ######################################## # Tool Management Functions # ######################################## # Download and setup cbfstool utility if not present function get_cbfstool() { if [ ! -f "${cbfstoolcmd}" ]; then ( cd "$(dirname "${cbfstoolcmd}")" #echo_yellow "Downloading cbfstool utility" util_file="" if [[ "$isMusl" = true ]]; then util_file="cbfstool-musl.tar.gz" else util_file="cbfstool.tar.gz" fi if ! ${CURL} -sLo "cbfstool.tar.gz" "${util_source}${util_file}"; then echo_red "Error downloading cbfstool; cannot proceed." return 1 fi if ! tar -zxf cbfstool.tar.gz --no-same-owner; then echo_red "Error extracting cbfstool; cannot proceed." return 1 fi #set +x chmod +x cbfstool ) || return 1 fi } # Download and setup flashrom utility if not present function get_flashrom() { if [ ! -f "${flashromcmd}" ]; then ( cd "$(dirname "${flashromcmd}")" util_file="" if [[ "$isChromeOS" = true ]]; then #needed to avoid dependencies not found on older ChromeOS util_file="flashrom_old.tar.gz" else if [[ "$isMusl" = true ]]; then util_file="flashrom-musl.tar.gz" else util_file="flashrom_ups_int_20241214.tar.gz" fi fi if ! ${CURL} -sLo "flashrom.tar.gz" "${util_source}${util_file}"; then echo_red "Error downloading flashrom; cannot proceed." return 1 fi if ! tar -zxf flashrom.tar.gz --no-same-owner; then echo_red "Error extracting flashrom; cannot proceed." return 1 fi #set +x chmod +x flashrom ) || return 1 fi #check if flashrom supports --noverify-all if ${flashromcmd} -h | grep -q "noverify-all" ; then export noverify="-N" else export noverify="-n" fi # append programmer type [[ "$isChromeOS" = false ]] && flashrom_programmer="${flashrom_programmer} --use-first-chip" flashromcmd="${flashromcmd} ${flashrom_programmer}" } # Download and setup gbb_utility if not present function get_gbb_utility() { if [ ! -f "${gbbutilitycmd}" ]; then ( cd "$(dirname "${gbbutilitycmd}")" util_file="" if [[ "$isMusl" = true ]]; then util_file="gbb_utility-musl.tar.gz" else util_file="gbb_utility.tar.gz" fi if ! ${CURL} -sLo "gbb_utility.tar.gz" "${util_source}${util_file}"; then echo_red "Error downloading gbb_utility; cannot proceed." return 1 fi if ! tar -zxf gbb_utility.tar.gz; then echo_red "Error extracting gbb_utility; cannot proceed." return 1 fi #set +x chmod +x gbb_utility ) || return 1 fi } # Download and setup ectool utility if not present function get_ectool() { # Regardless if running under ChromeOS or Linux, can put ectool # in same place as cbfstool ectoolcmd="$(dirname "${cbfstoolcmd}")/ectool" if [ ! -f "${ectoolcmd}" ]; then ( cd "$(dirname "${ectoolcmd}")" if ! ${CURL} -sLO "${util_source}ectool.tar.gz"; then echo_red "Error downloading ectool; cannot proceed." return 1 fi if ! tar -zxf ectool.tar.gz; then echo_red "Error extracting ectool; cannot proceed." return 1 fi #set +x chmod +x ectool ) || return 1 fi return 0 } # Download and setup tpmc utility if not present function get_tpmc() { # Regardless if running under ChromeOS or Linux, can put tpmc # in same place as cbfstool tpmccmd="$(dirname "${cbfstoolcmd}")/tpmc" if [ ! -f "${tpmccmd}" ]; then ( cd "$(dirname "${tpmccmd}")" if ! ${CURL} -sLO "${util_source}tpmc.tar.gz"; then echo_red "Error downloading tpmc; cannot proceed." return 1 fi if ! tar -zxf tpmc.tar.gz; then echo_red "Error extracting tpmc; cannot proceed." return 1 fi #set +x chmod +x tpmc ) || return 1 fi return 0 } ######################################## # Diagnostic Report Functions # ######################################## # Save diagnostic report for troubleshooting # Save diagnostic report to temporary file function diagnostic_report_save() { ( echo "mrchromebox firmware-util diagnostic report" date echo for key in "${!diagnostic_report_data[@]}"; do echo "[$key]" echo "${diagnostic_report_data[$key]}" echo done ) > /tmp/mrchromebox_diag.txt } # Set diagnostic report data for given key function diagnostic_report_set() { declare -gA diagnostic_report_data local key="$1" shift diagnostic_report_data[$key]="$*" } ######################################## # Main Setup and Initialization # ######################################## # Perform preliminary setup and validation function prelim_setup() { # Must run as root [ "$(whoami)" = "root" ] || die "You need to run this script with sudo; use 'sudo bash