| #!/bin/sh |
| |
| # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| # |
| # Chrome OS Disk Firmware Update Script |
| # This script checks whether if the root device needs to be upgraded. |
| # |
| |
| . /usr/share/misc/shflags |
| . /usr/share/misc/chromeos-common.sh |
| |
| # Temporary directory to put device information |
| DEFINE_string 'tmp_dir' '' "Use existing temporary directory." |
| DEFINE_string 'fw_package_dir' '' "Location of the firmware package." |
| DEFINE_string 'hdparm' 'hdparm' "hdparm binary to use." |
| DEFINE_string 'hdparm_kingston' '/opt/google/disk/bin/hdparm_kingston' \ |
| "hdparm for kingston recovery." |
| DEFINE_string 'smartctl' 'smartctl' "smartctl binary to use." |
| DEFINE_string 'pwr_suspend' 'powerd_dbus_suspend' "To power cycle SSD" |
| DEFINE_string 'mmc' 'mmc' "mmc binary to use." |
| DEFINE_string 'nvme' 'nvme' "nvme binary to use." |
| DEFINE_string 'status' '' "Status file to write to." |
| DEFINE_boolean 'test' ${FLAGS_FALSE} "For unit testing." |
| |
| # list global variables |
| # disk_model |
| # disk_fw_rev |
| # disk_fw_file |
| # disk_exp_fw_rev |
| # disk_fw_opt |
| # nvme_out : A file where the output of "nvme id-ctrl" is stored. |
| |
| log_msg() { |
| logger -t "chromeos-disk-firmware-update[${PPID}]" "$@" |
| echo "$@" |
| } |
| |
| die() { |
| log_msg "error: $*" |
| exit 1 |
| } |
| |
| # Parse command line |
| FLAGS "$@" || exit 1 |
| eval set -- "${FLAGS_ARGV}" |
| |
| # program_installed - check if the specified program is installed. |
| program_installed() { |
| if ! command -v "$1" > /dev/null; then |
| log_msg "$1 is not installed" |
| return 1 |
| fi |
| } |
| # disk_fw_select - Select the proper disk firmware to use. |
| # |
| # This code reuse old installer disk firmware upgrade code. |
| # |
| # inputs: |
| # disk_rules -- the file containing the list of rules. |
| # disk_model -- the model from hdparm -I |
| # disk_fw_rev -- the firmware version of the device. |
| # |
| # outputs: |
| # disk_fw_file -- name of the DISK firmware image file for this machine |
| # disk_exp_fw_rev -- the revision code of the firmware |
| # disk_fw_opt -- the options for this update |
| # |
| disk_fw_select() { |
| local disk_rules="$1" |
| local rule_model |
| local rule_fw_rev |
| local rule_exp_fw_rev |
| local rule_fw_opt |
| local rule_fw_file |
| disk_fw_file="" |
| disk_exp_fw_rev="" |
| disk_fw_opt="" |
| |
| # Check for obvious misconfiguration problems: |
| if [ -z "${disk_rules}" ]; then |
| log_msg "Warning: disk_rules not specified" |
| return 1 |
| fi |
| if [ ! -r "${disk_rules}" ]; then |
| log_msg "Warning: cannot read config file ${disk_rules}" |
| return 1 |
| fi |
| |
| # Read through the config file, looking for matches: |
| while read -r rule_model rule_fw_rev rule_exp_fw_rev rule_fw_opt rule_fw_file; do |
| if [ -z "${rule_fw_file}" ]; then |
| log_msg "${disk_rules}: incorrect number of items in file" |
| continue |
| fi |
| |
| # Check for match: |
| if [ "${disk_model}" != "${rule_model}" ]; then |
| continue |
| fi |
| if [ "${disk_fw_rev}" != "${rule_fw_rev}" ]; then |
| continue |
| fi |
| disk_exp_fw_rev="${rule_exp_fw_rev}" |
| disk_fw_opt="${rule_fw_opt}" |
| disk_fw_file="${rule_fw_file}" |
| done < "${disk_rules}" |
| |
| # If we got here, then no DISK firmware matched. |
| if [ -z "${disk_fw_file}" ]; then |
| return 1 |
| else |
| return 0 |
| fi |
| } |
| |
| # disk_hdparm_info - Shim for calling hdparm |
| # |
| # Useful for testing overide. |
| # |
| # inputs: |
| # device -- the device name [sda,...] |
| # |
| # echo the output of hdparm. |
| # |
| disk_hdparm_info() { |
| local device="$1" |
| |
| # Test if we have the tool needed for an upgrade. |
| if ! program_installed "${FLAGS_hdparm}"; then |
| return 1 |
| fi |
| |
| # use -I option to be sure the drive is accessed: |
| # will fail if the drive is not up |
| # sure that the firmware version is up to date if the |
| # disk upgrade without reset. |
| "${FLAGS_hdparm}" -I "/dev/${device}" |
| } |
| |
| # disk_nvme_id_info - Shim for calling nvme id-ctrl command |
| # |
| # Useful for testing overide. |
| # |
| # inputs: |
| # device -- the device name [nvme0,...] |
| # |
| # echo the output of nvme id-ctrl. |
| # |
| disk_nvme_id_info() { |
| local device="$1" |
| |
| "${FLAGS_nvme}" id-ctrl "/dev/${device}" |
| } |
| |
| #disk_mmc_info - Retrieve disk information for MMC device |
| # |
| # inputs: |
| # device -- the device name [mmcblk0] |
| # |
| # outputs: |
| # disk_model -- model of the device |
| # disk_fw_rev -- actual firmware revision on the device |
| # |
| # returns non 0 on error |
| # |
| disk_mmc_info() { |
| # Some vendor use hexa decimal character for indentification. |
| disk_model="$(cat "/sys/block/$1/device/cid" | cut -c 7-18)" |
| disk_fw_rev="$(cat "/sys/block/$1/device/fwrev")" |
| |
| # Test if we have the tool needed for an upgrade. |
| if ! program_installed "${FLAGS_mmc}"; then |
| return 1 |
| fi |
| |
| if [ -z "${disk_model}" -o -z "${disk_fw_rev}" ]; then |
| return 1 |
| fi |
| return 0 |
| } |
| |
| # disk_ata_info - Retrieve disk information for ata device |
| # |
| # inputs: |
| # device -- the device name [sda] |
| # |
| # outputs: |
| # disk_model -- model of the device |
| # disk_fw_rev -- actual firmware revision on the device |
| # |
| # returns non 0 on error |
| # |
| disk_ata_info() { |
| local device="$1" |
| local rc=0 |
| local hdparm_out="${FLAGS_tmp_dir}/${device}" |
| |
| disk_model="" |
| disk_fw_rev="" |
| disk_hdparm_info "${device}" > "${hdparm_out}" |
| rc=$? |
| if [ ${rc} -ne 0 ]; then |
| return ${rc} |
| fi |
| if [ ! -s "${hdparm_out}" ]; then |
| log_msg "hdparm did not produced any output" |
| return 1 |
| fi |
| disk_model=$(sed -nEe \ |
| '/^\t+Model/s|\t+Model Number: +(.*)|\1|p' "${hdparm_out}" \ |
| | sed -re 's/ +$//' -e 's/[ -]/_/g') |
| disk_fw_rev=$(sed -nEe \ |
| '/^\t+Firmware/s|\t+Firmware Revision: +(.*)|\1|p' "${hdparm_out}" \ |
| | sed -re 's/ +$//' -e 's/[ -]/_/g') |
| if [ -z "${disk_model}" -o -z "${disk_fw_rev}" ]; then |
| return 1 |
| fi |
| return 0 |
| } |
| |
| # disk_nvme_info - Retrieve disk information for NMVe device |
| # |
| # inputs: |
| # device -- the device name [nvme0] |
| # |
| # outputs: |
| # disk_model -- model of the device |
| # disk_fw_rev -- actual firmware revision on the device |
| # |
| # returns non 0 on error |
| # |
| disk_nvme_info() { |
| local device="$1" |
| local rc=0 |
| nvme_out="${FLAGS_tmp_dir}/${device}" |
| |
| # Test if we have the tool needed for an upgrade. |
| if ! program_installed "${FLAGS_mmc}"; then |
| return 1 |
| fi |
| |
| # Use -I option to be sure the drive is accessed. |
| disk_model="" |
| disk_fw_rev="" |
| disk_nvme_id_info "${device}" > "${nvme_out}" |
| rc=$? |
| if [ ${rc} -ne 0 ]; then |
| return ${rc} |
| fi |
| if [ ! -s "${nvme_out}" ]; then |
| log_msg "nvme did not produced any output" |
| return 1 |
| fi |
| disk_model=$(sed -nEe '/^mn +:/s|[^:]*: +(.*)|\1|p' "${nvme_out}" \ |
| | sed -re 's/ +$//' -e 's/[ -]/_/g') |
| disk_fw_rev=$(sed -nEe '/^fr +:/s|[^:]*: +(.*)|\1|p' "${nvme_out}" \ |
| | sed -re 's/ +$//' -e 's/[ -]/_/g') |
| if [ -z "${disk_model}" -o -z "${disk_fw_rev}" ]; then |
| return 1 |
| fi |
| return 0 |
| } |
| |
| # disk_info - Retrieve model and firmware version from disk |
| # |
| # Call the appropriate function for the device type. |
| # |
| # inputs: |
| # device -- the device name |
| # |
| # outputs: |
| # disk_model -- model of the device |
| # disk_fw_rev -- actual firmware revision on the device |
| # |
| # returns non 0 on error |
| # |
| disk_info() { |
| local device="$1" |
| local device_type |
| device_type="$(get_device_type "/dev/${device}")" |
| case ${device_type} in |
| "ATA") |
| disk_ata_info "$@" |
| ;; |
| "MMC") |
| disk_mmc_info "$@" |
| ;; |
| "NVME") |
| disk_nvme_info "$@" |
| ;; |
| *) |
| log_msg "Unknown device(${device}) type: ${device_type}" |
| return 1 |
| esac |
| } |
| |
| # disk_ata_power_cnt - Get the number of power cycle |
| # |
| # inputs: |
| # device -- the device name [sda,...] |
| # |
| disk_ata_power_cnt() { |
| local device="$1" |
| |
| "${FLAGS_smartctl}" -A "/dev/${device}" | awk ' |
| BEGIN { count = 0 } |
| $2 == "Power_Cycle_Count" { count = $10 } |
| END { print count } |
| ' |
| } |
| |
| # samus_ata1_power_cycle - Power Cycle the Samus uSSD |
| # |
| # When reformatting the samus uSSD, we can not use powerd. |
| # Toggle manually GPIOs. |
| samus_ata1_power_cycle() { |
| # SSD_RESET_L : 47 => 256 - 94 + 47 = 209 |
| # PP3300_SSD_EN : 21 => 256 - 94 + 21 = 183 |
| local SSD_RESET_L_ID=209 |
| local PP3300_SSD_EN=183 |
| local GPIO_PATH="/sys/class/gpio" |
| |
| local SSD_RESET_L_ID_PATH="${GPIO_PATH}/gpio${SSD_RESET_L_ID}" |
| local PP3300_SSD_EN_PATH="${GPIO_PATH}/gpio${PP3300_SSD_EN}" |
| |
| local EXPORT_PATH="${GPIO_PATH}/export" |
| |
| local device="$1" |
| |
| if [ ! -d "${SSD_RESET_L_ID_PATH}" ]; then |
| for i in ${SSD_RESET_L_ID} ${PP3300_SSD_EN}; do |
| echo $i > "${EXPORT_PATH}" |
| done |
| echo out > "${SSD_RESET_L_ID_PATH}/direction" |
| echo 1 > "${SSD_RESET_L_ID_PATH}/active_low" |
| |
| echo out > "${PP3300_SSD_EN_PATH}/direction" |
| |
| echo 1 > "${PP3300_SSD_EN_PATH}/value" |
| echo 0 > "${SSD_RESET_L_ID_PATH}/value" |
| fi |
| |
| # Down. |
| echo 1 > "${SSD_RESET_L_ID_PATH}/value" |
| sleep 1 |
| echo 0 > "${PP3300_SSD_EN_PATH}/value" |
| |
| sleep 4 |
| # Up. |
| echo 1 > "${PP3300_SSD_EN_PATH}/value" |
| sleep 1 |
| echo 0 > "${SSD_RESET_L_ID_PATH}/value" |
| |
| disk_hdparm_info "${device}" > /dev/null |
| } |
| |
| # disk_ata_power_cycle - Power cycle ATA SSD |
| # |
| # Suspend/resume the machine to power cycle the SSD. |
| # |
| # inputs: |
| # device -- the device name [sda,...] |
| # |
| # returns non 0 on error |
| # |
| disk_ata_power_cycle() { |
| local device="$1" |
| local tries=4 |
| local old_pwr_cycle_count |
| local new_pwr_cycle_count |
| |
| old_pwr_cycle_count="$(disk_ata_power_cnt "${device}")" |
| new_pwr_cycle_count="${old_pwr_cycle_count}" |
| # Gather power cycle count. |
| while [ "${old_pwr_cycle_count}" -eq "${new_pwr_cycle_count}" ] && \ |
| [ "${tries}" -gt 0 ]; do |
| : $(( tries -= 1 )) |
| "${FLAGS_pwr_suspend}" --wakeup_timeout=4 --timeout=10 |
| new_pwr_cycle_count="$(disk_ata_power_cnt "${device}")" |
| done |
| if [ "${old_pwr_cycle_count}" -eq "${new_pwr_cycle_count}" ]; then |
| log_msg "Unable to power cycle ${device}" |
| fi |
| } |
| |
| |
| # disk_hdparm_upgrade - Upgrade the firmware on the disk |
| # |
| # Update the firmware on the disk. |
| # TODO(gwendal): We assume the device can be updated in one shot. |
| # In a future version, we may place a |
| # a deep charge and reboot the machine. |
| # |
| # inputs: |
| # device -- the device name [sda,...] |
| # fw_file -- the firmware image |
| # fw_options -- the options from the rule file. |
| # |
| # returns non 0 on error |
| # |
| disk_hdparm_upgrade() { |
| local device="$1" |
| local fw_file="$2" |
| local fw_options="$3" |
| local hdparm_opt="--fwdownload-mode7" |
| local power_cyle="true" |
| local use_regular_hdparm="true" |
| |
| if [ "${fw_options}" != "-" ]; then |
| if echo "${fw_options}" | grep -q "mode3_max"; then |
| hdparm_opt="--fwdownload-mode3-max" |
| fi |
| if echo "${fw_options}" | grep -q "power_cycle"; then |
| power_cyle="disk_ata_power_cycle" |
| fi |
| if echo "${fw_options}" | grep -q "kingston_erase"; then |
| use_regular_hdparm=false |
| power_cyle="samus_ata1_power_cycle" |
| "${FLAGS_hdparm_kingston}" --eraseall "/dev/${device}" |
| fi |
| if echo "${fw_options}" | grep -q "kingston_reformat"; then |
| use_regular_hdparm=false |
| power_cyle="samus_ata1_power_cycle" |
| "${FLAGS_hdparm_kingston}" --mp_f1 "${fw_file}" \ |
| "KINGSTON_RBU_SUS151S3rr" "/dev/${device}" |
| fi |
| fi |
| |
| if "${use_regular_hdparm}"; then |
| # hdparm_opt could be several options, shell must see separator. |
| "${FLAGS_hdparm}" ${hdparm_opt} "${fw_file}" \ |
| --yes-i-know-what-i-am-doing --please-destroy-my-drive \ |
| "/dev/${device}" |
| fi |
| |
| if [ $? -ne 0 ]; then |
| return $? |
| fi |
| |
| ${power_cyle} "${device}" |
| } |
| |
| # disk_mmc_upgrade - Upgrade the firmware on the eMMC storage |
| # |
| # Update the firmware on the disk. |
| # |
| # inputs: |
| # device -- the device name [sda,...] |
| # fw_file -- the firmware image |
| # fw_options -- the options from the rule file. (unused) |
| # |
| # returns non 0 on error |
| # |
| disk_mmc_upgrade() { |
| local device="$1" |
| local fw_file="$2" |
| local fw_options="$3" |
| local options="" |
| |
| if [ "${fw_options}" = "new" ]; then |
| "${FLAGS_mmc}" ffu "${fw_file}" "/dev/${device}" |
| return $? |
| fi |
| |
| if [ "${fw_options}" != "-" ]; then |
| # Options for mmc in the config files are separated with commas. |
| # Translate the option for the command line. |
| options="$(echo "${fw_options}" | sed 's/,/ -k /g')" |
| options="-k ${options}" |
| fi |
| |
| "${FLAGS_mmc}" old_ffu ${options} "${fw_file##*/}" "/dev/${device}" |
| } |
| |
| # disk_nmve_reset - Reset NMVE SSD PCIe device. |
| # |
| # Reset the PCIe device hosting the NMVe device. |
| # |
| # inputs: |
| # device -- the device name [nvme0,...] |
| # |
| # returns non 0 on error |
| # |
| disk_nmve_reset() { |
| local device="$1" |
| |
| # Name space 1 is required to exits: |
| echo 1 > "/sys/block/${device}n1/device/device/reset" |
| } |
| |
| # disk_nvme_current_slot - Retrieve which slot the current firmware comes from. |
| # |
| # Information is located in the first byte of Log 3 |
| # (Firmware Slot Information Log). |
| # |
| # Return the slot number, assume nvme device is up. |
| disk_nvme_current_slot() { |
| local device="$1" |
| |
| local byte0="$("${FLAGS_nvme}" get-log "/dev/${device}" \ |
| --raw-binary --log-id 3 --log-len 512 | \ |
| od -An -t u1 -N 1)" |
| echo $((byte0 & 0x3)) |
| } |
| |
| # disk_nvme_get_frmw - get FRMW from identity data. |
| # |
| # Use already collected id-ctrl data, and extrace the Firmware Updates field. |
| # The fields have the following format: |
| # |
| # 7 5 4 3 1 0 |
| # +------+-+-----+-+ |
| # | | | | | |
| # +------+-+-----+-+ |
| # \ \ \ |
| # \ \ --- slot 1 is read only, can not be use for update. |
| # \ ------ number of slot available (between 1 and 7) |
| # --------- action 3 (upgrade without reset available) |
| disk_nvme_get_frmw() { |
| local device="$1" |
| grep "frmw" "${nvme_out}" | cut -d ':' -f 2 |
| } |
| |
| disk_nvme_get_min_writable_slot() { |
| local frmw="$1" |
| # Slot 1 can be read only, check frmw bit 0. |
| echo $(((frmw & 0x1) + 1)) |
| } |
| |
| disk_nvme_get_max_writable_slot() { |
| local frmw="$1" |
| # Bit 1 - 3 of frmw contains the number of slots. |
| echo $(((frmw & 0xe) >> 1)) |
| } |
| |
| # disk_nvme_action_supported - Return supported action |
| # |
| # Return the commit action the device is supporting: |
| # - 3 : the device can upgrade without reset, |
| # - 2 : the device needs reset to upgrade (mandatory). |
| disk_nvme_action_supported() { |
| local frmw="$1" |
| # Bit 4 of frmw indicates if upgrade without reset is supported. |
| echo $((((frmw & 0x10) >> 4) + 2)) |
| } |
| |
| # disk_nvme_upgrade - Upgrade the firmware on the NVME storage |
| # |
| # Update the firmware on the disk. |
| # |
| # inputs: |
| # device -- the device name [nvme0,...] |
| # fw_file -- the firmware image |
| # fw_options -- the options from the rule file. |
| # |
| # By default the NVMe device can be upgraded without reset (commit action 3), |
| # see NVMe 1.3 Figure 76. If reset is needed, we use commit action 2. |
| # |
| # returns non 0 on error |
| # |
| disk_nvme_upgrade() { |
| local device="$1" |
| local fw_file="$2" |
| local fw_options="$3" |
| local frmw="$(disk_nvme_get_frmw "${device}")" |
| local action="$(disk_nvme_action_supported "${frmw}")" |
| local curr_slot="$(disk_nvme_current_slot "${device}")" |
| local min_slot="$(disk_nvme_get_min_writable_slot "${frmw}")" |
| local max_slot="$(disk_nvme_get_max_writable_slot "${frmw}")" |
| local new_slot rc |
| |
| if [ "${curr_slot}" -eq "${max_slot}" ]; then |
| new_slot="${min_slot}" |
| else |
| new_slot="$((curr_slot + 1))" |
| fi |
| if echo "${fw_options}" | grep -q "separate_slot"; then |
| if [ "${new_slot}" -eq "${curr_slot}" ]; then |
| log_msg "Unable to find proper slot: current ${curr_slot}, " \ |
| "min: ${min_slot}, max: ${max_slot}" |
| return 1 |
| fi |
| fi |
| |
| "${FLAGS_nvme}" fw-download "/dev/${device}" --fw="${fw_file}" |
| rc=$? |
| if [ "${rc}" -ne 0 ]; then |
| log_msg "Unable to download ${fw_file} to ${device}" |
| return "${rc}" |
| fi |
| |
| # Use action 0 to download image into slot. |
| "${FLAGS_nvme}" fw-activate "/dev/${device}" --slot="${new_slot}" --action=0 |
| rc=$? |
| if [ "${rc}" -ne 0 ]; then |
| log_msg "Unable to load ${fw_file} to ${device}" |
| return "${rc}" |
| fi |
| "${FLAGS_nvme}" fw-activate "/dev/${device}" --slot="${new_slot}" \ |
| --action="${action}" |
| rc=$? |
| if [ "${rc}" -eq 11 -a "${action}" -eq 2 ]; then |
| disk_nmve_reset "${device}" |
| elif [ "${rc}" -ne 0 ]; then |
| log_msg "Unable to activate ${fw_file} to ${device}" |
| return "${rc}" |
| fi |
| } |
| |
| # disk_upgrade - Upgrade the firmware on the disk |
| # |
| # Update the firmware on the disk by calling the function appropriate for |
| # the transport. |
| # |
| # inputs: |
| # device -- the device name [sda,...] |
| # fw_file -- the firmware image |
| # fw_options -- the options from the rule file. |
| # |
| # returns non 0 on error |
| # |
| disk_upgrade() { |
| local device="$1" |
| local device_type |
| device_type="$(get_device_type "/dev/${device}")" |
| case ${device_type} in |
| "ATA") |
| disk_hdparm_upgrade "$@" |
| ;; |
| "MMC") |
| disk_mmc_upgrade "$@" |
| ;; |
| "NVME") |
| disk_nvme_upgrade "$@" |
| ;; |
| *) |
| log_msg "Unknown device(${device}) type: ${device_type}" |
| return 1 |
| esac |
| } |
| |
| # disk_upgrade_devices - Look for firmware upgrades |
| # |
| # major function: look for a rule match and upgrade. |
| # updated in one shot. In a future version, we may place a |
| # a deep charge and reboot the machine. |
| # |
| # input: |
| # list of devices to upgrade. |
| # retuns 0 on sucess |
| # The error code of hdparm or other functions that fails |
| # 120 if no rules is provided |
| # 121 when the disk works but the firmware was not applied. |
| # |
| disk_upgrade_devices() { |
| local disk_rules="$1" |
| local device |
| local fw_file |
| local success |
| local disk_old_fw_rev="" |
| local rc=0 |
| local tries=0 |
| |
| shift # skip disk rules parameters. |
| for device in "$@"; do |
| sucess="" |
| while true; do |
| disk_info "${device}" # sets disk_model, disk_fw_rev |
| rc=$? |
| if [ ${rc} -ne 0 ]; then |
| log_msg "Can not get info on this device. skip." |
| rc=0 |
| break |
| fi |
| disk_fw_select "${disk_rules}" # sets disk_fw_file, disk_exp_fw_rev, disk_fw_opt |
| rc=$? |
| if [ ${rc} -ne 0 ]; then |
| # Nothing to do, go to next drive if any. |
| : "${success:="No need to upgrade ${device}:${disk_model}"}" |
| log_msg "${success}" |
| rc=0 |
| break |
| fi |
| fw_file="${FLAGS_fw_package_dir}/${disk_fw_file}" |
| if [ ! -f "${fw_file}" ]; then |
| fw_file="${FLAGS_tmp_dir}/${disk_fw_file}" |
| bzcat "${FLAGS_fw_package_dir}/${disk_fw_file}.bz2" > "${fw_file}" 2> /dev/null |
| rc=$? |
| if [ ${rc} -ne 0 ]; then |
| log_msg "${disk_fw_file} in ${FLAGS_fw_package_dir} could not be extracted: ${rc}" |
| break |
| fi |
| fi |
| disk_old_fw_rev="${disk_fw_rev}" |
| disk_upgrade "${device}" "${fw_file}" "${disk_fw_opt}" |
| rc=$? |
| if [ "${rc}" -ne 0 ]; then |
| # Will change in the future if we need to power cycle, reboot... |
| log_msg "Unable to upgrade ${device} from ${disk_fw_rev} to ${disk_exp_fw_rev}" |
| break |
| else |
| # Allow the kernel to recover |
| tries=4 |
| rc=1 |
| # Verify that's the firmware upgrade stuck It may take some time. |
| while [ "${tries}" -ne 0 -a "${rc}" -ne 0 ]; do |
| : $(( tries -= 1 )) |
| # Allow the error handler to block the scsi queue if it is working. |
| if [ "${FLAGS_test}" -eq "${FLAGS_FALSE}" ]; then |
| sleep 1 |
| fi |
| disk_info "${device}" |
| rc=$? |
| done |
| if [ "${rc}" -ne 0 ]; then |
| # We are in trouble. The disk was expected to come back but did not. |
| # TODO(gwendal): Shall we have a preemptive message to ask to |
| # powercycle? |
| break |
| fi |
| if [ "${disk_exp_fw_rev}" = "${disk_fw_rev}" ]; then |
| # We are good, go to the next drive if any. |
| if [ -n "${success}" ]; then |
| success="${success} |
| " |
| fi |
| success="${success}Upgraded ${device}:${disk_model} from" |
| success="${success} ${disk_old_fw_rev} to ${disk_fw_rev}" |
| # Continue, in case we need upgrade in several steps. |
| continue |
| else |
| # The upgrade did not stick, we will retry later. |
| rc=121 |
| break |
| fi |
| fi |
| done |
| done |
| # Leave a trace of a successful run. |
| if [ "${rc}" -eq 0 -a -n "${FLAGS_status}" ]; then |
| echo "${success}" > "${FLAGS_status}" |
| fi |
| return "${rc}" |
| } |
| |
| main() { |
| local disk_rules_raw="${FLAGS_fw_package_dir}"/rules |
| local rc=0 |
| local erase_tmp_dir=${FLAGS_FALSE} |
| |
| if [ ! -d "${FLAGS_tmp_dir}" ]; then |
| erase_tmp_dir=${FLAGS_TRUE} |
| FLAGS_tmp_dir="$(mktemp -d)" |
| fi |
| if [ ! -f "${disk_rules_raw}" ]; then |
| log_msg "Unable to find rules file in ${FLAGS_fw_package_dir}" |
| return 120 |
| fi |
| disk_rules=${FLAGS_tmp_dir}/rules |
| |
| # remove unnecessary lines |
| sed '/^#/d;/^[[:space:]]*$/d' "${disk_rules_raw}" > "${disk_rules}" |
| |
| # Unquote call to list_fixed_* as they can return several device each. |
| disk_upgrade_devices "${disk_rules}" \ |
| $(list_fixed_ata_disks) $(list_fixed_mmc_disks) $(list_fixed_nvme_disks) |
| rc=$? |
| |
| if [ "${erase_tmp_dir}" -eq "${FLAGS_TRUE}" ]; then |
| rm -rf "${FLAGS_tmp_dir}" |
| fi |
| # Append a cksum to prevent multiple calls to this script. |
| if [ "${rc}" -eq 0 -a -n "${FLAGS_status}" ]; then |
| cksum "${disk_rules_raw}" >> "${FLAGS_status}" |
| fi |
| return "${rc}" |
| } |
| |
| # invoke main if not in test mode, otherwise let the test code call. |
| if [ "${FLAGS_test}" -eq "${FLAGS_FALSE}" ]; then |
| main "$@" |
| fi |
| |