#!/bin/sh # Copyright (c) 2013 International Business Machines # Common Public License Version 1.0 (see COPYRIGHT) # # Authors: Vasant Hegde # # Simple script for code update on "KVM on Power" machines. This # is a simple wrapper script to pass the image. The Linux kernel # and FW does the real work during system reboot. # # This script has minimal dependencies so it can operate in a # rescue environment. #set -x # Error codes E_SUCCESS=0 # Success E_UNSUPPORTED=1 # Firmware update is not supported E_USAGE=3 # Usage error E_PERM=4 # Permission error E_IMAGE=5 # Image file error E_SYS_FS=6 # Firmware update related sysfs file doesn't exist E_MODULE=7 # Error loading module E_OPAL=8 # OPAL call failed E_USER=9 # User aborted operation E_OVERWRITE=10 # Auto overwrite permanent side image E_WRNTY=15 # Update Access Key Expired # Firmware update related files SYS_IMAGE_FILE=/sys/firmware/opal/image SYS_VALIDATE_FLASH=/sys/firmware/opal/validate_flash SYS_MANAGE_FLASH=/sys/firmware/opal/manage_flash SYS_UPDATE_FLASH=/sys/firmware/opal/update_flash # Code update status values FLASH_SUCCESS=0 # Success FLASH_PARAM_ERR=-1 # Parameter error FLASH_BUSY=-2 # OPAL busy FLASH_HW_ERR=-6 # Hardware error FLASH_INTERNAL_ERR=-11 # Internal error FLASH_NO_OP=-1099 # No operation initiated by user FLASH_NO_AUTH=-9002 # Inband firmware update is not allowed # Validate image status values FLASH_IMG_READY=-1001 # Image ready for validation FLASH_IMG_INCOMPLETE=-1002 # User copied < VALIDATE_BUF_SIZE # Manage image status values FLASH_ACTIVE_ERR=-9001 # Cannot overwrite active img # Flash image status values FLASH_IMG_READY=0 # Image ready for flash on reboot FLASH_INVALID_IMG=-1003 # Flash image shorter than expected FLASH_IMG_NULL_DATA=-1004 # Bad data FLASH_IMG_BAD_LEN=-1005 # Bad length # Validate image update result tokens # # T side will be updated VALIDATE_TMP_UPDATE=0 # # Partition does not have authority VALIDATE_FLASH_AUTH=1 # # Candidate image is not valid for this platform VALIDATE_INVALID_IMG=2 # # Current fixpack level is unknown VALIDATE_CUR_UNKNOWN=3 # # Current T side will be committed to P side before being replace # with new image, and the new image is downlevel from current image VALIDATE_TMP_COMMIT_DL=4 # # Current T side will be committed to P side before being replaced # with new image VALIDATE_TMP_COMMIT=5 # # T side will be updated with a downlevel image VALIDATE_TMP_UPDATE_DL=6 # # The candidate image's release date is later than the system's Update # Access Key Expiration date - service warranty period has expired VALIDATE_OUT_OF_WRNTY=7 error() { local exit_code=$1 if [ $# -lt 1 ]; then echo "error(): usage." >&2 return $E_USAGE fi shift; echo update_flash: $* >&2 exit $exit_code } usage() { local exit_code; if [ "$1" == $E_SUCCESS ]; then exit_code=$E_SUCCESS else exit_code=$E_USAGE fi echo "USAGE: update_flash {-h | -s | -r | -c | [-v|-n] -f }" >&2 echo " -h Print this message." >&2 echo " -s Determine if partition has access to" >&2 echo " perform flash image management." >&2 echo " -r Reject temporary image." >&2 echo " -c Commit temporary image." >&2 echo " -v Validate the given image file." >&2 echo " -n Do not overwrite Permanent side" >&2 echo " image automatically." >&2 echo " -f Update with given image file. If possible," >&2 echo " the image is automatically validated prior" >&2 echo " to update." >&2 echo "" >&2 exit $exit_code } # Validate sysfs interface validate_sysfs_file() { local file="$1" if [ -r "$file" ]; then return $E_SUCCESS fi error $E_SYS_FS "sysfs interface for firmware update does not exists." } # Copy image to sysfs file copy_candidate_image() { local img_file=$1 [ $# -eq 1 ] || error $E_USAGE "copy_candidate_image(): usage." [ -r "$img_file" ] || error $E_IMAGE "Cannot read ${img_file}." # Copy candidate image dd if=$img_file of=$SYS_IMAGE_FILE 2>/dev/null if [ $? -ne 0 ]; then echo "update_flash: Error copying firmware image." error $E_IMAGE "Please retry with valid firmware image." fi } echo_opal_return_status() { case "$1" in $FLASH_PARAM_ERR) error $E_OPAL "Parameter Error.";; $FLASH_BUSY) error $E_OPAL "OPAL Busy.";; $FLASH_HW_ERR) error $E_OPAL "Hardware error.";; $FLASH_INTERNAL_ERR) error $E_OPAL "OPAL internal error.";; $FLASH_NO_AUTH) error $E_PERM "System does not have authority to perform firmware update.";; $FLASH_IMG_INCOMPLETE) error $E_IMAGE "Invalid candidate image.";; $FLASH_ACTIVE_ERR) error $E_OVERWRITE "Cannot Overwrite the Active Firmware Image.";; $FLASH_INVALID_IMG) error $E_IMAGE "Invalid candidate image.";; $FLASH_IMG_NULL_DATA) error $E_IMAGE "Bad data value in flash list block.";; $FLASH_IMG_BAD_LEN) error $E_IMAGE "Bad length value in flash list block.";; *) error $E_OPAL "Unknown return status.";; esac } # Determine if partition has access to perform flash image management query_flash_support() { # Validate sysfs interface validate_sysfs_file $SYS_IMAGE_FILE # By default KVM on Power host is allowed to do firmware management echo "update_flash: Firmware image management is supported." exit $E_SUCCESS } echo_validate_buf() { local output="$1" local cur_t=$(echo "$output" | grep "^MI" | head -n 1 | awk ' { print $2 } ') local cur_p=$(echo "$output" | grep "^MI" | head -n 1 | awk ' { print $3 } ') local new_t=$(echo "$output" | grep "^MI" | tail -n 1 | awk ' { print $2 } ') local new_p=$(echo "$output" | grep "^MI" | tail -n 1 | awk ' { print $3 } ') echo "Projected Flash Update Results:" echo "Current T Image: $cur_t" echo "Current P Image: $cur_p" echo "New T Image: $new_t" echo "New P Image: $new_p" } echo_validate_return_status() { local output="$1" local rc=$(echo "$output" | head -n 1) local opal_buf=$(echo "$output" | tail -n +2) [ $# -eq 1 ] || error $E_USAGE "echo_validate_return_status(): usage." if [ $rc -lt 0 ]; then echo_opal_return_status $rc fi # Validation result case "$rc" in $VALIDATE_TMP_UPDATE) echo -n "info: Temporary side will be updated with a newer or" echo " identical image.";; $VALIDATE_FLASH_AUTH) error $E_OPAL "System does not have authority.";; $VALIDATE_INVALID_IMG) error $E_OPAL "Invalid candidate image for this platform.";; $VALIDATE_CUR_UNKNOWN) echo "info: Current fixpack level is unknown.";; $VALIDATE_TMP_COMMIT_DL) echo "info: Current Temporary image will be committed to" echo "Permanent side before being replaced with new image," echo "and the new image is downlevel from current image.";; $VALIDATE_TMP_COMMIT) echo "info: Current Temporary side will be committed to" echo "Permanent side before being replaced with the new" echo "image.";; $VALIDATE_TMP_UPDATE_DL) echo "info: Temporary side will be updated with a downlevel image.";; *) error $E_OPAL "Unknown return status." esac echo echo_validate_buf "$opal_buf" # Do not commit T side image to P side if [ $no_overwrite_opt -eq 1 ]; then if [ $rc -eq $VALIDATE_TMP_COMMIT_DL ] || [ $rc -eq $VALIDATE_TMP_COMMIT ]; then echo "" echo "update_flash: Run without -n option to flash new image." exit $E_OVERWRITE fi fi } validate_flash() { local output="" # Validate candidate image echo 1 > $SYS_VALIDATE_FLASH 2>/dev/null # Display appropriate message, exiting if necessary output="$(cat $SYS_VALIDATE_FLASH)" echo_validate_return_status "$output" } validate_flash_from_file() { local img_file=$1 [ $# -eq 1 ] || error $E_USAGE "validate_flash_from_file(): usage." # Validate sysfs interface validate_sysfs_file $SYS_VALIDATE_FLASH # Copy candiadate image copy_candidate_image $img_file # Validate candidate image validate_flash exit $E_SUCCESS } echo_update_return_status() { local rc="$1" [ $# -eq 1 ] || error $E_USAGE "echo_update_return_status(): usage." if [ $rc -lt 0 ]; then echo_opal_return_status $rc elif [ $rc -eq $FLASH_IMG_READY ]; then echo echo "FLASH: Image ready...rebooting the system..." echo "FLASH: This will take several minutes." echo "FLASH: Do not power off!" else error $E_SYS_FS "Unknown return status." fi } update_flash_from_file() { local img_file=$1 local output="" [ $# -eq 1 ] || error $E_USAGE "update_flash_from_file(): usage." # Validate sysfs interface validate_sysfs_file $SYS_UPDATE_FLASH # Copy candidate image copy_candidate_image $img_file # Validate candidate image validate_flash # Update image echo 1 > $SYS_UPDATE_FLASH 2>/dev/null output="$(cat $SYS_UPDATE_FLASH)" echo_update_return_status "$output" # Reboot system, so that we can flash new image reboot -f exit $E_SUCCESS } echo_manage_return_status() { local is_commit=$1 local output=$2 local rc=$(echo $output) [ $# -eq 2 ] || error $E_USAGE "echo_manage_return_status(): usage." if [ $rc -lt 0 ]; then echo_opal_return_status $rc elif [ $rc -eq $FLASH_SUCCESS ]; then if [ $is_commit -eq 0 ]; then echo "Success: Rejected temporary firmware image." else echo "Success: Committed temporary firmware image." fi else error $E_OPAL "Unknown return status." fi } manage_flash() { local is_commit=$1 local commit_str="1" local reject_str="0" local output="" [ $# -eq 1 ] || error $E_USAGE "manage_flash(): usage." # Validate sysfs interface validate_sysfs_file $SYS_MANAGE_FLASH # Commit operation if [ $is_commit -eq 1 ]; then echo $commit_str > $SYS_MANAGE_FLASH else echo $reject_str > $SYS_MANAGE_FLASH fi # Result output=$(cat $SYS_MANAGE_FLASH) echo_manage_return_status $is_commit "$output" exit $E_SUCCESS } file="" check_opt=0 commit_opt=0 reject_opt=0 validate_opt=0 no_overwrite_opt=0 file_opt=0 # Only root user can perform firmware update [ "`whoami`" == "root" ] || error $E_PERM "Must be root to execute this command." # Parse command line options while [ -n "$1" ]; do arg="$1" shift case "$arg" in -q|-l|-D|-S) error $E_USAGE "The $arg option is not implemented.";; -h) usage $E_SUCCESS;; -s) check_opt=1;; -c) commit_opt=1;; -r) reject_opt=1;; -v) validate_opt=1;; -n) no_overwrite_opt=1;; -f) file_opt=1; file="$1"; shift;; *) error $E_USAGE "Unknown option ${arg}." esac done if [ -n "$file" ]; then if [ $commit_opt -eq 1 ] || [ $reject_opt -eq 1 ] || [ $check_opt -eq 1 ]; then usage elif [ $validate_opt -eq 1 ] && [ $no_overwrite_opt -eq 1 ]; then usage elif [ $validate_opt -eq 1 ]; then validate_flash_from_file $file else update_flash_from_file $file fi else if [ $check_opt -eq 1 ]; then if [ $commit_opt -eq 1 ] || [ $reject_opt -eq 1 ]; then usage else query_flash_support fi fi [ $commit_opt -eq 0 ] && [ $reject_opt -eq 0 ] && usage [ $commit_opt -eq 1 ] && [ $reject_opt -eq 1 ] && usage manage_flash $commit_opt fi