Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Mellanox]platform api support firmware install #3931

Merged
merged 9 commits into from
Jan 29, 2020
Next Next commit
[sonic_platform]support install firmware
Stephen Sun committed Nov 21, 2019
commit 2c8e646c215a26d045f3a819cd646756f6adde23
1 change: 1 addition & 0 deletions files/build_templates/sonic_debian_extension.j2
Original file line number Diff line number Diff line change
@@ -381,6 +381,7 @@ sudo cp $files_path/$MLNX_SPC_FW_FILE $FILESYSTEM_ROOT/etc/mlnx/fw-SPC.mfa
sudo cp $files_path/$MLNX_SPC2_FW_FILE $FILESYSTEM_ROOT/etc/mlnx/fw-SPC2.mfa
sudo cp $files_path/$ISSU_VERSION_FILE $FILESYSTEM_ROOT/etc/mlnx/issu-version
sudo cp $files_path/$MLNX_FFB_SCRIPT $FILESYSTEM_ROOT/usr/bin/mlnx-ffb.sh
sudo cp $files_path/$ONIE_FW_UPDATE $FILESYSTEM_ROOT/usr/bin/onie-fw-update.sh
j2 platform/mellanox/mlnx-fw-upgrade.j2 | sudo tee $FILESYSTEM_ROOT/usr/bin/mlnx-fw-upgrade.sh
sudo chmod 755 $FILESYSTEM_ROOT/usr/bin/mlnx-fw-upgrade.sh

149 changes: 144 additions & 5 deletions platform/mellanox/mlnx-platform-api/sonic_platform/component.py
Original file line number Diff line number Diff line change
@@ -5,13 +5,14 @@
#
# implementation of new platform api
#############################################################################

from __future__ import print_function
try:
from sonic_platform_base.component_base import ComponentBase
from glob import glob
import subprocess
import io
import re
import sys
except ImportError as e:
raise ImportError(str(e) + "- required module not found")

@@ -23,6 +24,26 @@
CPLD_VERSION_FILE_PATTERN = '/var/run/hw-management/system/cpld[0-9]_version'
CPLD_VERSION_MAX_LENGTH = 4

CPLD_UPDATE_COMMAND = "cpldupdate --dev {} {}"
CPLD_INSTALL_SUCCESS_FLAG = "PASS!"

MST_DEVICE_PATTERN = "/dev/mst/mt[0-9]*_pciconf0"
MACHINE_CONF_FILE = "/host/machine.conf"
MACHINE_CONF_MAX_SIZE = 2048

BIOS_VERSION_PARSE_PATTERN = 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)'
ONIE_VERSION_PARSE_PATTERN = 'onie_version=[0-9]{4}\.[0-9]{2}-([0-9]+)\.([0-9]+)\.([0-9]+)'
ONIE_VERSION_MAJOR_OFFSET = 1
ONIE_VERSION_MINOR_OFFSET = 2
ONIE_VERSION_RELEASE_OFFSET = 3
# To update BIOS requires the ONIE with version 5.2.0016 or upper
ONIE_REQUIRED_MAJOR = "5"
ONIE_REQUIRED_MINOR = "2"
ONIE_REQUIRED_RELEASE = "0016"

ONIE_FW_UPDATE_CMD_ADD = "/usr/bin/onie-fw-update add {}"
ONIE_FW_UPDATE_CMD_UPDATE = "/usr/bin/onie-fw-update update"

class Component(ComponentBase):
def get_name(self):
"""
@@ -60,10 +81,16 @@ def _get_command_result(self, cmdline):
return result


class ComponentBIOS(Component):
BIOS_VERSION_PARSE_PATTERN = 'OEM[\s]*Strings\n[\s]*String[\s]*1:[\s]*([0-9a-zA-Z_\.]*)'
def _does_file_exist(self, image_path):
image = glob(image_path)
if not image:
return False, "Failed to get the image file via {} or multiple files matched".format(image_path)
if len(image) != 1:
return False, "Multiple files {} matched {}".format(image, image_path)
return True, ""


class ComponentBIOS(Component):
def __init__(self):
self.name = COMPONENT_BIOS

@@ -101,15 +128,63 @@ def get_firmware_version(self):
"""
bios_ver_str = self._get_command_result(BIOS_QUERY_VERSION_COMMAND)
try:
m = re.search(self.BIOS_VERSION_PARSE_PATTERN, bios_ver_str)
m = re.search(BIOS_VERSION_PARSE_PATTERN, bios_ver_str)
result = m.group(1)
except AttributeError as e:
raise RuntimeError("Failed to parse BIOS version by {} from {} due to {}".format(
self.BIOS_VERSION_PARSE_PATTERN, bios_ver_str, repr(e)))
BIOS_VERSION_PARSE_PATTERN, bios_ver_str, repr(e)))

return result


