diff options
Diffstat (limited to 'meta-openbmc-mods/meta-common/recipes-core/fw-update')
3 files changed, 424 insertions, 0 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 new file mode 100644 index 000000000..3073128e0 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/fwupd.sh @@ -0,0 +1,258 @@ +#!/bin/sh + +SSH_ID=$HOME/.ssh/id_rsa.db +[ -e $HOME/.fwupd.defaults ] && source $HOME/.fwupd.defaults + +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 +} + +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 +} + +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 +} + +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 +} + +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" + local_file=0 ; + 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 + 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 + fi + ;; + http|https|ftp) + wget --no-check-certificate "$URI" -O "$LOCAL_PATH" + if [ $? -ne 0 ]; then + echo "wget $URI failed!" + exit 255 + fi + ;; + file) + cp "$REMOTE_PATH" "$LOCAL_PATH" + ;; + *) + echo "Invalid URI $URI" + exit 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 + 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\}$//') + +echo "image-type=$img_type" +echo "image-target=$img_target" + +# 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 + 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 + else + upd_intent_val=0x01 + 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 + +# 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}') + +TGT="/dev/mtd/image-a" +if [ ! -e /usr/share/pfr ]; then + 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}" + +logevent_update_completed $component $version + +# reboot +reboot +fi diff --git a/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/usb-ctrl b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/usb-ctrl new file mode 100644 index 000000000..ae9f54263 --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/fw-update/files/usb-ctrl @@ -0,0 +1,136 @@ +#!/bin/sh + +setup_image() +{ + set -x + local storage="$1" + local sz_mb="$2" + # create the backing store + dd if=/dev/zero of=$storage bs=1M seek=$sz_mb count=0 2>/dev/null + # this shows up as 23FC-F676 in /dev/disk/by-uuid + local diskid=0x23FCF676 + mkdosfs -n 'OPENBMC-FW' -i $diskid -I $storage >/dev/null 2>&1 +} + +mount_image() +{ + set -x + local storage="$1" + local stormnt="$2" + mkdir -p $stormnt || exit 1 + mount -o loop -t vfat $storage $stormnt +} + +cleanup_image() +{ + set -x + local storage="$1" + local stormnt="$2" + umount -f "$stormnt" + rm -f "$storage" + rmdir "$stormnt" +} + +GADGET_BASE=/sys/kernel/config/usb_gadget + +which_dev() +{ + local in_use=$(cat $GADGET_BASE/*/UDC) + cd /sys/class/udc + for D in *; do + case "$in_use" in + *"$D"*) ;; + *) echo "$D"; return 0;; + esac + done + return 1 +} + +usb_ms_insert() +{ + local name="$1" + local storage="$2" + + if [ -d $GADGET_BASE/$name ]; then + echo "device $name already exists" >&2 + return 1 + fi + mkdir $GADGET_BASE/$name + cd $GADGET_BASE/$name + + echo 0x1d6b > idVendor # Linux Foundation + echo 0x0105 > idProduct # FunctionFS Gadget + mkdir strings/0x409 + local machineid=$(cat /etc/machine-id) + local data="OpenBMC USB mass storage gadget device serial number" + local serial=$( echo -n "${machineid}${data}${machineid}" | \ + sha256sum | cut -b 0-12 ) + echo $serial > strings/0x409/serialnumber + echo OpenBMC > strings/0x409/manufacturer + echo "OpenBMC Mass Storage" > strings/0x409/product + + mkdir configs/c.1 + mkdir functions/mass_storage.$name + echo $storage > functions/mass_storage.$name/lun.0/file + echo 0 > functions/mass_storage.$name/lun.0/removable + mkdir configs/c.1/strings/0x409 + + echo "Conf 1" > configs/c.1/strings/0x409/configuration + echo 120 > configs/c.1/MaxPower + ln -s functions/mass_storage.$name configs/c.1 + local dev=$(which_dev) + echo $dev > UDC +} + +usb_ms_eject() +{ + local name="$1" + + echo '' > $GADGET_BASE/$name/UDC + + rm -f $GADGET_BASE/$name/configs/c.1/mass_storage.$name + rmdir $GADGET_BASE/$name/configs/c.1/strings/0x409 + rmdir $GADGET_BASE/$name/configs/c.1 + rmdir $GADGET_BASE/$name/functions/mass_storage.$name + rmdir $GADGET_BASE/$name/strings/0x409 + rmdir $GADGET_BASE/$name +} + +usage() +{ + echo "Usage: $0 <action> ..." + echo " $0 setup <file> <sizeMB>" + echo " $0 insert <name> <file>" + echo " $0 eject <name>" + echo " $0 mount <file> <mnt>" + echo " $0 cleanup <file> <mnt>" + exit 1 +} + +echo "$#: $0 $@" +case "$1" in + insert) + shift + usb_ms_insert "$@" + ;; + eject) + shift + usb_ms_eject "$@" + ;; + setup) + shift + setup_image "$@" + ;; + mount) + shift + mount_image "$@" + ;; + cleanup) + shift + cleanup_image "$@" + ;; + *) + usage + ;; +esac +exit $? diff --git a/meta-openbmc-mods/meta-common/recipes-core/fw-update/intel-fw-update.bb b/meta-openbmc-mods/meta-common/recipes-core/fw-update/intel-fw-update.bb new file mode 100644 index 000000000..7c994053e --- /dev/null +++ b/meta-openbmc-mods/meta-common/recipes-core/fw-update/intel-fw-update.bb @@ -0,0 +1,30 @@ +SUMMARY = "Temporary intel-fw-update script" +DESCRIPTION = "At runtime, perform a firmware update and reboot" +PR = "r1" + +# flash_eraseall +RDEPENDS_intel-fw-update += "mtd-utils" +# wget tftp scp +RDEPENDS_intel-fw-update += "busybox dropbear" +# mkfs.vfat, parted +RDEPENDS_intel-fw-update += "dosfstools" + +LICENSE = "Apache-2.0" +LIC_FILES_CHKSUM = "file://${INTELBASE}/COPYING.apache-2.0;md5=34400b68072d710fecd0a2940a0d1658" +PFR_EN = "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', 'pfr', '', d)}" + +SRC_URI += "file://fwupd.sh" +SRC_URI += "file://usb-ctrl" + +FILES_${PN} += "${@bb.utils.contains('IMAGE_FSTYPES', 'intel-pfr', '${datadir}/pfr', '', d)}" + +do_install() { + install -d ${D}${bindir} + install -m 0755 ${WORKDIR}/fwupd.sh ${D}${bindir} + install -m 0755 ${WORKDIR}/usb-ctrl ${D}${bindir} + + if [ "${PFR_EN}" = "pfr" ]; then + install -d ${D}${datadir} + touch ${D}${datadir}/pfr + fi +} |