diff options
Diffstat (limited to 'meta-phosphor/recipes-phosphor/flash/phosphor-software-manager/obmc-flash-bmc')
-rw-r--r-- | meta-phosphor/recipes-phosphor/flash/phosphor-software-manager/obmc-flash-bmc | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/meta-phosphor/recipes-phosphor/flash/phosphor-software-manager/obmc-flash-bmc b/meta-phosphor/recipes-phosphor/flash/phosphor-software-manager/obmc-flash-bmc new file mode 100644 index 000000000..0b1b16ed3 --- /dev/null +++ b/meta-phosphor/recipes-phosphor/flash/phosphor-software-manager/obmc-flash-bmc @@ -0,0 +1,516 @@ +#!/bin/bash +set -eo pipefail + +# Get the root mtd device number (mtdX) from "/dev/ubiblockX_Y on /" +findrootmtd() { + rootmatch=" on / " + m="$(mount | grep "${rootmatch}" | grep "ubiblock")" + m="${m##*ubiblock}" + m="${m%_*}" + if [ -z "${m}" ]; then + # default to bmc mtd (0) + m=0 + fi + echo "mtd${m}" +} + +findrootubi() { + rootmatch=" on / " + m="$(mount | grep "${rootmatch}")" + m="${m##*ubiblock}" + m="${m% on*}" + echo "ubi${m}" +} + +# Get the mtd device number (mtdX) +findmtd() { + m="$(grep -xl "$1" /sys/class/mtd/*/name)" + m="${m%/name}" + m="${m##*/}" + echo "${m}" +} + +# Get the mtd device number only (return X of mtdX) +findmtdnum() { + m="$(findmtd "$1")" + m="${m##mtd}" + echo "${m}" +} + +# Get the ubi device number (ubiX_Y) +findubi() { + u="$(grep -xl "$1" /sys/class/ubi/ubi?/subsystem/ubi*/name)" + u="${u%/name}" + u="${u##*/}" + echo "${u}" +} + +# Get the ubi device number (ubiX_Y) on a specific mtd +findubi_onmtd() { + u="$(grep -xl "$1" /sys/class/ubi/ubi"$2"/subsystem/ubi"$2"*/name)" + u="${u%/name}" + u="${u##*/}" + echo "${u}" +} + +# Get all ubi device names on a specific mtd that match requested string +findubiname_onmtd() { + u="$(grep -h "$1" /sys/class/ubi/ubi"$2"/subsystem/ubi"$2"*/name)" + u="${u%/name}" + u="${u##*/}" + echo "${u}" +} + +# Get the name from the requested ubiX_Y volume +findname() { + n="$(cat /sys/class/ubi/$1/name)" + echo "${n}" +} + +# Set the u-boot envs that perform a side switch on failure to boot +set_wdt2bite() { + if ! fw_printenv wdt2bite 2>/dev/null; then + fw_setenv wdt2bite "mw.l 0x1e785024 0xa 1; mw.b 0x1e78502c 0xb3 1" + fw_setenv bootalt "run wdt2bite" + fw_setenv obmc_bootcmd "ubi part obmc-ubi; run do_rwreset; ubi read \ +\${loadaddr} \${kernelname}; bootm \${loadaddr} || run bootalt" + fi +} + +# Make space on flash before creating new volumes. This can be enhanced +# determine current flash usage. For now only keep a "keepmax" number of them +ubi_remove_volumes() +{ + rootubi="$(findrootubi)" + rootname="$(findname "${rootubi}")" + rootversion="${rootname##*-}" + rootkernel="kernel-${rootversion}" + + # Just keep max number of volumes before updating, don't delete the version + # the BMC is booted from, and when a version is identified to be deleted, + # delete both the rofs and kernel volumes for that version. + rmnames="$(findubiname_onmtd "${name%-*}-" "${ro}")" + rmnames=(${rmnames}) + ubicount="${#rmnames[@]}" + while [ ${ubicount} -ge ${keepmax} ]; do + # Loop through existing volumes and skip currently active ones + for (( index=0; index<${#rmnames[@]}; index++ )); do + rmname="${rmnames[${index}]}" + rmversion="${rmname##*-}" + [ "${rmversion}" == "${version}" ] && continue + rmubi="$(findubi_onmtd "rofs-${rmversion}" "${ro}")" + if [[ ( "${rmubi}" != "${rootubi}" ) && + ( "${rmname}" != "${rootkernel}" ) ]]; then + ubi_remove "rofs-${rmversion}" "${ro}" + ubi_remove "kernel-${rmversion}" "${ro}" + # Remove priority value + fw_setenv "${rmversion}" + break + fi + done + # Decrease count regardless to avoid an infinite loop + (( ubicount-- )) + done +} + +ubi_rw() { + rwmtd="$(findmtd "${reqmtd}")" + rw="${rwmtd#mtd}" + ubidev="/dev/ubi${rw}" + + # Update rwfs_size, check imgsize was specified, otherwise it'd clear the var + if [ ! -z "$imgsize" ]; then + rwsize="$(fw_printenv -n rwfs_size 2>/dev/null)" || true + if [[ "${imgsize}" != "${rwsize}" ]]; then + fw_setenv rwfs_size "${imgsize}" + fi + fi + + vol="$(findubi "${name}")" + if [ -z "${vol}" ]; then + ubimkvol "${ubidev}" -N "${name}" -s "${imgsize}" + fi +} + +ubi_ro() { + keepmax=2 # Default 2 volumes per mtd + romtd="$(findmtd "${reqmtd}")" + romtd2="$(findmtd "${reqmtd2}")" + + if [ ! "${romtd}" == "${romtd2}" ]; then + # Request to use alternate mtd device, choose the non-root one + keepmax=1 # 1 volume on each of the requested mtds + rootmtd="$(findrootmtd)" + if [ "${rootmtd}" == "${romtd}" ]; then + romtd="${romtd2}" + fi + fi + ro="${romtd#mtd}" + ubidev="/dev/ubi${ro}" + + ubi_remove_volumes + + if [ -z "${imgfile}" ]; then + echo "Unable to create read-only volume. Image file not specified." + return 1 + fi + + # Create a ubi volume, dynamically sized to fit BMC image if size unspecified + img="/tmp/images/${version}/${imgfile}" + imgsize="$(stat -c '%s' ${img})" + + vol="$(findubi "${name}")" + if [ ! -z "${vol}" ]; then + # Allow a duplicate kernel volume on the alt mtd + if [[ "${name}" =~ "kernel" ]]; then + vol="$(findubi_onmtd "${name}" "${ro}")" + fi + fi + if [ -z "${vol}" ]; then + ubimkvol "${ubidev}" -N "${name}" -s "${imgsize}" --type=static + vol="$(findubi "${name}")" + fi +} + +# Squashfs images need a ubi block +ubi_block() { + vol="$(findubi "${name}")" + ubidevid="${vol#ubi}" + block="/dev/ubiblock${ubidevid}" + if [ ! -e "$block" ]; then + ubiblock --create "/dev/ubi${ubidevid}" + fi +} + +ubi_updatevol() { + vol="$(findubi "${name}")" + ubidevid="${vol#ubi}" + img="/tmp/images/${version}/${imgfile}" + ubiupdatevol "/dev/ubi${ubidevid}" "${img}" +} + +ubi_remove() { + rmname="$1" + rmmtd="$2" + if [ ! -z "${rmmtd}" ]; then + vol="$(findubi_onmtd "${rmname}" "${rmmtd}")" + else + vol="$(findubi "${rmname}")" + fi + + if [ ! -z "$vol" ]; then + vol="${vol%_*}" + + if grep -q "$rmname" /proc/mounts; then + mountdir=$(grep "$rmname" /proc/mounts | cut -d " " -f 2) + umount "$mountdir" + rm -r "$mountdir" + fi + + ubirmvol "/dev/${vol}" -N "$rmname" + fi +} + +ubi_cleanup() { + # When ubi_cleanup is run, it expects one or no active version. + activeVersion=$(busctl --list --no-pager tree \ + xyz.openbmc_project.Software.BMC.Updater | \ + grep /xyz/openbmc_project/software/ | tail -c 9) + + if [[ -z "$activeVersion" ]]; then + vols=$(ubinfo -a | grep "rofs-" | cut -c 14-) + vols=(${vols}) + else + vols=$(ubinfo -a | grep "rofs-" | \ + grep -v "$activeVersion" | cut -c 14-) + vols=(${vols}) + fi + + for (( index=0; index<${#vols[@]}; index++ )); do + ubi_remove ${vols[index]} + done +} + +mount_alt_rwfs() { + altNum="$(findmtdnum "alt-bmc")" + if [ ! -z "${altNum}" ]; then + altRwfs=$(ubinfo -a -d ${altNum} | grep -w "rwfs") || true + if [ ! -z "${altRwfs}" ]; then + altVarMount="/media/alt/var" + mkdir -p "${altVarMount}" + if mount ubi"${altNum}":rwfs "${altVarMount}" -t ubifs -o defaults; then + mkdir -p "${altVarMount}"/persist/etc + fi + fi + fi +} + +remount_ubi() { + bmcmtd="$(findmtd "bmc")" + altbmcmtd="$(findmtd "alt-bmc")" + mtds="${bmcmtd: -1}","${altbmcmtd: -1}" + + IFS=',' read -r -a mtds <<< "$mtds" + mtds=($(echo "${mtds[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) + for mtd in ${mtds[@]}; do + # Get information on all ubi volumes + ubinfo=$(ubinfo -d ${mtd}) + presentVolumes=${ubinfo##*:} + IFS=', ' read -r -a array <<< "$presentVolumes" + for element in ${array[@]}; do + elementProperties=$(ubinfo -d $mtd -n $element) + # Get ubi volume name by getting rid of additional properties + name=${elementProperties#*Name:} + name="${name%Character*}" + name="$(echo -e "${name}" | tr -d '[:space:]')" + + if [[ ${name} == rofs-* ]]; then + mountdir="/media/${name}" + + if [ ! -d ${mountdir} ]; then + mkdir -p "${mountdir}" + # U-Boot will create the ubiblock for the running version, but not + # for the version on the other chip + if [ ! -e "/dev/ubiblock${mtd}_${element}" ]; then + ubiblock --create /dev/ubi${mtd}_${element} + fi + mount -t squashfs -o ro "/dev/ubiblock${mtd}_${element}" "${mountdir}" + fi + fi + done + done + + set_wdt2bite +} + +# Read the current env variable and set it on the alternate boot env +copy_env_var_to_alt() { + varName=$1 + value="$(fw_printenv -n "${varName}")" + fw_setenv -c /etc/alt_fw_env.config "${varName}" "${value}" +} + +# When the alternate bmc chip boots, u-boot thinks its the primary mtdX. +# Therefore need to swap the chip numbers when copying the ubiblock and root to +# alternate bmc u-boot environment. +copy_ubiblock_to_alt() { + value="$(fw_printenv -n ubiblock)" + bmcNum="$(findmtdnum "bmc")" + altNum="$(findmtdnum "alt-bmc")" + replaceAlt="${value/${altNum},/${bmcNum},}" + + if [[ "${value}" == "${replaceAlt}" ]]; then + replaceBmc="${value/${bmcNum},/${altNum},}" + value=${replaceBmc} + else + value=${replaceAlt} + fi + + fw_setenv -c /etc/alt_fw_env.config ubiblock "${value}" +} + +copy_root_to_alt() { + value="$(fw_printenv -n root)" + bmcNum="$(findmtdnum "bmc")" + altNum="$(findmtdnum "alt-bmc")" + replaceAlt="${value/${altNum}_/${bmcNum}_}" + + if [[ "${value}" == "${replaceAlt}" ]]; then + replaceBmc="${value/${bmcNum}_/${altNum}_}" + value=${replaceBmc} + else + value=${replaceAlt} + fi + + fw_setenv -c /etc/alt_fw_env.config root "${value}" +} + +ubi_setenv() { + # The U-Boot environment maintains two banks of environment variables. + # The banks need to be consistent with each other to ensure that these + # variables can reliably be read from file. In order to guarantee that the + # banks are both correct, we need to run fw_setenv twice. + variable=$1 + if [[ "$variable" == *"="* ]]; then + varName="${variable%=*}" + value="${variable##*=}" + # Write only if var is not set already to the requested value + currentValue="$(fw_printenv -n "${varName}" 2>/dev/null)" || true + if [[ "${currenValue}" != "${value}" ]]; then + fw_setenv "$varName" "$value" + fw_setenv "$varName" "$value" + fi + else + fw_setenv "$variable" + fw_setenv "$variable" + fi +} + +mtd_write() { + flashmtd="$(findmtd "${reqmtd}")" + img="/tmp/images/${version}/${imgfile}" + flashcp -v ${img} /dev/${flashmtd} +} + +backup_env_vars() { + copy_env_var_to_alt kernelname + copy_ubiblock_to_alt + copy_root_to_alt +} + +update_env_vars() { + vol="$(findubi rofs-"${version}")" + if [ -z "${vol}" ]; then + return 1 + fi + ubidevid="${vol#ubi}" + block="/dev/ubiblock${ubidevid}" + if [ ! -e "${block}" ]; then + return 1 + fi + ubi_setenv "kernelname=kernel-${version}" + ubi_setenv "ubiblock=$(echo "${ubidevid}" | sed 's/_/,/')" + ubi_setenv "root=${block}" +} + +#TODO: Replace the implementation with systemd-inhibitors lock +# once systemd/systemd#949 is resolved +rebootguardenable() { + dir="/run/systemd/system/" + file="reboot-guard.conf" + units=("reboot" "poweroff" "halt") + + for unit in "${units[@]}"; do + mkdir -p ${dir}${unit}.target.d + echo -e "[Unit]\nRefuseManualStart=yes" >> ${dir}${unit}.target.d/${file} + done +} + +#TODO: Replace the implementation with systemd-inhibitors lock +# once systemd/systemd#949 is resolved +rebootguarddisable() { + dir="/run/systemd/system/" + file="reboot-guard.conf" + units=("reboot" "poweroff" "halt") + + for unit in "${units[@]}"; do + rm -rf ${dir}${unit}.target.d/${file} + done +} + +# Create a copy in the alt mtd +create_vol_in_alt() { + alt="alt-bmc" + altmtd="$(findmtd "${alt}")" + if [ ! -z "${altmtd}" ]; then + reqmtd="${alt}" + reqmtd2="${alt}" + ubi_ro + ubi_updatevol + fi +} + +# Copy contents of one MTD device to another +mtd_copy() { + in=$1 + out=$2 + + # Must erase MTD first to prevent corruption + flash_eraseall "${out}" + dd if="${in}" of="${out}" +} + +mirroruboot() { + bmc="$(findmtd "u-boot")" + bmcdev="/dev/${bmc}" + alt="$(findmtd "alt-u-boot")" + altdev="/dev/${alt}" + + checksum_bmc="$(md5sum "${bmcdev}")" + checksum_bmc="${checksum_bmc% *}" + checksum_alt="$(md5sum "${altdev}")" + checksum_alt="${checksum_alt% *}" + + if [[ "${checksum_bmc}" != "${checksum_alt}" ]]; then + bmcenv="$(findmtd "u-boot-env")" + bmcenvdev="/dev/${bmcenv}" + altenv="$(findmtd "alt-u-boot-env")" + altenvdev="/dev/${altenv}" + + echo "Mirroring U-boot to alt chip" + mtd_copy "${bmcdev}" "${altdev}" + mtd_copy "${bmcenvdev}" "${altenvdev}" + + copy_ubiblock_to_alt + copy_root_to_alt + fi +} + +case "$1" in + mtduboot) + reqmtd="$2" + version="$3" + imgfile="image-u-boot" + mtd_write + ;; + ubirw) + reqmtd="$2" + name="$3" + imgsize="$4" + ubi_rw + ;; + ubiro) + reqmtd="$(echo "$2" | cut -d "+" -f 1)" + reqmtd2="$(echo "$2" | cut -d "+" -f 2)" + name="$3" + version="$4" + imgfile="image-rofs" + ubi_ro + ubi_updatevol + ubi_block + ;; + ubikernel) + reqmtd="$(echo "$2" | cut -d "+" -f 1)" + reqmtd2="$(echo "$2" | cut -d "+" -f 2)" + name="$3" + version="$4" + imgfile="image-kernel" + ubi_ro + ubi_updatevol + create_vol_in_alt + ;; + ubiremove) + name="$2" + ubi_remove "${name}" + ;; + ubicleanup) + ubi_cleanup + ;; + ubisetenv) + ubi_setenv "$2" + ;; + ubiremount) + remount_ubi + mount_alt_rwfs + ;; + createenvbackup) + backup_env_vars + ;; + updateubootvars) + version="$2" + update_env_vars + ;; + rebootguardenable) + rebootguardenable + ;; + rebootguarddisable) + rebootguarddisable + ;; + mirroruboot) + mirroruboot + ;; + *) + echo "Invalid argument" + exit 1 + ;; +esac |