summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh
diff options
context:
space:
mode:
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh')
-rw-r--r--meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh550
1 files changed, 321 insertions, 229 deletions
diff --git a/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh
index 03dac7582..17923b84d 100644
--- a/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh
+++ b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh
@@ -1,263 +1,355 @@
#!/bin/sh
-SSH_ID=$HOME/.ssh/id_rsa.db
-[ -e $HOME/.fwupd.defaults ] && source $HOME/.fwupd.defaults
+log() {
+ echo "$@"
+}
-usage() {
- echo "usage: $(basename $0) uri"
- echo " uri is something like: file:///path/to/fw"
- echo " tftp://tftp.server.ip.addr/path/to/fw"
- echo " scp://[user@]scp.server.ip.addr:/path/to/fw"
- echo " http[s]://web.server.ip.addr/path/to/fw"
- echo " ftp://[user@]ftp.server.ip.addr/path/to/fw"
- exit 1
+FWTYPE=""
+FWVER=""
+redfish_log_fw_evt() {
+ local evt=$1
+ local sev=""
+ local msg=""
+ [ -z "$FWTYPE" ] && return
+ [ -z "$FWVER" ] && return
+ case "$evt" in
+ start)
+ evt=OpenBMC.0.1.FirmwareUpdateStarted
+ msg="${FWTYPE} firmware update to version ${FWVER} started."
+ sev=OK
+ ;;
+ success)
+ evt=OpenBMC.0.1.FirmwareUpdateCompleted
+ msg="${FWTYPE} firmware update to version ${FWVER} completed successfully."
+ sev=OK
+ ;;
+ abort)
+ evt=OpenBMC.0.1.FirmwareUpdateFailed
+ msg="${FWTYPE} firmware update to version ${FWVER} failed."
+ sev=Warning
+ ;;
+ *) return ;;
+ esac
+ logger-systemd --journald <<-EOF
+ MESSAGE=$msg
+ PRIORITY=2
+ SEVERITY=${sev}
+ REDFISH_MESSAGE_ID=${evt}
+ REDFISH_MESSAGE_ARGS=${FWTYPE},${FWVER}
+ EOF
}
-logevent_update_started() {
-echo
-cat <<EOF | logger-systemd --journald
-REDFISH_MESSAGE_ID=OpenBMC.0.1.FirmwareUpdateStarted
-PRIORITY=2
-MESSAGE=$1 firmware update to version $2 started.
-REDFISH_MESSAGE_ARGS=$1,$2
-EOF
+wait_for_log_sync()
+{
+ sync
+ sleep 5
}
-logevent_update_completed() {
-echo
-cat <<EOF | logger-systemd --journald
-REDFISH_MESSAGE_ID=OpenBMC.0.1.FirmwareUpdateCompleted
-PRIORITY=2
-MESSAGE=$1 firmware update to version $2 completed.
-REDFISH_MESSAGE_ARGS=$1,$2
-EOF
+PFR_BUS=4
+PFR_ADDR=0x38
+PFR_ID_REG=0x00
+PFR_STATE_REG=0x03
+PFR_PROV_REG=0x0a
+PFR_INTENT_REG=0x13
+pfr_read() {
+ [ $# -ne 1 ] && return 1
+ local reg=$1
+ i2cget -y $PFR_BUS $PFR_ADDR $reg 2>/dev/null
}
-logevent_update_failed() {
-echo
-cat <<EOF | logger-systemd --journald
-REDFISH_MESSAGE_ID=OpenBMC.0.1.FirmwareUpdateFailed
-PRIORITY=4
-MESSAGE=$1 firmware update to version $2 failed.
-REDFISH_MESSAGE_ARGS=$1,$2
-EOF
+pfr_write() {
+ [ $# -ne 2 ] && return 1
+ local reg=$1
+ local val=$2
+ i2cset -y $PFR_BUS $PFR_ADDR $reg $val >&/dev/null
}
-if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then usage; fi
-if [ $# -eq 0 ]; then
- # set DEFURI in $HOME/.fwupd.defaults
- URI="$DEFURI"
-else
- if [[ "$1" == *"/"* ]]; then
- URI=$1 # local file
- local_file=1 ;
+pfr_active_update() {
+ local factory_reset=""
+ systemctl stop nv-sync.service || \
+ log "BMC NV sync failed to stop"
+ # transition from non-PFR to PFR image requires factory reset
+ [ ! -e /usr/share/pfr ] && factory_reset="-r"
+ mtd-util $factory_reset pfr write $LOCAL_PATH
+ redfish_log_fw_evt success
+ # only wait for logging if not transitioning from non-PFR to PFR
+ if [ -e /usr/share/pfr ]; then
+ # exit bmc no nv mode
+ systemctl start nv-sync.service || log "failed to start nv-sync"
+ wait_for_log_sync
+ fi
+ reboot
+}
+
+pfr_staging_update() {
+ log "Updating $(basename $TGT)"
+ flash_erase $TGT $erase_offset $blk_cnt
+ log "Writing $(stat -c "%s" "$LOCAL_PATH") bytes"
+ # cat "$LOCAL_PATH" > "$TGT"
+ dd bs=4k seek=$(($erase_offset / 0x1000)) if=$LOCAL_PATH of=$TGT 2>/dev/null
+
+ # remove the updated image from /tmp
+ rm -f $LOCAL_PATH
+ redfish_log_fw_evt success
+ log "Setting update intent in PFR CPLD"
+ wait_for_log_sync
+
+ # write to PFRCPLD about BMC update intent.
+ pfr_write 0x13 $upd_intent_val
+}
+
+pfr_active_mode() {
+ # check for 0xde in register file 0
+ local id=$(pfr_read $PFR_ID_REG) || return 1
+ [ "$id" == "0xde" ] || return 1
+ local state=$(pfr_read $PFR_STATE_REG) || return 1
+ local prov=$(pfr_read $PFR_PROV_REG) || return 1
+ prov=$((prov & 0x20))
+ [ "$prov" == "32" ] && return 0
+ return 1
+}
+
+blk0blk1_update() {
+ # PFR-style image update section
+ # read the image type from the uploaded image
+ # Byte at location 0x8 gives image type
+ TGT="/dev/mtd/image-stg"
+ img_type=$(hexdump -s 8 -n 1 -e '/1 "%02x\n"' $LOCAL_PATH)
+ log "image-type=$img_type"
+
+ if [ $local_file -eq 0 ]; then
+ img_type_str=$(busctl get-property xyz.openbmc_project.Software.BMC.Updater /xyz/openbmc_project/software/$img_obj xyz.openbmc_project.Software.Version Purpose | cut -d " " -f 2 | cut -d "." -f 6 | sed 's/.\{1\}$//')
+ img_target=$(busctl get-property xyz.openbmc_project.Software.BMC.Updater /xyz/openbmc_project/software/$img_obj xyz.openbmc_project.Software.Activation RequestedActivation | cut -d " " -f 2| cut -d "." -f 6 | sed 's/.\{1\}$//')
else
- URI="file:////tmp/images/$1/image-runtime"
- local_file=0 ;
+ img_type_str='BMC'
+ img_target='Active'
fi
-fi
-PROTO=$(echo "$URI" | sed 's,\([a-z]*\)://.*$,\1,')
-REMOTE=$(echo "$URI" | sed 's,.*://\(.*\)$,\1,')
-REMOTE_HOST=$(echo "$REMOTE" | sed 's,\([^/]*\)/.*$,\1,')
-if [ "$PROTO" = 'scp' ]; then
- REMOTE_PATH=$(echo "$REMOTE" | cut -d':' -f2)
-else
- REMOTE_PATH=$(echo "$REMOTE" | sed 's,[^/]*/\(.*\)$,\1,')
-fi
-LOCAL_PATH="/tmp/$(basename $REMOTE_PATH)"
-echo "URI=$URI"
-echo "PROTO=$PROTO"
-echo "REMOTE=$REMOTE"
-echo "REMOTE_HOST=$REMOTE_HOST"
-echo "REMOTE_PATH=$REMOTE_PATH"
-echo "LOCAL_PATH=$LOCAL_PATH"
-if [ ! -e $LOCAL_PATH ] || [ $(stat -c %s $LOCAL_PATH) -eq 0 ]; then
- echo "Download '$REMOTE_PATH' from $PROTO $REMOTE_HOST $REMOTE_PATH"
- case "$PROTO" in
- scp)
- mkdir -p $HOME/.ssh
- if [ -e "$SSH_ID" ]; then
- ARG_ID="-i $SSH_ID"
- fi
- scp $ARG_ID $REMOTE_HOST$REMOTE_PATH $LOCAL_PATH
- if [ $? -ne 0 ]; then
- echo "scp $REMOTE $LOCAL_PATH failed!"
- exit 255
+ apply_time=$(busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/software/apply_time xyz.openbmc_project.Software.ApplyTime RequestedApplyTime | cut -d " " -f 2 | cut -d "." -f 6 | sed 's/.\{1\}$//')
+ log "image-name=$img_type_str"
+ log "image-target=$img_target"
+ log "apply_time=$apply_time"
+
+ case "$img_type" in
+ 04)
+ if [ "$img_type_str" == 'BMC' ]; then
+ # BMC image - max size 32MB
+ log "BMC firmware image"
+ img_size=33554432
+ if [ "$img_target" == 'StandbySpare' ]; then
+ upd_intent_val=0x10
+ else
+ upd_intent_val=0x08
fi
- ;;
- tftp)
- cd /tmp
- tftp -g -r "$REMOTE_PATH" "$REMOTE_HOST"
- if [ $? -ne 0 ]; then
- echo "tftp -g -r \"$REMOTE_PATH\" \"$REMOTE_HOST\" failed!"
- exit 255
+ erase_offset=0
+ FWTYPE="BMC"
+ FWVER="${RANDOM}-fixme"
+ else
+ # log error the image selected for update is not same as downloaded.
+ log "Mismatch: image selected for update and image parsed are different"
+ redfish_log_fw_evt abort
+ return 1
+ fi
+ ;;
+ 00)
+ if [ "$img_type_str" == 'Other' ]; then
+ log "CPLD firmware image"
+ # CPLD image- max size 1MB
+ img_size=1048576
+ if [ "$img_target" == 'StandbySpare' ]; then
+ upd_intent_val=0x20
+ else
+ upd_intent_val=0x04
fi
- ;;
- http|https|ftp)
- wget --no-check-certificate "$URI" -O "$LOCAL_PATH"
- if [ $? -ne 0 ]; then
- echo "wget $URI failed!"
- exit 255
+ erase_offset=0x3000000
+ FWTYPE="CPLD"
+ FWVER="${RANDOM}-fixme"
+ else
+ # log error the image selected for update is not same as downloaded.
+ log "Mismatch: image selected for update and image parsed are different"
+ redfish_log_fw_evt abort
+ return 1
+ fi
+ ;;
+ 02)
+ if [ "$img_type_str" = 'Host' ]; then
+ # BIOS image- max size 16MB
+ log "BIOS firmware image"
+ img_size=16777216
+ if [ "$img_target" == 'StandbySpare' ]; then
+ upd_intent_val=0x02
+ else
+ upd_intent_val=0x41
fi
- ;;
- file)
- cp "$REMOTE_PATH" "$LOCAL_PATH"
- ;;
- *)
- echo "Invalid URI $URI"
- exit 1;
- ;;
+ erase_offset=0x2000000
+ # TODO: parse out the fwtype and fwver once that is specified
+ FWTYPE="BIOS"
+ FWVER="${RANDOM}-fixme"
+ else
+ # log error the image selected for update is not same as downloaded.
+ log "Mismatch: image selected for update and image parsed are different"
+ redfish_log_fw_evt abort
+ return 1
+ fi
+ ;;
+ *)
+ log "Unknown image type ${img_type}"
+ return 1
+ ;;
esac
-fi
-# PFR image update section
-# this file being created at build time for PFR images
-if [ -e /usr/share/pfr ] && [ $local_file -eq 0 ]; then
- if [ -e /tmp/fwupd_progress ]; then
- echo "Firmware update already in progress"
- exit 1
+ # For deferred updates
+ if [ "$apply_time" == 'OnReset' ]; then
+ upd_intent_val=$(( "$upd_intent_val"|0x80 ))
fi
-touch /tmp/fwupd_progress
-
-# read the image type from the uploaded image
-# Byte at location 0x8 gives image type
-img_type=$(busctl get-property xyz.openbmc_project.Software.BMC.Updater /xyz/openbmc_project/software/$1 xyz.openbmc_project.Software.Version Purpose | cut -d " " -f 2 | cut -d "." -f 6 | sed 's/.\{1\}$//')
-img_target=$(busctl get-property xyz.openbmc_project.Software.BMC.Updater /xyz/openbmc_project/software/$1 xyz.openbmc_project.Software.Activation RequestedActivation | cut -d " " -f 2| cut -d "." -f 6 | sed 's/.\{1\}$//')
-apply_time=$(busctl get-property xyz.openbmc_project.Settings /xyz/openbmc_project/software/apply_time xyz.openbmc_project.Software.ApplyTime RequestedApplyTime | cut -d " " -f 2 | cut -d "." -f 6 | sed 's/.\{1\}$//')
-echo "image-type=$img_type"
-echo "image-target=$img_target"
-echo "apply_time=$apply_time"
-# BMC image - max size 32MB
-if [ "$img_type" = 'BMC' ]; then
- echo "BMC firmware image"
- # 32MB - 33554432
- img_size=33554432
- if [ "$img_target" = 'StandbySpare' ]; then
- upd_intent_val=0x10
- else
- upd_intent_val=0x08
+ # do a quick sanity check on the image
+ if [ $(stat -c "%s" "$LOCAL_PATH") -gt $img_size ]; then
+ log "Update file "$LOCAL_PATH" is bigger than the supported image size"
+ redfish_log_fw_evt abort
+ return 1
fi
- # page is at 4KB boundary
- img_page_offset=0
- erase_offset=0
- blk_cnt=0x200
-# CPLD image- max size 4MB
-elif [ "$img_type" = 'Other' ]; then
- echo "CPLD firmware image"
- # 4MB - 4194304
- img_size=4194304
- upd_intent_val=0x04
- # dd command accepts the offset in decimal
- # below is the page offset in 4KB boundary
- img_page_offset=12288
- erase_offset=0x3000000
- blk_cnt=0x40
-# BIOS image- max size 16MB
-elif [ "$img_type" = 'Host' ]; then
- echo "BIOS firmware image"
- # 16MB- 16777216
- img_size=16777216
- if [ "$img_target" = 'StandbySpare' ]; then
- upd_intent_val=0x02
+ blk_cnt=$((img_size / 0x10000))
+
+ if pfr_active_mode; then
+ # pfr enforcing mode; any b0b1 image type
+ pfr_staging_update
+ elif [ "$img_type" == '04' ]; then
+ # legacy mode; pfr is not present but we got a pfr image
+ log "Updating BMC active firmware- PFR unprovisioned mode"
+ pfr_active_update
else
- upd_intent_val=0x01
+ # error; pfr is not present but we got a pfr image,
+ # an invalid image, or nonBMC image
+ log "PFR inactive or invalid image type:${img_type}, cowardly refusing to process image"
+ redfish_log_fw_evt abort
+ return 1
fi
- # dd command accepts the offset in decimal
- # below is the page offset in 4KB boundary
- img_page_offset=8192
- erase_offset=0x2000000
- blk_cnt=0x100
-else
- echo "${img_type}:Unknown image type, exiting the firmware update script"
- rm -rf /tmp/fwupd_progress
- exit 1
-fi
-
-if [ "$apply_time" == 'OnReset' ]; then
- upd_intent_val=$(( "$upd_intent_val"|0x80 ))
-fi
-
-# do a size check on the image
-if [ $(stat -c "%s" "$LOCAL_PATH") -gt $img_size ]; then
- echo "Update file "$LOCAL_PATH" is bigger than the supported image size"
- rm -rf /tmp/fwupd_progress
- exit 1
-fi
-
-TGT="/dev/mtd/image-stg"
-echo "Update $(basename $TGT)"
-flash_erase $TGT $erase_offset $blk_cnt
-sync
-echo "Writing $(stat -c "%s" "$LOCAL_PATH") bytes"
-# cat "$LOCAL_PATH" > "$TGT"
-dd bs=4k seek=$img_page_offset if=$LOCAL_PATH of=$TGT
-sync
-echo "Written $(stat -c "%s" "$LOCAL_PATH") bytes"
-# remove the updated image from /tmp
-rm -f $LOCAL_PATH
-echo "Writing $upd_intent_val to update intent register in PFR RoT"
-sleep 5 # delay for sync and to get the above echo messages
-
-# remove the file which used as lock
-rm -rf /tmp/fwupd_progress
-
-# write to PFRCPLD about BMC update intent.
-i2cset -y 4 0x38 0x13 $upd_intent_val
-
-else # Non-PFR image update section
-version="unknown"
-component="BMC"
-manifest_file=$(dirname "${REMOTE_PATH}")"/MANIFEST"
-if [ -e $manifest_file ]; then
- version=`awk -F= -v key="version" '$1==key {print $2}' $manifest_file`
-fi
-
-logevent_update_started $component $version
-
-# do a quick sanity check on the image
-if [ $(stat -c "%s" "$LOCAL_PATH") -lt 10000000 ]; then
- echo "Update file "$LOCAL_PATH" seems to be too small"
- logevent_update_failed $component $version
- exit 1
-fi
-dtc -I dtb -O dtb "$LOCAL_PATH" > /dev/null 2>&1
-if [ $? -ne 0 ]; then
- echo "Update file $LOCAL_PATH doesn't seem to be in the proper format"
- logevent_update_failed $component $version
- exit 1
-fi
+}
-# guess based on fw_env which partition we booted from
-BOOTADDR=$(fw_printenv bootcmd | awk '{print $2}')
+ping_pong_update() {
+ # do a quick sanity check on the image
+ if [ $(stat -c "%s" "$LOCAL_PATH") -lt 10000000 ]; then
+ log "Update file "$LOCAL_PATH" seems to be too small"
+ redfish_log_fw_evt abort
+ return 1
+ fi
+ dtc -I dtb -O dtb "$LOCAL_PATH" > /dev/null 2>&1
+ if [ $? -ne 0 ]; then
+ log "Update file $LOCAL_PATH doesn't seem to be in the proper format"
+ redfish_log_fw_evt abort
+ return 1
+ fi
-TGT="/dev/mtd/image-a"
-if [ ! -e /usr/share/pfr ]; then
+ # guess based on fw_env which partition we booted from
+ local BOOTADDR=$(fw_printenv bootcmd | awk '{print $2}')
+ local TGT="/dev/mtd/image-a"
case "$BOOTADDR" in
20080000) TGT="/dev/mtd/image-b"; BOOTADDR="22480000" ;;
22480000) TGT="/dev/mtd/image-a"; BOOTADDR="20080000" ;;
*) TGT="/dev/mtd/image-a"; BOOTADDR="20080000" ;;
esac
-fi
-echo "Updating $(basename $TGT) (use bootm $BOOTADDR)"
-flash_erase $TGT 0 0
-if [ $? -ne 0 ]; then
- echo "Erasing the flash failed"
- logevent_update_failed $component $version
- exit 1
-fi
-echo "Writing $(stat -c "%s" "$LOCAL_PATH") bytes"
-cat "$LOCAL_PATH" > "$TGT"
-if [ $? -ne 0 ]; then
- echo "Writing to flash failed"
- logevent_update_failed $component $version
- exit 1
-fi
-fw_setenv "bootcmd" "bootm ${BOOTADDR}"
+ log "Updating $(basename $TGT) (use bootm $BOOTADDR)"
+ flash_erase $TGT 0 0
+ log "Writing $(stat -c "%s" "$LOCAL_PATH") bytes"
+ cat "$LOCAL_PATH" > "$TGT"
+ fw_setenv "bootcmd" "bootm ${BOOTADDR}"
+ redfish_log_fw_evt success
+ wait_for_log_sync
+ # reboot
+ reboot
+}
-logevent_update_completed $component $version
+fetch_fw() {
+ PROTO=$(echo "$URI" | sed 's,\([a-z]*\)://.*$,\1,')
+ REMOTE=$(echo "$URI" | sed 's,.*://\(.*\)$,\1,')
+ REMOTE_HOST=$(echo "$REMOTE" | sed 's,\([^/]*\)/.*$,\1,')
+ if [ "$PROTO" = 'scp' ]; then
+ REMOTE_PATH=$(echo "$REMOTE" | cut -d':' -f2)
+ else
+ REMOTE_PATH=$(echo "$REMOTE" | sed 's,[^/]*/\(.*\)$,\1,')
+ fi
+ LOCAL_PATH="/tmp/$(basename $REMOTE_PATH)"
+ log "PROTO=$PROTO"
+ log "REMOTE=$REMOTE"
+ log "REMOTE_HOST=$REMOTE_HOST"
+ log "REMOTE_PATH=$REMOTE_PATH"
+ if [ ! -e $LOCAL_PATH ] || [ $(stat -c %s $LOCAL_PATH) -eq 0 ]; then
+ log "Download '$REMOTE_PATH' from $PROTO $REMOTE_HOST $REMOTE_PATH"
+ case "$PROTO" in
+ scp)
+ mkdir -p $HOME/.ssh
+ if [ -e "$SSH_ID" ]; then
+ ARG_ID="-i $SSH_ID"
+ fi
+ scp $ARG_ID $REMOTE_HOST$REMOTE_PATH $LOCAL_PATH
+ if [ $? -ne 0 ]; then
+ log "scp $REMOTE $LOCAL_PATH failed!"
+ return 1
+ fi
+ ;;
+ tftp)
+ cd /tmp
+ tftp -g -r "$REMOTE_PATH" "$REMOTE_HOST"
+ if [ $? -ne 0 ]; then
+ log "tftp -g -r \"$REMOTE_PATH\" \"$REMOTE_HOST\" failed!"
+ return 1
+ fi
+ ;;
+ http|https|ftp)
+ wget --no-check-certificate "$URI" -O "$LOCAL_PATH"
+ if [ $? -ne 0 ]; then
+ log "wget $URI failed!"
+ return 1
+ fi
+ ;;
+ file)
+ LOCAL_PATH=$(echo $URI | sed 's,^file://,,')
+ ;;
+ *)
+ log "Invalid URI $URI"
+ return 1
+ ;;
+ esac
+ fi
+}
-# reboot
-reboot
+update_fw() {
+ redfish_log_fw_evt start
+ # determine firmware file type
+ local magic=$(hexdump -n 4 -v -e '/1 "%02x"' "$LOCAL_PATH")
+ case "$magic" in
+ d00dfeed) ping_pong_update ;;
+ 19fdeab6) blk0blk1_update ;;
+ *) log "Uknown file type ${magic}"
+ esac
+}
+
+# if this script was sourced, just return without executing anything
+[ "$_" != "$0" ] && return 0 >&/dev/null
+
+usage() {
+ echo "usage: $(basename $0) uri"
+ echo " uri is something like: file:///path/to/fw"
+ echo " tftp://tftp.server.ip.addr/path/to/fw"
+ echo " scp://[user@]scp.server.ip.addr:/path/to/fw"
+ echo " http[s]://web.server.ip.addr/path/to/fw"
+ echo " ftp://[user@]ftp.server.ip.addr/path/to/fw"
+ exit 1
+}
+
+if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then usage; fi
+if [ $# -eq 0 ]; then
+ # set DEFURI in $HOME/.fwupd.defaults
+ URI="$DEFURI"
+else
+ if [[ "$1" == *"/"* ]]; then
+ URI=$1 # local file
+ local_file=1 ;
+ else
+ URI="file:////tmp/images/$1/image-runtime"
+ img_obj=$1
+ local_file=0 ;
+ fi
fi
+fetch_fw && update_fw