def install_firmware(self, image_path):
"""
Installs firmware to the component
Args:
image_path: A string, path to firmware image
Returns:
A boolean, True if install was successful, False if not
"""
# check ONIE version. To update ONIE requires version 5.2.0016 or later.
machine_conf = self._read_generic_file(MACHINE_CONF_FILE, MACHINE_CONF_MAX_SIZE)
try:
m = re.search(ONIE_VERSION_PARSE_PATTERN, machine_conf)
onie_major = m.group(ONIE_VERSION_MAJOR_OFFSET)
onie_minor = m.group(ONIE_VERSION_MINOR_OFFSET)
onie_release = m.group(ONIE_VERSION_RELEASE_OFFSET)
except AttributeError as e:
print("Failed to parse ONIE version by {} from {} due to {}".format(
BIOS_VERSION_PARSE_PATTERN, machine_conf, repr(e)))
return False
if onie_major < ONIE_REQUIRED_MAJOR or onie_minor < ONIE_REQUIRED_MINOR or onie_release < ONIE_REQUIRED_RELEASE:
print("ONIE {}.{}.{} or later is required".format(ONIE_REQUIRED_MAJOR, ONIE_REQUIRED_MINOR, ONIE_REQUIRED_RELEASE))
return False

# check whether the file exists
image_exists, message = self._does_file_exist(image_path)
if not image_exists:
print(message)
return False

# do the real work.
try:
result = subprocess.call(ONIE_FW_UPDATE_CMD_ADD.format(image_path).split())
if result:
return False
result = subprocess.call(ONIE_FW_UPDATE_CMD_UPDATE.split())
if result:
return False
except Exception as e:
print("Installing BIOS failed due to {}".format(repr(e)))
return False

print("Reboot via \"/sbin/reboot\" is required to finish BIOS installation.")
print("Please don't try installing a new sonic image before BIOS installation finishing")
return True


class ComponentCPLD(Component):
def __init__(self):
self.name = COMPONENT_CPLD
@@ -146,3 +221,67 @@ def get_firmware_version(self):

return cpld_version


def install_firmware(self, image_path):
"""
Installs firmware to the component
Args:
image_path: A string, path to firmware image
Returns:
A boolean, True if install was successful, False if not
Details:
The command "cpldupdate" is provided to install CPLD. There are two ways to do it:
1. To burn CPLD via gpio, which is faster but only supported on new systems, like Anaconda, ...
2. To install CPLD via firmware, which is slower but supported on older systems.
This also requires the mst device designated.
"cpldupdate --dev <devname> <vme_file>" has the logic of testing whether to update via gpio is supported,
and if so then go this way, otherwise tries updating software via fw. So we take advantage of it to update the CPLD.
By doing so we don't have to mind whether to update via gpio supported, which belongs to hardware details.
So the procedure should be:
1. Test whether the file exists
2. Fetch the mst device name
3. Update CPLD via executing "cpldupdate --dev <devname> <vme_file>"
4. Check the result
"""
# check whether the file exists
image_exists, message = self._does_file_exist(image_path)
if not image_exists:
print(message)
return False

mst_dev_list = glob(MST_DEVICE_PATTERN)
if not mst_dev_list or not len(mst_dev_list) == 1:
print("Failed to get mst device which is required for CPLD updating or multiple device files matched")
return False

cmdline = CPLD_UPDATE_COMMAND.format(mst_dev_list[0], image_path)
outputline = ""
success_flag = False
try:
proc = subprocess.Popen(cmdline, stdout=subprocess.PIPE, shell=True, stderr=subprocess.STDOUT)
while True:
out = proc.stdout.read(1)
if out == '' and proc.poll() != None:
break
if out != '':
sys.stdout.write(out)
sys.stdout.flush()
outputline += out
if (out == '\n' or out == '\r') and len(outputline):
m = re.search(CPLD_INSTALL_SUCCESS_FLAG, outputline)
if m and m.group(0) == CPLD_INSTALL_SUCCESS_FLAG:
success_flag = True

except OSError as e:
raise RuntimeError("Failed to execute command {} due to {}".format(cmdline, repr(e)))

if success_flag:
print("Success. Refresh or power cycle is required to finish CPLD installation.")
else:
print("Failed to install CPLD")

return success_flag
2 changes: 1 addition & 1 deletion platform/mellanox/one-image.mk
Original file line number Diff line number Diff line change
@@ -11,5 +11,5 @@ $(SONIC_ONE_IMAGE)_DOCKERS += $(filter-out $(patsubst %-$(DBG_IMAGE_MARK).gz,%.g
else
$(SONIC_ONE_IMAGE)_DOCKERS = $(SONIC_INSTALL_DOCKER_IMAGES)
endif
$(SONIC_ONE_IMAGE)_FILES += $(MLNX_SPC_FW_FILE) $(MLNX_SPC2_FW_FILE) $(MLNX_FFB_SCRIPT) $(ISSU_VERSION_FILE)
$(SONIC_ONE_IMAGE)_FILES += $(MLNX_SPC_FW_FILE) $(MLNX_SPC2_FW_FILE) $(MLNX_FFB_SCRIPT) $(ISSU_VERSION_FILE) $(ONIE_FW_UPDATE)
SONIC_INSTALLERS += $(SONIC_ONE_IMAGE)
150 changes: 150 additions & 0 deletions platform/mellanox/onie-fw-update
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#!/bin/sh

# Copyright (C) 2019 Mellanox Technologies Ltd.
# Copyright (C) 2019 Michael Shych <michaelsh@mellanox.com>
#
# SPDX-License-Identifier: GPL-2.0

this_script=${ONIE_FWPKG_PROGRAM_NAME:-$(basename $(realpath $0))}

onie_mount=/mnt/onie-boot
os_boot=/host
onie_partition=
onie_entry=0

export ONIE_FWPKG_PROGRAM_NAME=$(basename $(realpath $0))

usage()
{
cat <<EOF
update
The 'update' command will reboot system to ONIE update mode
and ONIE will perform automatically update of previously
added (i.e. pending) FW (ONIE itself, BIOS or CPLD) image.
EOF
}

enable_onie_access()
{
onie_partition=$(fdisk -l | grep "ONIE boot" | awk '{print $1}')
if [ ! -d $onie_mount ]; then
mkdir /mnt/onie-boot
fi
mount $onie_partition /mnt/onie-boot
if [ ! -e /lib/onie ]; then
ln -s /mnt/onie-boot/onie/tools/lib/onie /lib/onie
fi
PATH=/sbin:/usr/sbin:/bin:/usr/bin:$onie_mount/onie/tools/bin/
export PATH
}

clean_onie_access()
{
rm -f /lib/onie
umount $onie_partition
}

# ONIE entry must exist in grub config
find_onie_menuentry()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stephenxs Since we are already use onie_entry, we do not need this anymore

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed.

{
onie_entry="$(cat $os_boot/grub/grub.cfg | grep -e 'menuentry' | cat -n | awk '$0~/ONIE/ {print $1-1}')"
entries_num="$(echo "$onie_entry" | grep -E '^[0-9]+$' | wc -l)"
if [ $entries_num -eq 1 ] && [ $onie_entry -ge 1 ]; then
return 0
fi
return 1
}

change_grub_boot_order()
{
find_onie_menuentry
rc=$?
if [ $rc -eq 0 ]; then
grub-reboot --boot-directory=$os_boot $onie_entry
else
echo "ERROR: ONIE entry wasn't found in grub config"
return 1
fi

grub-editenv $onie_mount/grub/grubenv set onie_mode=update
return 0
}

show_pending()
{
curr_dir=$(pwd)
cd $onie_mount/onie/update/pending || return 0
num=$(find . -type f | wc -l)
if [ $num -ge 1 ]; then
echo "Number of FW update pending files are: "$num
ls -l * | awk {'print $9" "$5" "$7"-"$6" "$8'}
else
echo "There is no pending files for FW update."
fi
cd $curr_dir

return $num
}

system_reboot()
{
echo "Reboot will be done after 5 sec."
sleep 5
/sbin/reboot
}

# Process command arguments
cmd=$1
# optional argument
name="$2"

if [ -z "$cmd" ] ; then
# Default to 'show' if no command is specified.
cmd="show"
fi

case "$cmd" in
add | remove)
[ -z "$name" ] && {
echo "ERROR: This command requires a firmware update file name."
echo "Run '$this_script help' for complete details."
exit 1
}
;;
update)
enable_onie_access
show_pending
rc=$?
if [ $rc -ne 0 ]; then
change_grub_boot_order
rc=$?
clean_onie_access
if [ $rc -eq 0 ]; then
system_reboot
fi
exit $rc
else
echo "ERROR: NO FW images for update."
echo "Run: $this_script add <image> before update."
clean_onie_access
exit 1
fi
;;
purge | show | show-results | show-log | show-pending | help)
;;
*)
echo "Unknown command: $cmd"
exit 1
;;
esac

enable_onie_access
$onie_mount/onie/tools/bin/onie-fwpkg "$@"
rc=$?
if [ $cmd = "help" ]; then
usage
fi
clean_onie_access

exit $rc
7 changes: 7 additions & 0 deletions platform/mellanox/onie-fw-update.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# bios update tool

ONIE_FW_UPDATE= onie-fw-update
$(ONIE_FW_UPDATE)_PATH = platform/mellanox/
SONIC_COPY_FILES += $(ONIE_FW_UPDATE)

export ONIE_FW_UPDATE
1 change: 1 addition & 0 deletions platform/mellanox/rules.mk
Original file line number Diff line number Diff line change
@@ -12,6 +12,7 @@ include $(PLATFORM_PATH)/libsaithrift-dev.mk
include $(PLATFORM_PATH)/docker-ptf-mlnx.mk
include $(PLATFORM_PATH)/mlnx-ffb.mk
include $(PLATFORM_PATH)/issu-version.mk
include $(PLATFORM_PATH)/onie-fw-update.mk

SONIC_ALL += $(SONIC_ONE_IMAGE) \
$(DOCKER_FPM)