1#!/bin/bash 2set -eo pipefail 3 4# Get the root mtd device number (mtdX) from "/dev/ubiblockX_Y on /" 5findrootmtd() { 6 rootmatch=" on / " 7 m="$(mount | grep "${rootmatch}" | grep "ubiblock")" 8 m="${m##*ubiblock}" 9 m="${m%_*}" 10 if [ -z "${m}" ]; then 11 # default to bmc mtd (0) 12 m=0 13 fi 14 echo "mtd${m}" 15} 16 17findrootubi() { 18 rootmatch=" on / " 19 m="$(mount | grep "${rootmatch}")" 20 m="${m##*ubiblock}" 21 m="${m% on*}" 22 echo "ubi${m}" 23} 24 25# Get the mtd device number (mtdX) 26findmtd() { 27 m="$(grep -xl "$1" /sys/class/mtd/*/name)" 28 m="${m%/name}" 29 m="${m##*/}" 30 echo "${m}" 31} 32 33# Get the mtd device number only (return X of mtdX) 34findmtdnum() { 35 m="$(findmtd "$1")" 36 m="${m##mtd}" 37 echo "${m}" 38} 39 40# Get the ubi device number (ubiX_Y) 41findubi() { 42 u="$(grep -xl "$1" /sys/class/ubi/ubi?/subsystem/ubi*/name)" 43 u="${u%/name}" 44 u="${u##*/}" 45 echo "${u}" 46} 47 48# Get the ubi device number (ubiX_Y) on a specific mtd 49findubi_onmtd() { 50 u="$(grep -xl "$1" /sys/class/ubi/ubi"$2"/subsystem/ubi"$2"*/name)" 51 u="${u%/name}" 52 u="${u##*/}" 53 echo "${u}" 54} 55 56# Get all ubi device names on a specific mtd that match requested string 57findubiname_onmtd() { 58 u="$(grep -h "$1" /sys/class/ubi/ubi"$2"/subsystem/ubi"$2"*/name)" 59 u="${u%/name}" 60 u="${u##*/}" 61 echo "${u}" 62} 63 64# Get the name from the requested ubiX_Y volume 65findname() { 66 n="$(cat /sys/class/ubi/$1/name)" 67 echo "${n}" 68} 69 70# Set the u-boot envs that perform a side switch on failure to boot 71set_wdt2bite() { 72 if ! fw_printenv wdt2bite 2>/dev/null; then 73 fw_setenv wdt2bite "mw.l 0x1e785024 0xa 1; mw.b 0x1e78502c 0xb3 1" 74 fw_setenv bootalt "run wdt2bite" 75 fw_setenv obmc_bootcmd "ubi part obmc-ubi; run do_rwreset; ubi read \ 76\${loadaddr} \${kernelname}; bootm \${loadaddr} || run bootalt" 77 fi 78} 79 80# Make space on flash before creating new volumes. This can be enhanced 81# determine current flash usage. For now only keep a "keepmax" number of them 82ubi_remove_volumes() 83{ 84 rootubi="$(findrootubi)" 85 rootname="$(findname "${rootubi}")" 86 rootversion="${rootname##*-}" 87 rootkernel="kernel-${rootversion}" 88 89 # Just keep max number of volumes before updating, don't delete the version 90 # the BMC is booted from, and when a version is identified to be deleted, 91 # delete both the rofs and kernel volumes for that version. 92 rmnames="$(findubiname_onmtd "${name%-*}-" "${ro}")" 93 rmnames=(${rmnames}) 94 ubicount="${#rmnames[@]}" 95 while [ ${ubicount} -ge ${keepmax} ]; do 96 # Loop through existing volumes and skip currently active ones 97 for (( index=0; index<${#rmnames[@]}; index++ )); do 98 rmname="${rmnames[${index}]}" 99 rmversion="${rmname##*-}" 100 [ "${rmversion}" == "${version}" ] && continue 101 rmubi="$(findubi_onmtd "rofs-${rmversion}" "${ro}")" 102 if [[ ( "${rmubi}" != "${rootubi}" ) && 103 ( "${rmname}" != "${rootkernel}" ) ]]; then 104 ubi_remove "rofs-${rmversion}" "${ro}" 105 ubi_remove "kernel-${rmversion}" "${ro}" 106 # Remove priority value 107 fw_setenv "${rmversion}" 108 break 109 fi 110 done 111 # Decrease count regardless to avoid an infinite loop 112 (( ubicount-- )) 113 done 114} 115 116ubi_rw() { 117 rwmtd="$(findmtd "${reqmtd}")" 118 rw="${rwmtd#mtd}" 119 ubidev="/dev/ubi${rw}" 120 121 # Update rwfs_size, check imgsize was specified, otherwise it'd clear the var 122 if [ ! -z "$imgsize" ]; then 123 rwsize="$(fw_printenv -n rwfs_size 2>/dev/null)" || true 124 if [[ "${imgsize}" != "${rwsize}" ]]; then 125 fw_setenv rwfs_size "${imgsize}" 126 fi 127 fi 128 129 vol="$(findubi "${name}")" 130 if [ -z "${vol}" ]; then 131 ubimkvol "${ubidev}" -N "${name}" -s "${imgsize}" 132 fi 133} 134 135ubi_ro() { 136 keepmax=2 # Default 2 volumes per mtd 137 romtd="$(findmtd "${reqmtd}")" 138 romtd2="$(findmtd "${reqmtd2}")" 139 140 if [ ! "${romtd}" == "${romtd2}" ]; then 141 # Request to use alternate mtd device, choose the non-root one 142 keepmax=1 # 1 volume on each of the requested mtds 143 rootmtd="$(findrootmtd)" 144 if [ "${rootmtd}" == "${romtd}" ]; then 145 romtd="${romtd2}" 146 fi 147 fi 148 ro="${romtd#mtd}" 149 ubidev="/dev/ubi${ro}" 150 151 ubi_remove_volumes 152 153 if [ -z "${imgfile}" ]; then 154 echo "Unable to create read-only volume. Image file not specified." 155 return 1 156 fi 157 158 # Create a ubi volume, dynamically sized to fit BMC image if size unspecified 159 img="/tmp/images/${version}/${imgfile}" 160 imgsize="$(stat -c '%s' ${img})" 161 162 vol="$(findubi "${name}")" 163 if [ ! -z "${vol}" ]; then 164 # Allow a duplicate kernel volume on the alt mtd 165 if [[ "${name}" =~ "kernel" ]]; then 166 vol="$(findubi_onmtd "${name}" "${ro}")" 167 fi 168 fi 169 if [ -z "${vol}" ]; then 170 ubimkvol "${ubidev}" -N "${name}" -s "${imgsize}" --type=static 171 vol="$(findubi "${name}")" 172 fi 173} 174 175# Squashfs images need a ubi block 176ubi_block() { 177 vol="$(findubi "${name}")" 178 ubidevid="${vol#ubi}" 179 block="/dev/ubiblock${ubidevid}" 180 if [ ! -e "$block" ]; then 181 ubiblock --create "/dev/ubi${ubidevid}" 182 fi 183} 184 185ubi_updatevol() { 186 vol="$(findubi "${name}")" 187 ubidevid="${vol#ubi}" 188 img="/tmp/images/${version}/${imgfile}" 189 ubiupdatevol "/dev/ubi${ubidevid}" "${img}" 190} 191 192ubi_remove() { 193 rmname="$1" 194 rmmtd="$2" 195 if [ ! -z "${rmmtd}" ]; then 196 vol="$(findubi_onmtd "${rmname}" "${rmmtd}")" 197 else 198 vol="$(findubi "${rmname}")" 199 fi 200 201 if [ ! -z "$vol" ]; then 202 vol="${vol%_*}" 203 204 if grep -q "$rmname" /proc/mounts; then 205 mountdir=$(grep "$rmname" /proc/mounts | cut -d " " -f 2) 206 umount "$mountdir" 207 rm -r "$mountdir" 208 fi 209 210 ubirmvol "/dev/${vol}" -N "$rmname" 211 fi 212} 213 214ubi_cleanup() { 215 # When ubi_cleanup is run, it expects one or no active version. 216 activeVersion=$(busctl --list --no-pager tree \ 217 xyz.openbmc_project.Software.BMC.Updater | \ 218 grep /xyz/openbmc_project/software/ | tail -c 9) 219 220 if [[ -z "$activeVersion" ]]; then 221 vols=$(ubinfo -a | grep "rofs-" | cut -c 14-) 222 vols=(${vols}) 223 else 224 vols=$(ubinfo -a | grep "rofs-" | \ 225 grep -v "$activeVersion" | cut -c 14-) 226 vols=(${vols}) 227 fi 228 229 for (( index=0; index<${#vols[@]}; index++ )); do 230 ubi_remove ${vols[index]} 231 done 232} 233 234mount_alt_rwfs() { 235 altNum="$(findmtdnum "alt-bmc")" 236 if [ ! -z "${altNum}" ]; then 237 altRwfs=$(ubinfo -a -d ${altNum} | grep -w "rwfs") || true 238 if [ ! -z "${altRwfs}" ]; then 239 altVarMount="/media/alt/var" 240 mkdir -p "${altVarMount}" 241 if mount ubi"${altNum}":rwfs "${altVarMount}" -t ubifs -o defaults; then 242 mkdir -p "${altVarMount}"/persist/etc 243 fi 244 fi 245 fi 246} 247 248remount_ubi() { 249 bmcmtd="$(findmtd "bmc")" 250 altbmcmtd="$(findmtd "alt-bmc")" 251 mtds="${bmcmtd: -1}","${altbmcmtd: -1}" 252 253 IFS=',' read -r -a mtds <<< "$mtds" 254 mtds=($(echo "${mtds[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) 255 for mtd in ${mtds[@]}; do 256 # Get information on all ubi volumes 257 ubinfo=$(ubinfo -d ${mtd}) 258 presentVolumes=${ubinfo##*:} 259 IFS=', ' read -r -a array <<< "$presentVolumes" 260 for element in ${array[@]}; do 261 elementProperties=$(ubinfo -d $mtd -n $element) 262 # Get ubi volume name by getting rid of additional properties 263 name=${elementProperties#*Name:} 264 name="${name%Character*}" 265 name="$(echo -e "${name}" | tr -d '[:space:]')" 266 267 if [[ ${name} == rofs-* ]]; then 268 mountdir="/media/${name}" 269 270 if [ ! -d ${mountdir} ]; then 271 mkdir -p "${mountdir}" 272 # U-Boot will create the ubiblock for the running version, but not 273 # for the version on the other chip 274 if [ ! -e "/dev/ubiblock${mtd}_${element}" ]; then 275 ubiblock --create /dev/ubi${mtd}_${element} 276 fi 277 mount -t squashfs -o ro "/dev/ubiblock${mtd}_${element}" "${mountdir}" 278 fi 279 fi 280 done 281 done 282 283 set_wdt2bite 284} 285 286# Read the current env variable and set it on the alternate boot env 287copy_env_var_to_alt() { 288 varName=$1 289 value="$(fw_printenv -n "${varName}")" 290 fw_setenv -c /etc/alt_fw_env.config "${varName}" "${value}" 291} 292 293# When the alternate bmc chip boots, u-boot thinks its the primary mtdX. 294# Therefore need to swap the chip numbers when copying the ubiblock and root to 295# alternate bmc u-boot environment. 296copy_ubiblock_to_alt() { 297 value="$(fw_printenv -n ubiblock)" 298 bmcNum="$(findmtdnum "bmc")" 299 altNum="$(findmtdnum "alt-bmc")" 300 replaceAlt="${value/${altNum},/${bmcNum},}" 301 302 if [[ "${value}" == "${replaceAlt}" ]]; then 303 replaceBmc="${value/${bmcNum},/${altNum},}" 304 value=${replaceBmc} 305 else 306 value=${replaceAlt} 307 fi 308 309 fw_setenv -c /etc/alt_fw_env.config ubiblock "${value}" 310} 311 312copy_root_to_alt() { 313 value="$(fw_printenv -n root)" 314 bmcNum="$(findmtdnum "bmc")" 315 altNum="$(findmtdnum "alt-bmc")" 316 replaceAlt="${value/${altNum}_/${bmcNum}_}" 317 318 if [[ "${value}" == "${replaceAlt}" ]]; then 319 replaceBmc="${value/${bmcNum}_/${altNum}_}" 320 value=${replaceBmc} 321 else 322 value=${replaceAlt} 323 fi 324 325 fw_setenv -c /etc/alt_fw_env.config root "${value}" 326} 327 328ubi_setenv() { 329 # The U-Boot environment maintains two banks of environment variables. 330 # The banks need to be consistent with each other to ensure that these 331 # variables can reliably be read from file. In order to guarantee that the 332 # banks are both correct, we need to run fw_setenv twice. 333 variable=$1 334 if [[ "$variable" == *"="* ]]; then 335 varName="${variable%=*}" 336 value="${variable##*=}" 337 # Write only if var is not set already to the requested value 338 currentValue="$(fw_printenv -n "${varName}" 2>/dev/null)" || true 339 if [[ "${currenValue}" != "${value}" ]]; then 340 fw_setenv "$varName" "$value" 341 fw_setenv "$varName" "$value" 342 fi 343 else 344 fw_setenv "$variable" 345 fw_setenv "$variable" 346 fi 347} 348 349mtd_write() { 350 flashmtd="$(findmtd "${reqmtd}")" 351 img="/tmp/images/${version}/${imgfile}" 352 flashcp -v ${img} /dev/${flashmtd} 353} 354 355backup_env_vars() { 356 copy_env_var_to_alt kernelname 357 copy_ubiblock_to_alt 358 copy_root_to_alt 359} 360 361update_env_vars() { 362 vol="$(findubi rofs-"${version}")" 363 if [ -z "${vol}" ]; then 364 return 1 365 fi 366 ubidevid="${vol#ubi}" 367 block="/dev/ubiblock${ubidevid}" 368 if [ ! -e "${block}" ]; then 369 return 1 370 fi 371 ubi_setenv "kernelname=kernel-${version}" 372 ubi_setenv "ubiblock=$(echo "${ubidevid}" | sed 's/_/,/')" 373 ubi_setenv "root=${block}" 374} 375 376#TODO: Replace the implementation with systemd-inhibitors lock 377# once systemd/systemd#949 is resolved 378rebootguardenable() { 379 dir="/run/systemd/system/" 380 file="reboot-guard.conf" 381 units=("reboot" "poweroff" "halt") 382 383 for unit in "${units[@]}"; do 384 mkdir -p ${dir}${unit}.target.d 385 echo -e "[Unit]\nRefuseManualStart=yes" >> ${dir}${unit}.target.d/${file} 386 done 387} 388 389#TODO: Replace the implementation with systemd-inhibitors lock 390# once systemd/systemd#949 is resolved 391rebootguarddisable() { 392 dir="/run/systemd/system/" 393 file="reboot-guard.conf" 394 units=("reboot" "poweroff" "halt") 395 396 for unit in "${units[@]}"; do 397 rm -rf ${dir}${unit}.target.d/${file} 398 done 399} 400 401# Create a copy in the alt mtd 402create_vol_in_alt() { 403 alt="alt-bmc" 404 altmtd="$(findmtd "${alt}")" 405 if [ ! -z "${altmtd}" ]; then 406 reqmtd="${alt}" 407 reqmtd2="${alt}" 408 ubi_ro 409 ubi_updatevol 410 fi 411} 412 413# Copy contents of one MTD device to another 414mtd_copy() { 415 in=$1 416 out=$2 417 418 # Must erase MTD first to prevent corruption 419 flash_eraseall "${out}" 420 dd if="${in}" of="${out}" 421} 422 423mirroruboot() { 424 bmc="$(findmtd "u-boot")" 425 bmcdev="/dev/${bmc}" 426 alt="$(findmtd "alt-u-boot")" 427 altdev="/dev/${alt}" 428 429 checksum_bmc="$(md5sum "${bmcdev}")" 430 checksum_bmc="${checksum_bmc% *}" 431 checksum_alt="$(md5sum "${altdev}")" 432 checksum_alt="${checksum_alt% *}" 433 434 if [[ "${checksum_bmc}" != "${checksum_alt}" ]]; then 435 bmcenv="$(findmtd "u-boot-env")" 436 bmcenvdev="/dev/${bmcenv}" 437 altenv="$(findmtd "alt-u-boot-env")" 438 altenvdev="/dev/${altenv}" 439 440 echo "Mirroring U-boot to alt chip" 441 mtd_copy "${bmcdev}" "${altdev}" 442 mtd_copy "${bmcenvdev}" "${altenvdev}" 443 444 copy_ubiblock_to_alt 445 copy_root_to_alt 446 fi 447} 448 449case "$1" in 450 mtduboot) 451 reqmtd="$2" 452 version="$3" 453 imgfile="image-u-boot" 454 mtd_write 455 ;; 456 ubirw) 457 reqmtd="$2" 458 name="$3" 459 imgsize="$4" 460 ubi_rw 461 ;; 462 ubiro) 463 reqmtd="$(echo "$2" | cut -d "+" -f 1)" 464 reqmtd2="$(echo "$2" | cut -d "+" -f 2)" 465 name="$3" 466 version="$4" 467 imgfile="image-rofs" 468 ubi_ro 469 ubi_updatevol 470 ubi_block 471 ;; 472 ubikernel) 473 reqmtd="$(echo "$2" | cut -d "+" -f 1)" 474 reqmtd2="$(echo "$2" | cut -d "+" -f 2)" 475 name="$3" 476 version="$4" 477 imgfile="image-kernel" 478 ubi_ro 479 ubi_updatevol 480 create_vol_in_alt 481 ;; 482 ubiremove) 483 name="$2" 484 ubi_remove "${name}" 485 ;; 486 ubicleanup) 487 ubi_cleanup 488 ;; 489 ubisetenv) 490 ubi_setenv "$2" 491 ;; 492 ubiremount) 493 remount_ubi 494 mount_alt_rwfs 495 ;; 496 createenvbackup) 497 backup_env_vars 498 ;; 499 updateubootvars) 500 version="$2" 501 update_env_vars 502 ;; 503 rebootguardenable) 504 rebootguardenable 505 ;; 506 rebootguarddisable) 507 rebootguarddisable 508 ;; 509 mirroruboot) 510 mirroruboot 511 ;; 512 *) 513 echo "Invalid argument" 514 exit 1 515 ;; 516esac 517