blob: 833a565e34e3aaf31500827145a9adf69f3c885a [file] [log] [blame]
#!/bin/bash
set -eo pipefail
# Get the root mtd device number (mtdX) from "/dev/ubiblockX_Y on /"
function 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}"
}
function findrootubi() {
rootmatch=" on / "
m="$(mount | grep "${rootmatch}")"
m="${m##*ubiblock}"
m="${m% on*}"
echo "ubi${m}"
}
# Get the mtd device number (mtdX)
function 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)
function findmtdnum() {
m="$(findmtd "$1")"
m="${m##mtd}"
echo "${m}"
}
# Get the ubi device number (ubiX_Y)
function 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
function 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
function 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
function findname() {
n="$(cat /sys/class/ubi/"$1"/name)"
echo "${n}"
}
# Set the version path property to the flash location where the image was
# successfully flashed
function set_flashid() {
busctl set-property xyz.openbmc_project.Software.BMC.Updater \
"/xyz/openbmc_project/software/${version}" \
xyz.openbmc_project.Common.FilePath \
Path s "$1"
}
# Set the u-boot envs that perform a side switch on failure to boot
function 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
function 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}")"
mapfile -t array <<< "${rmnames}"
ubicount="${#array[@]}"
while [ "${ubicount}" -ge "${keepmax}" ]; do
# Loop through existing volumes and skip currently active ones
for (( index=0; index<${#array[@]}; index++ )); do
rmname="${array[${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
}
function 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 [ -n "$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
}
function 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 [ -n "${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
set_flashid "${version}"
}
# Squashfs images need a ubi block
function ubi_block() {
vol="$(findubi "${name}")"
ubidevid="${vol#ubi}"
block="/dev/ubiblock${ubidevid}"
if [ ! -e "$block" ]; then
ubiblock --create "/dev/ubi${ubidevid}"
fi
}
function ubi_updatevol() {
vol="$(findubi "${name}")"
ubidevid="${vol#ubi}"
img="/tmp/images/${version}/${imgfile}"
ubiupdatevol "/dev/ubi${ubidevid}" "${img}"
}
function ubi_remove() {
rmname="$1"
rmmtd="$2"
if [ -n "${rmmtd}" ]; then
vol="$(findubi_onmtd "${rmname}" "${rmmtd}")"
else
vol="$(findubi "${rmname}")"
fi
if [ -n "$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
}
function 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-)
else
flashid=$(busctl get-property xyz.openbmc_project.Software.BMC.Updater \
"/xyz/openbmc_project/software/${activeVersion}" \
xyz.openbmc_project.Common.FilePath Path | awk '{print $NF;}' | tr -d '"')
vols=$(ubinfo -a | grep "rofs-" | \
grep -v "$flashid" | cut -c 14-) || true
fi
mapfile -t array <<< "${vols}"
for (( index=0; index<${#array[@]}; index++ )); do
ubi_remove "${array[index]}"
done
}
function mount_ubi_alt_rwfs() {
altNum="$(findmtdnum "alt-bmc")"
if [ -n "${altNum}" ]; then
altRwfs=$(ubinfo -a -d "${altNum}" | grep -w "rwfs") || true
if [ -n "${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
}
function remount_ubi() {
bmcmtd="$(findmtd "bmc")"
altbmcmtd="$(findmtd "alt-bmc")"
mtds="${bmcmtd: -1}","${altbmcmtd: -1}"
rootubi="$(findrootubi)"
rootname="$(findname "${rootubi}")"
IFS=',' read -r -a arrayMtds <<< "$mtds"
for mtd in "${arrayMtds[@]}"; 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
if [[ "${name}" == "${rootname}" ]]; then
mountdir="/media/${name}-functional"
else
mountdir="/media/${name}"
fi
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
}
function mount_static_alt() {
typ=$1
altFs=$2
mountName=$3
altNum="$(findmtdnum "${altFs}")"
if [ -n "${altNum}" ]; then
altFsMount="/run/media/${mountName}"
mkdir -p "${altFsMount}"
altFsBlock="/dev/mtdblock${altNum}"
mount -t "${typ}" "${altFsBlock}" "${altFsMount}"
fi
}
function umount_static_alt() {
altFs=$1
altFsMount="/run/media/${altFs}"
umount "${altFsMount}"
}
# Read the current env variable and set it on the alternate boot env
function 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.
function 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}"
}
function 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}"
}
function 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 [[ "${currentValue}" != "${value}" ]]; then
fw_setenv "$varName" "$value"
fw_setenv "$varName" "$value"
fi
else
fw_setenv "$variable"
fw_setenv "$variable"
fi
}
function mtd_write() {
flashmtd="$(findmtd "${reqmtd}")"
img="/tmp/images/${version}/${imgfile}"
flashcp -v "${img}" /dev/"${flashmtd}"
}
function backup_env_vars() {
copy_env_var_to_alt kernelname
copy_ubiblock_to_alt
copy_root_to_alt
}
function update_env_vars() {
vol="$(findubi rofs-"${flashid}")"
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-${flashid}"
ubi_setenv "ubiblock=${ubidevid//_/,}"
ubi_setenv "root=${block}"
}
#TODO: Replace the implementation with systemd-inhibitors lock
# once systemd/systemd#949 is resolved
function 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
function 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
function create_vol_in_alt() {
alt="alt-bmc"
altmtd="$(findmtd "${alt}")"
if [ -n "${altmtd}" ]; then
reqmtd="${alt}"
reqmtd2="${alt}"
ubi_ro
ubi_updatevol
fi
}
# Copy contents of one MTD device to another
function mtd_copy() {
in=$1
out=$2
# Must erase MTD first to prevent corruption
flash_eraseall "${out}"
dd if="${in}" of="${out}"
}
function 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
}
# Compare the device where u-boot resides with an image file. Specify the full
# path to the device and image file to use for the compare. Print a value of
# "0" if identical, "1" otherwise.
function cmp_uboot() {
device="$1"
image="$2"
# Since the image file can be smaller than the device, copy the device to a
# tmp file and write the image file on top, then compare the sum of each.
# Use cat / redirection since busybox does not have the conv=notrunc option.
tmpFile="$(mktemp /tmp/ubootdev.XXXXXX)"
dd if="${device}" of="${tmpFile}"
devSum="$(sha256sum "${tmpFile}")"
cat < "${image}" 1<> "${tmpFile}"
imgSum="$(sha256sum "${tmpFile}")"
rm -f "${tmpFile}"
if [ "${imgSum}" == "${devSum}" ]; then
echo "0";
else
echo "1";
fi
}
# The eMMC partition labels for the kernel and rootfs are boot-a/b and rofs-a/b.
# Return the label (a or b) for the running partition.
function mmc_get_primary_label() {
# Get root device /dev/mmcblkpX
rootmatch=" on / "
root="$(mount | grep "${rootmatch}")"
# shellcheck disable=SC2295
root="${root%${rootmatch}*}"
# Find the device label
if [[ $(readlink -f /dev/disk/by-partlabel/rofs-a) == "${root}" ]]; then
echo "a"
elif [[ $(readlink -f /dev/disk/by-partlabel/rofs-b) == "${root}" ]]; then
echo "b"
else
echo ""
fi
}
# The eMMC partition labels for the kernel and rootfs are boot-a/b and rofs-a/b.
# Return the label (a or b) for the non-running partition.
function mmc_get_secondary_label() {
root="$(mmc_get_primary_label)"
if [[ "${root}" == "a" ]]; then
echo "b"
elif [[ "${root}" == "b" ]]; then
echo "a"
else
echo ""
fi
}
function mmc_mount() {
primaryId="$(mmc_get_primary_label)"
secondaryId="$(mmc_get_secondary_label)"
primaryDir="${mediaDir}/rofs-${primaryId}-functional"
secondaryDir="${mediaDir}/rofs-${secondaryId}"
mkdir -p "${primaryDir}"
mkdir -p "${secondaryDir}"
mount PARTLABEL=rofs-"${primaryId}" "${primaryDir}" -t ext4 -o ro || rmdir "${primaryDir}"
mount PARTLABEL=rofs-"${secondaryId}" "${secondaryDir}" -t ext4 -o ro || rmdir "${secondaryDir}"
}
function mmc_update() {
# Update u-boot if needed
bootPartition="mmcblk0boot0"
devUBoot="/dev/${bootPartition}"
imgUBoot="${imgpath}/${version}/image-u-boot"
if [ "$(cmp_uboot "${devUBoot}" "${imgUBoot}")" != "0" ]; then
echo 0 > "/sys/block/${bootPartition}/force_ro"
dd if="${imgUBoot}" of="${devUBoot}"
echo 1 > "/sys/block/${bootPartition}/force_ro"
fi
# Update the secondary (non-running) boot and rofs partitions.
label="$(mmc_get_secondary_label)"
# Update the boot and rootfs partitions, restore their labels after the update
# by getting the partition number mmcblk0pX from their label.
zstd -d -c "${imgpath}"/"${version}"/image-kernel | dd of="/dev/disk/by-partlabel/boot-${label}"
number="$(readlink -f /dev/disk/by-partlabel/boot-"${label}")"
number="${number##*mmcblk0p}"
sgdisk --change-name="${number}":boot-"${label}" /dev/mmcblk0 1>/dev/null
zstd -d -c "${imgpath}"/"${version}"/image-rofs | dd of="/dev/disk/by-partlabel/rofs-${label}"
number="$(readlink -f /dev/disk/by-partlabel/rofs-"${label}")"
number="${number##*mmcblk0p}"
sgdisk --change-name="${number}":rofs-"${label}" /dev/mmcblk0 1>/dev/null
# Run this after sgdisk for labels to take effect.
partprobe
# Update hostfw
if [ -f "${imgpath}"/"${version}"/image-hostfw ]; then
# Remove patches
patchdir="/usr/local/share/hostfw/alternate"
if [ -d "${patchdir}" ]; then
rm -rf "${patchdir:?}"/*
fi
hostfwdir=$(grep "hostfw " /proc/mounts | cut -d " " -f 2)
cp "${imgpath}"/"${version}"/image-hostfw "${hostfwdir}"/hostfw-"${label}"
mkdir -p "${hostfwdir}"/alternate
mount "${hostfwdir}"/hostfw-"${label}" "${hostfwdir}"/alternate -o ro
fi
set_flashid "${label}"
}
function mmc_remove() {
# Render the filesystem unbootable by wiping out the first 1MB, this
# invalidates the filesystem header.
# Check if the requested id is the one the BMC is running from since dd
# can still write and corrupt the running partition.
primaryid="$(mmc_get_primary_label)"
if [[ "${flashid}" == "${primaryid}" ]]; then
return 1
fi
dd if=/dev/zero of=/dev/disk/by-partlabel/boot-"${flashid}" count=2048
dd if=/dev/zero of=/dev/disk/by-partlabel/rofs-"${flashid}" count=2048
hostfw_alt="hostfw/alternate"
if grep -q "${hostfw_alt}" /proc/mounts; then
hostfw_alt=$(grep "${hostfw_alt}" /proc/mounts | cut -d " " -f 2)
umount "${hostfw_alt}"
fi
hostfw_base="hostfw "
if grep -q "${hostfw_base}" /proc/mounts; then
hostfw_base=$(grep "${hostfw_base}" /proc/mounts | cut -d " " -f 2)
rm -f "${hostfw_base}/hostfw-${flashid}"
fi
}
# Set the requested version as primary for the BMC to boot from upon reboot.
function mmc_setprimary() {
# Point root to the flashid of the requested BMC rootfs.
fw_setenv bootside "${flashid}"
}
function mmc_mirroruboot() {
# Get current boot device; 0-primary_bootdev device; 1 - alt_bootdev
bootdev=$(cat /sys/kernel/debug/aspeed/sbc/abr_image)
if [[ "${bootdev}" == "0" ]]; then
bootPartition="mmcblk0boot0"
alt_bootPartition="mmcblk0boot1"
else
bootPartition="mmcblk0boot1"
alt_bootPartition="mmcblk0boot0"
fi
devUBoot="/dev/${bootPartition}"
alt_devUBoot="/dev/${alt_bootPartition}"
checksum_UBoot="$(md5sum "${devUBoot}")"
checksum_UBoot="${checksum_UBoot% *}"
checksum_alt_UBoot="$(md5sum "${alt_devUBoot}")"
checksum_alt_UBoot="${checksum_alt% *}"
if [[ "${checksum_UBoot}" != "${checksum_alt_UBoot}" ]]; then
echo "Mirroring U-boot to alt chip"
echo 0 > "/sys/block/${alt_bootPartition}/force_ro"
dd if="${devUBoot}" of="${alt_devUBoot}"
echo 1 > "/sys/block/${alt_bootPartition}/force_ro"
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_ubi_alt_rwfs
;;
createenvbackup)
backup_env_vars
;;
updateubootvars)
flashid="$2"
update_env_vars
;;
rebootguardenable)
rebootguardenable
;;
rebootguarddisable)
rebootguarddisable
;;
mirroruboot)
mirroruboot
;;
mmc)
version="$2"
imgpath="$3"
mmc_update
;;
mmc-mount)
mediaDir="$2"
mmc_mount
;;
mmc-remove)
flashid="$2"
mmc_remove
;;
mmc-setprimary)
flashid="$2"
mmc_setprimary
;;
mmc-mirroruboot)
mmc_mirroruboot
;;
static-altfs)
mount_static_alt "$2" "$3" "$4"
;;
umount-static-altfs)
umount_static_alt "$2"
;;
*)
echo "Invalid argument"
exit 1
;;
esac