N3000 BMC and retimer firmware update

The update provides the support of updating BMC and retimer firmware
for the N3000 device.

Story: 2008965
Task: 42953

Change-Id: Id745bd642d906c917e50d54b02d5923ca6d277a7
Signed-off-by: Teresa Ho <teresa.ho@windriver.com>
This commit is contained in:
Teresa Ho 2021-07-30 12:16:54 -04:00
parent 8e82287b4a
commit d66269a6a1
13 changed files with 164 additions and 12 deletions

View File

@ -5594,6 +5594,7 @@ itemNotFound (404)
"description (Optional)", "plain", "xsd:string", "The description of the device image."
"image_version (Optional)", "plain", "xsd:string", "The version of the device image."
"applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels."
"retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image."
"uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
"links (Optional)", "plain", "xsd:list", "For convenience, resources contain links to themselves. This allows a client to easily obtain rather than construct resource URIs. The following types of link relations are associated with resources: a self link containing a versioned link to the resource, and a bookmark link containing a permanent link to a resource that is appropriate for long term storage."
@ -5612,6 +5613,7 @@ itemNotFound (404)
"description": null,
"name": null,
"image_version": null,
"retimer_included": false,
"applied_labels":
{
"key1": "value1",
@ -5690,6 +5692,7 @@ itemNotFound (404)
"description (Optional)", "plain", "xsd:string", "The description of the device image."
"image_version (Optional)", "plain", "xsd:string", "The version of the device image."
"applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels."
"retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image."
"uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
"links (Optional)", "plain", "xsd:list", "For convenience, resources contain links to themselves. This allows a client to easily obtain rather than construct resource URIs. The following types of link relations are associated with resources: a self link containing a versioned link to the resource, and a bookmark link containing a permanent link to a resource that is appropriate for long term storage."
@ -5708,6 +5711,7 @@ itemNotFound (404)
"description": null,
"name": null,
"image_version": null,
"retimer_included": false,
"applied_labels":
{
"key1": "value1",
@ -5746,6 +5750,7 @@ badMediaType (415)
"name (Optional)", "plain", "xsd:string", "The name of the device image."
"description (Optional)", "plain", "xsd:string", "The description of the device image."
"image_version (Optional)", "plain", "xsd:string", "The version of the device image."
"retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image."
**Response parameters**
@ -5762,6 +5767,7 @@ badMediaType (415)
"name (Optional)", "plain", "xsd:string", "The name of the device image."
"description (Optional)", "plain", "xsd:string", "The description of the device image."
"image_version (Optional)", "plain", "xsd:string", "The version of the device image."
"retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image."
"applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels."
"uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
@ -5780,6 +5786,7 @@ badMediaType (415)
"description": null,
"name": null,
"image_version": null,
"retimer_included": false,
"applied_labels": null
}
]
@ -5830,6 +5837,7 @@ badMediaType (415)
"name (Optional)", "plain", "xsd:string", "The name of the device image."
"description (Optional)", "plain", "xsd:string", "The description of the device image."
"image_version (Optional)", "plain", "xsd:string", "The version of the device image."
"retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image."
"applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels."
"uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
@ -5848,6 +5856,7 @@ badMediaType (415)
"description": null,
"name": null,
"image_version": null,
"retimer_included": false,
"applied_labels":
{
"key1": "value1"
@ -5902,6 +5911,7 @@ badMediaType (415)
"name (Optional)", "plain", "xsd:string", "The name of the device image."
"description (Optional)", "plain", "xsd:string", "The description of the device image."
"image_version (Optional)", "plain", "xsd:string", "The version of the device image."
"retimer_included (Optional)", "plain", "xsd:boolean", "This indicates whether the retimer firmware is included in the BMC functional image."
"applied_labels (Optional)", "plain", "xsd:list", "The device image applied to the device labels."
"uuid (Optional)", "plain", "csapi:UUID", "The universally unique identifier for this object."
@ -5920,6 +5930,7 @@ badMediaType (415)
"description": null,
"name": null,
"image_version": null,
"retimer_included": false,
"applied_labels": null
}
]

View File

@ -12,7 +12,7 @@ from cgtsclient import exc
CREATION_ATTRIBUTES = [
'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version', 'uuid']
'name', 'description', 'image_version', 'uuid', 'retimer_included']
class DeviceImage(base.Resource):

View File

@ -13,7 +13,7 @@ def _print_device_image_show(obj):
'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version',
'applied', 'applied_labels']
'applied', 'applied_labels', 'retimer_included']
if isinstance(obj, dict):
data = [(f, obj.get(f, '')) for f in fields]
@ -37,11 +37,11 @@ def do_device_image_list(cc, args):
labels = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version',
'name', 'description', 'image_version', 'retimer_included',
'applied', 'applied_labels']
fields = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version',
'name', 'description', 'image_version', 'retimer_included',
'applied', 'applied_labels']
device_images = cc.device_image.list()
utils.print_list(device_images, fields, labels, sortby=1)
@ -81,6 +81,9 @@ def do_device_image_list(cc, args):
@utils.arg('-u', '--uuid',
metavar='<uuid>',
help='UUID of the device image')
@utils.arg('--retimer-included',
metavar='<true/false>',
help='Retimer firmware included in BMC FW binary')
def do_device_image_upload(cc, args):
"""Upload a device image."""
@ -90,7 +93,7 @@ def do_device_image_upload(cc, args):
field_list = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version']
'name', 'description', 'image_version', 'retimer_included']
# Prune input fields down to required/expected values
user_fields = dict((k, v) for (k, v) in vars(args).items()

View File

@ -81,6 +81,9 @@ class DeviceImage(base.APIBase):
image_version = wtypes.text
"The version of the device image"
retimer_included = bool
"Retimer firmware included in BMC firmware binary"
applied = bool
"Represent current status: created or applied"
@ -109,7 +112,7 @@ class DeviceImage(base.APIBase):
['id', 'uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version',
'applied', 'applied_labels'])
'applied', 'applied_labels', 'retimer_included'])
# insert applied labels for this device image if they exist
device_image = _get_applied_labels(device_image)
@ -246,7 +249,7 @@ class DeviceImageController(rest.RestController):
field_list = ['uuid', 'bitstream_type', 'pci_vendor', 'pci_device',
'bitstream_id', 'key_signature', 'revoke_key_id',
'name', 'description', 'image_version']
'name', 'description', 'image_version', 'retimer_included']
data = dict((k, v) for (k, v) in pecan.request.POST.items()
if k in field_list and not (v is None))
msg = _validate_syntax(data)
@ -431,6 +434,9 @@ def _validate_bitstream_type(dev_img):
elif (dev_img['bitstream_type'] == dconstants.BITSTREAM_TYPE_KEY_REVOCATION and
'revoke_key_id' not in dev_img):
msg = _("revoke_key_id is required for key revocation bitstream type")
elif (dev_img['bitstream_type'] != dconstants.BITSTREAM_TYPE_FUNCTIONAL and
'retimer_included' in dev_img.keys()):
msg = _("retimer_included option is only applicable to functional BMC image")
return msg
@ -477,6 +483,10 @@ def _validate_syntax(device_image):
not cutils.is_uuid_like(device_image['uuid'])):
msg = _("uuid must be a valid UUID")
return msg
if ('retimer_included' in device_image.keys() and
not cutils.is_valid_boolstr(device_image['retimer_included'])):
msg = _("Parameter retimer_included must be a valid bool string")
return msg
msg = _validate_hexadecimal_fields(device_image)
if not msg:
msg = _validate_bitstream_type(device_image)

View File

@ -13705,7 +13705,8 @@ class ConductorManager(service.PeriodicService):
(host.hostname, pci_device.pciaddr, filename, device_image_state.id))
fpga_rpcapi = fpga_agent_rpcapi.AgentAPI()
fpga_rpcapi.host_device_update_image(
context, host.hostname, pci_device.pciaddr, filename, device_image_state.id)
context, host.hostname, pci_device.pciaddr, filename, device_image_state.id,
device_image.retimer_included)
# We've kicked off a device image update, so exit the function.
return
LOG.info("no more device images to process")

View File

@ -0,0 +1,24 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright (c) 2021 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
from sqlalchemy import Boolean, Column, MetaData, Table
ENGINE = 'InnoDB'
CHARSET = 'utf8'
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
dev_img_functional = Table('device_images_functional', meta, autoload=True)
dev_img_functional.create_column(Column('retimer_included', Boolean, default=False))
def downgrade(migrate_engine):
# Downgrade is unsupported in this release.
raise NotImplementedError('SysInv database downgrade is unsupported.')

View File

@ -1524,6 +1524,7 @@ class DeviceImageFunctional(DeviceImageCommon, DeviceImage):
__tablename__ = 'device_images_functional'
bitstream_id = Column(String(255), nullable=True)
retimer_included = Column(Boolean, nullable=False, default=False)
__mapper_args__ = {
'polymorphic_identity': 'functional',

View File

@ -35,3 +35,6 @@ OPAE_IMG = "registry.local:9001/docker.io/starlingx/n3000-opae:stx.6.0-v1.0.1"
DOCKER_LOGIN_FLAG = "/var/run/docker_login_done"
N3000_RESET_FLAG = os.path.join(tsc.VOLATILE_PATH, ".sysinv_n3000_reset")
# This flag is set if the N3000 requires a second reset
N3000_RETIMER_FLAG = os.path.join(tsc.PLATFORM_CONF_PATH, ".sysinv_n3000_retimer")

View File

@ -551,7 +551,8 @@ class FpgaAgentManager(service.PeriodicService):
"this will likely cause problems.")
pass
def device_update_image(self, context, pci_addr, filename, transaction_id):
def device_update_image(self, context, pci_addr, filename, transaction_id,
retimer_included):
"""Write the device image to the device at the specified address.
Transaction is the transaction ID as specified by sysinv-conductor.
@ -612,6 +613,9 @@ class FpgaAgentManager(service.PeriodicService):
os.remove(local_path)
# start the watchdog service again
start_watchdog()
# If device image contains c827 retimer firmware, set the retimer flag
if retimer_included:
utils.touch(constants.N3000_RETIMER_FLAG)
except exception.SysinvException as exc:
LOG.info("setting transaction id %s as failed" % transaction_id)

View File

@ -20,6 +20,7 @@
import os
import shlex
from eventlet.green import subprocess
from glob import glob
from oslo_log import log
from sysinv.common import utils
@ -30,6 +31,18 @@ from sysinv.fpga_agent import constants
# Volatile flag file so we only reset the N3000s once after bootup.
LOG = log.getLogger(__name__)
SYSFS_DEVICE_PATH = "/sys/bus/pci/devices/"
FME_PATH = "/fpga/intel-fpga-dev.*/intel-fpga-fme.*/"
SPI_PATH = "spi-altera.*.auto/spi_master/spi*/spi*.*/"
# These are relative to SPI_PATH
EEPROM_LOAD_PATH = "pkvl/eeprom_load"
EEPROM_UPDATE_STATUS_PATH = "pkvl/eeprom_update_status"
# The value in eeprom_update_status must be 0x1111 to indicate successful
# update as documented in the Intel FPGA N3000 User Guide
EEPROM_UPDATE_SUCCESS = '0x1111'
def n3000_img_accessible():
cmd = 'docker image list "%s" --format "{{.Repository}}:{{.Tag}}"' % \
@ -73,6 +86,52 @@ def reset_device_n3000(pci_addr):
raise exception.SysinvException(msg)
def get_n3000_sysfs_file(pattern):
"""Find a sysfs file related to the N3000.
The result should be an empty string if the file doesn't exist,
or a single line of text if it does.
"""
# Convert the pattern to a list of matching filenames
filenames = glob(pattern)
# If there are no matching files, return an empty string.
if len(filenames) == 0:
return ""
# If there's more than one filename, complain.
if len(filenames) > 1:
LOG.warn("Pattern %s gave %s matching filenames, using the first." %
(pattern, len(filenames)))
filename = filenames[0]
return filename
def update_device_n3000_retimer(pci_addr):
# Write 1 to the eeprom_load sysfs node of the card
eeprom_load_pattern = (SYSFS_DEVICE_PATH + pci_addr + FME_PATH +
SPI_PATH + EEPROM_LOAD_PATH)
try:
eeprom_load_file = get_n3000_sysfs_file(eeprom_load_pattern)
with open(eeprom_load_file, "w") as writer:
writer.write("1")
except Exception as e:
msg = "Failed to load retimer: %s" % str(e)
LOG.error(msg)
raise exception.SysinvException(msg)
# Check the eeprom_update_status node for completion
eeprom_update_status_pattern = (SYSFS_DEVICE_PATH + pci_addr + FME_PATH +
SPI_PATH + EEPROM_UPDATE_STATUS_PATH)
eeprom_update_status = get_n3000_sysfs_file(eeprom_update_status_pattern)
with open(eeprom_update_status, 'r') as reader:
status = reader.read()
if EEPROM_UPDATE_SUCCESS not in status:
LOG.error("Failed to update retimer, status=%s" % status)
def reset_n3000_fpgas():
if not os.path.exists(constants.N3000_RESET_FLAG):
# Reset all N3000 FPGAs on the system.
@ -91,9 +150,23 @@ def reset_n3000_fpgas():
except Exception:
got_exception = True
if not got_exception and os.path.exists(constants.N3000_RETIMER_FLAG):
# The retimer included flag is set, execute additional steps
fpga_addrs = get_n3000_devices()
for fpga_addr in fpga_addrs:
try:
LOG.info("Updating retimer")
update_device_n3000_retimer(fpga_addr)
LOG.info("Resetting N3000 second time")
reset_device_n3000(fpga_addr)
except Exception:
got_exception = True
LOG.info("Done resetting N3000 FPGAs.")
if not got_exception:
utils.touch(constants.N3000_RESET_FLAG)
if os.path.exists(constants.N3000_RETIMER_FLAG):
os.remove(constants.N3000_RETIMER_FLAG)
return True
else:
return False

View File

@ -52,11 +52,12 @@ class AgentAPI(sysinv.openstack.common.rpc.proxy.RpcProxy):
default_version=self.RPC_API_VERSION)
def host_device_update_image(self, context, hostname, pci_addr,
filename, transaction_id):
filename, transaction_id, retimer_included):
LOG.info("sending device_update_image to host %s" % hostname)
topic = '%s.%s' % (self.topic, hostname)
return self.cast(context,
self.make_msg('device_update_image',
pci_addr=pci_addr, filename=filename,
transaction_id=transaction_id),
transaction_id=transaction_id,
retimer_included=retimer_included),
topic=topic)

View File

@ -27,6 +27,7 @@ class DeviceImage(base.SysinvObject):
'image_version': utils.str_or_none,
'applied': utils.bool_or_none,
'capabilities': utils.dict_or_none,
'retimer_included': utils.bool_or_none,
}
_optional_fields = {'bitstream_id',
@ -34,7 +35,8 @@ class DeviceImage(base.SysinvObject):
'revoke_key_id',
'name',
'description',
'image_version'}
'image_version',
'retimer_included'}
@base.remotable_classmethod
def get_by_uuid(cls, context, uuid):

View File

@ -276,6 +276,25 @@ class TestPostDeviceImage(TestDeviceImage, dbbase.ControllerHostTestCase):
self.assertIn("revoke_key_id is required for key revocation bitstream"
" type", str(result))
def test_create_non_functional_image_with_retimer(self):
# Test creation of device image
bitstream_file = os.path.join(os.path.dirname(__file__), "data",
'bitstream.bit')
data = {
'bitstream_type': dconstants.BITSTREAM_TYPE_KEY_REVOCATION,
'pci_vendor': fpga_constants.N3000_VENDOR,
'pci_device': fpga_constants.N3000_DEVICE,
'revoke_key_id': '12345',
'retimer_included': True,
}
upload_file = [('file', bitstream_file)]
result = self.post_with_files('/device_images', data,
upload_files=upload_file,
headers=self.API_HEADERS,
expect_errors=True)
self.assertIn("retimer_included option is only applicable to"
" functional BMC image", str(result))
def test_create_bitstream_type_invalid(self):
# Test creation of device image
bitstream_file = os.path.join(os.path.dirname(__file__), "data",