Created
May 19, 2017 16:05
-
-
Save popcornmix/1068a021003a4aee1de0786f33c60bf2 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
set -o nounset | |
set -o errexit | |
REPO_URI=${REPO_URI:-"https://github.com/Hexxeh/rpi-firmware"} | |
UPDATE_SELF=${UPDATE_SELF:-1} | |
UPDATE_URI="https://raw.githubusercontent.com/Hexxeh/rpi-update/master/rpi-update" | |
if [[ ${BOOT_PATH:-"unset"} == "unset" && ${ROOT_PATH:-"unset"} != "unset" ]] || | |
[[ ${BOOT_PATH:-"unset"} != "unset" && ${ROOT_PATH:-"unset"} == "unset" ]]; then | |
echo " *** You need to specify both ROOT_PATH and BOOT_PATH, or neither" | |
exit 1 | |
fi | |
if [[ ${BOOT_PATH:-"unset"} == "unset" ]]; then | |
NOOBS_CHECK=${NOOBS_CHECK:-1} | |
else | |
NOOBS_CHECK=${NOOBS_CHECK:-0} | |
fi | |
BRANCH=${BRANCH:-"master"} | |
ROOT_PATH=${ROOT_PATH:-"/"} | |
BOOT_PATH=${BOOT_PATH:-"/boot"} | |
WORK_PATH=${WORK_PATH:-"${ROOT_PATH}/root"} | |
SKIP_KERNEL=${SKIP_KERNEL:-0} | |
SKIP_SDK=${SKIP_SDK:-0} | |
SKIP_REPODELETE=${SKIP_REPODELETE:-0} | |
SKIP_BACKUP=${SKIP_BACKUP:-0} | |
SKIP_DOWNLOAD=${SKIP_DOWNLOAD:-0} | |
SKIP_WARNING=${SKIP_WARNING:-0} | |
WANT_SYMVERS=${WANT_SYMVERS:-0} | |
PRUNE_MODULES=${PRUNE_MODULES:-0} | |
RPI_UPDATE_UNSUPPORTED=${RPI_UPDATE_UNSUPPORTED:-0} | |
JUST_CHECK=${JUST_CHECK:-0} | |
GITHUB_API_TOKEN=${GITHUB_API_TOKEN:-""} | |
FW_REPO="${REPO_URI}.git" | |
FW_REPOLOCAL=${FW_REPOLOCAL:-"${WORK_PATH}/.rpi-firmware"} | |
FW_PATH="${BOOT_PATH}" | |
FW_MODPATH="${ROOT_PATH}/lib/modules" | |
FW_REV_IN=${1:-""} | |
FW_REVFILE="${FW_PATH}/.firmware_revision" | |
[ "${RPI_UPDATE_UNSUPPORTED}" -ne 0 ] && echo -e "You appear to be trying to update firmware on an incompatible distribution. To force update, run the following:\nsudo -E RPI_UPDATE_UNSUPPORTED=0 rpi-update" && exit 1 | |
[ `vcgencmd get_config str | egrep "^kernel="` ] && echo -e "You appear to be using a custom kernel file.\nSkipping installation of new kernel, as bundled dtb files may be incompatible with your kernel." && SKIP_KERNEL=1 | |
if command -v vcgencmd > /dev/null; then | |
vcgencmd get_config str | grep -qE "^kernel=" && echo -e "You appear to be using a custom kernel file.\nSkipping installation of new kernel, as bundled dtb files may be incompatible with your kernel." && SKIP_KERNEL=1 | |
fi | |
# Support for custom GitHub Auth Tokens | |
GITHUB_AUTH_PARAM="" | |
if [[ -n "${GITHUB_API_TOKEN}" ]]; then | |
echo " *** Using GitHub token for all requests." | |
GITHUB_AUTH_PARAM="--header \"Authorization: token ${GITHUB_API_TOKEN}\"" | |
fi | |
function update_self() { | |
echo " *** Performing self-update" | |
_tempFileName="$0.tmp" | |
if ! eval curl -Lfs ${GITHUB_AUTH_PARAM} --output "${_tempFileName}" "${UPDATE_URI}"; then | |
echo " !!! Failed to download update for rpi-update!" | |
echo " !!! Make sure you have ca-certificates installed and that the time is set correctly" | |
exit 1 | |
fi | |
OCTAL_MODE=$(stat -c '%a' "$0") | |
if ! chmod ${OCTAL_MODE} "${_tempFileName}" ; then | |
echo " !!! Failed: Error while trying to set mode on ${_tempFileName}" | |
exit 1 | |
fi | |
cat > "${WORK_PATH}/.updateScript.sh" << EOF | |
if mv "${_tempFileName}" "$0"; then | |
rm -- "\$0" | |
exec env UPDATE_SELF=0 /bin/bash "$0" "${FW_REV}" | |
else | |
echo " !!! Failed!" | |
fi | |
EOF | |
echo " *** Relaunching after update" | |
exec /bin/bash "${WORK_PATH}/.updateScript.sh" | |
} | |
function update_modules { | |
if [[ ${SKIP_KERNEL} -eq 0 ]]; then | |
echo " *** Updating kernel modules" | |
find "${FW_REPOLOCAL}/modules" -mindepth 1 -maxdepth 1 -type d | while read DIR; do | |
BASEDIR=$(basename "${DIR}") | |
rm -rf "${FW_MODPATH}/${BASEDIR}/kernel" | |
done | |
if [[ ${PRUNE_MODULES} -ne 0 ]]; then | |
find "${FW_MODPATH}" -mindepth 1 -maxdepth 1 -type d | while read DIR; do | |
COUNT=$(find ${DIR} -type f ! \( -name '*.ko' -o -name 'modules.*' \) | wc -l); | |
if [[ ${COUNT} -eq 0 ]]; then | |
echo "Pruning ${DIR}" | |
rm -rf ${DIR} | |
else | |
echo "Keeping ${DIR}" | |
fi | |
done | |
fi | |
cp -R "${FW_REPOLOCAL}/modules/"* "${FW_MODPATH}/" | |
find "${FW_REPOLOCAL}/modules" -mindepth 1 -maxdepth 1 -type d | while read DIR; do | |
BASEDIR=$(basename "${DIR}") | |
echo " *** depmod ${BASEDIR}" | |
depmod -b "${ROOT_PATH}" -a "${BASEDIR}" | |
done | |
else | |
echo " *** As requested, not updating kernel modules" | |
fi | |
} | |
function update_vc_libs { | |
echo " *** Updating VideoCore libraries" | |
if [[ -e ${ROOT_PATH}/bin/sh ]]; then | |
ELFOUTPUT=$(readelf -a "${ROOT_PATH}/bin/sh"; readelf -h "${ROOT_PATH}/bin/sh") | |
else | |
ELFOUTPUT="VFP_args" | |
fi | |
if [[ "${ELFOUTPUT}" != "${ELFOUTPUT/VFP_args/}" || \ | |
"${ELFOUTPUT}" != "${ELFOUTPUT/hard-float/}" ]]; then | |
echo " *** Using HardFP libraries" | |
cp -R "${FW_REPOLOCAL}/vc/hardfp/"* "${ROOT_PATH}/" | |
else | |
echo " *** Using SoftFP libraries" | |
cp -R "${FW_REPOLOCAL}/vc/softfp/"* "${ROOT_PATH}/" | |
fi | |
} | |
function update_sdk { | |
if [[ ${SKIP_SDK} -eq 0 ]]; then | |
echo " *** Updating SDK" | |
cp -R "${FW_REPOLOCAL}/vc/sdk/"* "${ROOT_PATH}/" | |
else | |
echo " *** As requested, not updating SDK" | |
fi | |
} | |
function show_notice { | |
local FULL_NOTICE=`eval curl -Lfs ${GITHUB_AUTH_PARAM} https://raw.githubusercontent.com/hexxeh/rpi-firmware/${FW_REV}/NOTICE.md` | |
local NOTICE=$(echo "$FULL_NOTICE" | tail -n+2) | |
if [ -z "$NOTICE" ]; then | |
return | |
fi | |
local NOTICE_HASH_HEAD=$(echo "$FULL_NOTICE" | head -1) | |
if [ $(echo "${NOTICE_HASH_HEAD}" | awk -F: '{print $1}') == "HASH" ]; then | |
local NOTICE_HASH_EXISTS=true | |
local NOTICE_HASH=$(echo "${NOTICE_HASH_HEAD}" | awk '{print $2}') | |
else | |
local NOTICE_HASH_EXISTS=false | |
fi | |
if ${NOTICE_HASH_EXISTS}; then | |
local NEW_HASH=${FW_REV} | |
local LOCAL_lt_NOTICE=$(compare_hashes ${LOCAL_HASH} lt ${NOTICE_HASH}) | |
local NEW_ge_NOTICE=$(compare_hashes ${NEW_HASH} ge ${NOTICE_HASH}) | |
if ! ${LOCAL_lt_NOTICE} && ! ${NEW_ge_NOTICE}; then | |
return | |
fi | |
fi | |
echo "$NOTICE" | |
if ! echo "$NOTICE" | grep -q WARNING; then | |
return | |
fi | |
if [[ ${SKIP_WARNING} -ne 0 ]]; then | |
return | |
fi | |
read -p "Would you like to proceed? (y/N)" -n 1 -r -s | |
echo "" | |
if ! [[ $REPLY =~ ^[Yy]$ ]]; then | |
exit 1; | |
fi | |
} | |
function update_firmware { | |
echo " *** Updating firmware" | |
rm -rf "${FW_PATH}/"*.elf | |
rm -rf "${FW_PATH}/"bootcode.bin | |
cp "${FW_REPOLOCAL}/"*.elf "${FW_PATH}/" | |
cp "${FW_REPOLOCAL}/"*.bin "${FW_PATH}/" | |
cp "${FW_REPOLOCAL}/"*.dat "${FW_PATH}/" | |
if [[ ${SKIP_KERNEL} -eq 0 ]]; then | |
cp "${FW_REPOLOCAL}/"*.img "${FW_PATH}/" | |
if [[ -n $(shopt -s nullglob; echo "${FW_REPOLOCAL}/"*.dtb*) ]]; then | |
cp "${FW_REPOLOCAL}/"*.dtb* "${FW_PATH}/" | |
fi | |
if [[ -n $(shopt -s nullglob; echo "${FW_REPOLOCAL}/overlays/"*.dtb*) ]]; then | |
mkdir -p "${FW_PATH}/overlays" | |
cp "${FW_REPOLOCAL}/overlays/"*.dtb* "${FW_PATH}/overlays/" | |
if [[ -f "${FW_REPOLOCAL}/overlays/README" ]]; then | |
cp "${FW_REPOLOCAL}/overlays/README" "${FW_PATH}/overlays/" | |
fi | |
fi | |
else | |
echo " *** As requested, not updating kernel" | |
fi | |
if [[ ${WANT_SYMVERS} -ne 0 ]]; then | |
if [[ -f "${FW_REPOLOCAL}/Module.symvers" ]]; then | |
cp "${FW_REPOLOCAL}/Module.symvers" "${FW_PATH}/" | |
fi | |
if [[ -f "${FW_REPOLOCAL}/Module7.symvers" ]]; then | |
cp "${FW_REPOLOCAL}/Module7.symvers" "${FW_PATH}/" | |
fi | |
if [[ -f "${FW_REPOLOCAL}/git_hash" ]]; then | |
cp "${FW_REPOLOCAL}/git_hash" "${FW_PATH}/" | |
fi | |
fi | |
} | |
function finalise { | |
if [[ -f "${FW_PATH}/arm192_start.elf" ]]; then | |
echo " *** Setting 192M ARM split" | |
cp "${FW_PATH}/arm192_start.elf" "${FW_PATH}/start.elf" | |
fi | |
if [[ -e ${ROOT_PATH}/etc ]]; then | |
echo " *** Running ldconfig" | |
ldconfig -r "${ROOT_PATH}" | |
fi | |
echo " *** Storing current firmware revision" | |
echo "${FW_REV}" > "${FW_REVFILE}" | |
} | |
function do_backup { | |
if [[ ${SKIP_BACKUP} -eq 0 ]]; then | |
echo " *** Backing up files (this will take a few minutes)" | |
if [[ -d "${FW_PATH}.bak" ]]; then | |
echo " *** Remove old firmware backup" | |
rm -rf "${FW_PATH}.bak" | |
fi | |
echo " *** Backing up firmware" | |
cp -a "${FW_PATH}" "${FW_PATH}.bak" | |
if [[ ${SKIP_KERNEL} -eq 0 ]]; then | |
if [[ -d "${FW_MODPATH}.bak" ]]; then | |
echo " *** Remove old modules backup" | |
rm -rf "${FW_MODPATH}.bak" | |
fi | |
echo " *** Backing up modules $(uname -r)" | |
if [[ -d "${FW_MODPATH}/$(uname -r)" ]]; then | |
mkdir -p "${FW_MODPATH}.bak" && cp -a "${FW_MODPATH}/$(uname -r)" "${FW_MODPATH}.bak" | |
fi | |
fi | |
fi | |
} | |
function do_update { | |
show_notice | |
download_rev | |
if [[ -f "${FW_REPOLOCAL}/pre-install" ]]; then | |
echo " *** Running pre-install script" | |
source "${FW_REPOLOCAL}/pre-install" | |
fi | |
update_firmware | |
update_modules | |
update_vc_libs | |
update_sdk | |
finalise | |
if [[ -f "${FW_REPOLOCAL}/post-install" ]]; then | |
echo " *** Running post-install script" | |
source "${FW_REPOLOCAL}/post-install" | |
fi | |
remove_rev | |
echo " *** Syncing changes to disk" | |
sync | |
echo " *** If no errors appeared, your firmware was successfully updated to ${FW_REV}" | |
if [[ "${ROOT_PATH}" == "/" ]]; then | |
echo " *** A reboot is needed to activate the new firmware" | |
fi | |
} | |
function download_rev { | |
if [[ ${SKIP_DOWNLOAD} -eq 0 ]]; then | |
if ! eval curl -Lfs ${GITHUB_AUTH_PARAM} --output /dev/null --head --fail "${REPO_URI}/tarball/${FW_REV}"; then | |
echo "Invalid git hash specified" | |
exit 1 | |
fi | |
echo " *** Downloading specific firmware revision (this will take a few minutes)" | |
rm -rf "${FW_REPOLOCAL}" | |
mkdir -p "${FW_REPOLOCAL}" | |
eval curl -L ${GITHUB_AUTH_PARAM} "${REPO_URI}/tarball/${FW_REV}" | tar xzf - -C "${FW_REPOLOCAL}" --strip-components=1 | |
fi | |
} | |
function remove_rev { | |
echo " *** Deleting downloaded files" | |
if [[ ${SKIP_REPODELETE} -eq 0 ]]; then | |
rm -rf "${FW_REPOLOCAL}" | |
fi | |
} | |
function noobs_fix { | |
echo " !!! $BOOT_PATH appears to contain NOOBS files" | |
echo " This may occur if fstab contains incorrect entries." | |
echo " rpi-update will attempt to correct fstab." | |
read -p "Would you like to proceed? (y/N)" -n 1 -r -s | |
echo | |
if ! [[ $REPLY =~ ^[Yy]$ ]]; then | |
exit 1; | |
fi | |
if ! grep -qE "/dev/mmcblk0p1\s+/boot" ${ROOT_PATH}/etc/fstab; then | |
echo "Unexpected fstab entry" | |
exit 1 | |
fi | |
local ROOTNUM=`cat /proc/cmdline | sed -n 's|.*root=/dev/mmcblk0p\([0-9]*\).*|\1|p'` | |
if [ ! "$ROOTNUM" ];then | |
echo "Could not determine root partition." | |
exit 1 | |
fi | |
local BOOT_DEV="/dev/mmcblk0p$((ROOTNUM-1))" | |
local ROOT_DEV="/dev/mmcblk0p${ROOTNUM}" | |
sed ${ROOT_PATH}/etc/fstab -e "s|^.*[^#].* / |${ROOT_DEV} / |;s|^.*[^#].* /boot |${BOOT_DEV} /boot |" | |
read -p "Does this look correct? (y/N)" -n 1 -r -s | |
echo | |
if ! [[ $REPLY =~ ^[Yy]$ ]]; then | |
exit 1; | |
fi | |
sed ${ROOT_PATH}/etc/fstab -i -e "s|^.*[^#].* / |${ROOT_DEV} / |;s|^.*[^#].* /boot |${BOOT_DEV} /boot |" | |
umount /boot | |
if [ $? -ne 0 ]; then | |
echo "Failed to umount /boot. Remount manually and try again." | |
exit 1 | |
else | |
mount /boot | |
fi | |
} | |
function get_hash_date { | |
commit_api="https://api.github.com/repos/Hexxeh/rpi-firmware/git/commits/" | |
eval curl -Ls ${GITHUB_AUTH_PARAM} "$commit_api"$1 | grep "date" | head -1 | awk -F\" '{print $4}' | |
} | |
function compare_hashes { | |
date1=$(get_hash_date $1) | |
date2=$(get_hash_date $3) | |
if [ $(date -d "$date1" +%s) -$2 $(date -d "$date2" +%s) ]; then | |
echo true | |
else | |
echo false | |
fi | |
} | |
function get_long_hash { | |
# ask github for long version hash | |
local REPO_API=${REPO_URI/github.com/api.github.com\/repos}/commits/$1 | |
eval curl -Ls ${GITHUB_AUTH_PARAM} ${REPO_API} | awk '{ if ($1 == "\"sha\":") { print substr($2, 2, 40); exit 0 } }' | |
} | |
if [[ ${EUID} -ne 0 ]]; then | |
echo " !!! This tool must be run as root" | |
exit 1 | |
fi | |
echo " *** Raspberry Pi firmware updater by Hexxeh, enhanced by AndrewS and Dom" | |
if [[ ! -d ${WORK_PATH} ]]; then | |
echo " !!! ${WORK_PATH} doesn't exist - creating" | |
mkdir -p ${WORK_PATH} | |
fi | |
if [[ ${UPDATE_SELF} -ne 0 ]]; then | |
update_self | |
fi | |
if [[ ! -d "${FW_PATH}" ]]; then | |
echo " !!! ${FW_PATH} doesn't exist - creating" | |
mkdir -p ${FW_PATH} | |
fi | |
if [[ ${SKIP_KERNEL} -eq 0 ]] && [[ ! -d "${FW_MODPATH}" ]]; then | |
echo " !!! ${FW_MODPATH} doesn't exist - creating" | |
mkdir -p ${FW_MODPATH} | |
fi | |
if [[ ${NOOBS_CHECK} -eq 1 ]] && [[ -f ${BOOT_PATH}/recovery.elf ]]; then | |
noobs_fix | |
fi | |
command -v readelf >/dev/null 2>&1 || { | |
echo " !!! This tool requires you have readelf installed, please install it first" | |
echo " In Debian, try: sudo apt-get install binutils" | |
echo " In Arch, try: pacman -S binutils" | |
exit 1 | |
} | |
if [[ "${FW_REV_IN}" == "" ]]; then | |
FW_REV_IN=${BRANCH} | |
fi | |
FW_REV=$(get_long_hash ${FW_REV_IN}) | |
if [[ "${FW_REV}" == "" ]]; then | |
echo " *** Invalid hash given" | |
exit 1 | |
fi | |
if [[ ! -f "${FW_REVFILE}" ]]; then | |
LOCAL_HASH=0 | |
echo " *** We're running for the first time" | |
if [[ ${JUST_CHECK} -ne 0 ]]; then | |
exit 2 | |
fi | |
do_backup | |
else | |
LOCAL_HASH=$(get_long_hash $(cat "${FW_REVFILE}")) | |
if [[ ${LOCAL_HASH} == "${FW_REV}" ]]; then | |
echo " *** Your firmware is already up to date" | |
exit 0 | |
fi | |
if [[ ${JUST_CHECK} -ne 0 ]]; then | |
echo " *** Firmware update required. New commits available:" | |
DIFF_API=${REPO_URI/github.com/api.github.com\/repos}/compare/${LOCAL_HASH}...${FW_REV} | |
SEPARATOR="======================================================" | |
eval curl -Ls ${GITHUB_AUTH_PARAM} ${DIFF_API} | awk -v SEPARATOR="${SEPARATOR}" -F\" ' { if ($2 == "commits") {commits=1} if (commits && $2 == "message") {print SEPARATOR "\nCommit: " $4} }' | sed 's/\\n\\n/\nCommit:\ /g' | |
exit 2 | |
fi | |
fi | |
do_update |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment