File opae-missing-shebang.patch of Package opae.38014
commit 623ed6defe401aedf57ad539f2feda41e368b9ec
Author: Nicolas Morey-Chaisemartin <nmoreychaisemartin@suse.com>
Date: Mon Mar 7 09:55:30 2022 +0100
opae missing shebang
Signed-off-by: Nicolas Morey-Chaisemartin <nmoreychaisemartin@suse.com>
diff --git cmake/modules/packaging.cmake cmake/modules/packaging.cmake
index 7b2dfd168d2c..a028cb147d78 100644
--- cmake/modules/packaging.cmake
+++ cmake/modules/packaging.cmake
@@ -98,7 +98,7 @@ macro(CREATE_PYTHON_EXE EXE_NAME MAIN_MODULE)
"\n"
"# Write to a buffer so that the shebang can be prepended easily\n"
"wr_buf = BytesIO()\n"
- "wr_buf.write('#!/usr/bin/env python' + os.linesep)\n"
+ "wr_buf.write('#!/usr/bin/python3' + os.linesep)\n"
"\n"
"z = zipfile.PyZipFile(wr_buf, 'w')\n")
diff --git libopae/plugins/ase/scripts/afu_sim_setup libopae/plugins/ase/scripts/afu_sim_setup
index 7d1939ce29ac..af6de5ba3c2c 100755
--- libopae/plugins/ase/scripts/afu_sim_setup
+++ libopae/plugins/ase/scripts/afu_sim_setup
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2013-2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git libopae/plugins/ase/scripts/generate_ase_environment.py libopae/plugins/ase/scripts/generate_ase_environment.py
index 99d569789aa2..6372081972f4 100755
--- libopae/plugins/ase/scripts/generate_ase_environment.py
+++ libopae/plugins/ase/scripts/generate_ase_environment.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2013-2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git libopae/plugins/ase/scripts/ipc_clean.py libopae/plugins/ase/scripts/ipc_clean.py
index 2ca3f985afb5..85f653a992a8 100755
--- libopae/plugins/ase/scripts/ipc_clean.py
+++ libopae/plugins/ase/scripts/ipc_clean.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2013-2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git platforms/scripts/afu_platform_config platforms/scripts/afu_platform_config
index 5bab88b8f7c5..fd6622988307 100755
--- platforms/scripts/afu_platform_config
+++ platforms/scripts/afu_platform_config
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
#
# Copyright (c) 2017, Intel Corporation
diff --git platforms/scripts/afu_platform_info platforms/scripts/afu_platform_info
index 64abe0041901..54ce277e143c 100755
--- platforms/scripts/afu_platform_info
+++ platforms/scripts/afu_platform_info
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
#
# Copyright (c) 2018, Intel Corporation
diff --git platforms/scripts/afu_synth_setup platforms/scripts/afu_synth_setup
index 5cba6af04610..4d5f99384a89 100755
--- platforms/scripts/afu_synth_setup
+++ platforms/scripts/afu_synth_setup
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2013-2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git platforms/scripts/rtl_src_config platforms/scripts/rtl_src_config
index 7703005ddfe6..bb132f316c8c 100755
--- platforms/scripts/rtl_src_config
+++ platforms/scripts/rtl_src_config
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
#
# Consume a file with a list of source files, include paths and preprocessor
diff --git pyopae/pybind11/docs/conf.py pyopae/pybind11/docs/conf.py
index e328eb161d76..55bb723956b9 100644
--- pyopae/pybind11/docs/conf.py
+++ pyopae/pybind11/docs/conf.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# pybind11 documentation build configuration file, created by
diff --git pyopae/pybind11/setup.py pyopae/pybind11/setup.py
index f677f2af4a6f..9a184673fbba 100644
--- pyopae/pybind11/setup.py
+++ pyopae/pybind11/setup.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Setup script for PyPI; use CMakeFile.txt to build extension modules
diff --git pyopae/pybind11/tools/mkdoc.py pyopae/pybind11/tools/mkdoc.py
index 1fd8cceed52b..3e4b5d6c3622 100644
--- pyopae/pybind11/tools/mkdoc.py
+++ pyopae/pybind11/tools/mkdoc.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/python3
#
# Syntax: mkdoc.py [-I<path> ..] [.. a list of header files ..]
#
diff --git scripts/index_generator.py scripts/index_generator.py
index dc0bcc2d13fd..0f406738c384 100644
--- scripts/index_generator.py
+++ scripts/index_generator.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/python3
# -*- coding: utf-8 -*-
# vim:fenc=utf-8
#
diff --git tools/base/fpgaport/fpgaport tools/base/fpgaport/fpgaport
index af0beafdcb57..bbb47654c873 100644
--- tools/base/fpgaport/fpgaport
+++ tools/base/fpgaport/fpgaport
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/fpgadiag/fpgadiag tools/extra/fpgadiag/fpgadiag
index 98063daf8476..c798d26b4e2d 100755
--- tools/extra/fpgadiag/fpgadiag
+++ tools/extra/fpgadiag/fpgadiag
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/python3
# Copyright(c) 2019, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/fpgadiag/mux.py tools/extra/fpgadiag/mux.py
index 4752973c18d3..7396a1b6995a 100644
--- tools/extra/fpgadiag/mux.py
+++ tools/extra/fpgadiag/mux.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/fpgabist/bist_app.py tools/extra/pac/fpgabist/bist_app.py
index 3eb0bf3672d4..b7578740dbaf 100644
--- tools/extra/pac/fpgabist/bist_app.py
+++ tools/extra/pac/fpgabist/bist_app.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/fpgabist/bist_common.py tools/extra/pac/fpgabist/bist_common.py
index ee9ca852cb53..f978698c4ba9 100644
--- tools/extra/pac/fpgabist/bist_common.py
+++ tools/extra/pac/fpgabist/bist_common.py
@@ -1,3 +1,4 @@
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/fpgabist/bist_def.py tools/extra/pac/fpgabist/bist_def.py
index a8f8583910fb..b413627b044a 100644
--- tools/extra/pac/fpgabist/bist_def.py
+++ tools/extra/pac/fpgabist/bist_def.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/fpgabist/bist_dma.py tools/extra/pac/fpgabist/bist_dma.py
index 7843828e08a7..085fa1bf426b 100644
--- tools/extra/pac/fpgabist/bist_dma.py
+++ tools/extra/pac/fpgabist/bist_dma.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/fpgabist/bist_nlb0.py tools/extra/pac/fpgabist/bist_nlb0.py
index 8bcc34dc4b1e..663429f1586d 100644
--- tools/extra/pac/fpgabist/bist_nlb0.py
+++ tools/extra/pac/fpgabist/bist_nlb0.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2018, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/fpgabist/bist_nlb3.py tools/extra/pac/fpgabist/bist_nlb3.py
index e54333c9d018..02b9e7fd120e 100644
--- tools/extra/pac/fpgabist/bist_nlb3.py
+++ tools/extra/pac/fpgabist/bist_nlb3.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/fpgabist/fpgabist tools/extra/pac/fpgabist/fpgabist
index f0d477209ae4..0825587e7722 100755
--- tools/extra/pac/fpgabist/fpgabist
+++ tools/extra/pac/fpgabist/fpgabist
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/fpgaflash/fpgaflash tools/extra/pac/fpgaflash/fpgaflash
index ff22bd583c43..c14faea6c11d 100755
--- tools/extra/pac/fpgaflash/fpgaflash
+++ tools/extra/pac/fpgaflash/fpgaflash
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2019, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/fpgaflash/super-rsu tools/extra/pac/fpgaflash/super-rsu
index cd2e129c3068..fd3f1bb42bea 100755
--- tools/extra/pac/fpgaflash/super-rsu
+++ tools/extra/pac/fpgaflash/super-rsu
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2019, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/pac_hssi_config/pac_hssi_config.py tools/extra/pac/pac_hssi_config/pac_hssi_config.py
index 83d52b5374ab..0eea7221ea9a 100755
--- tools/extra/pac/pac_hssi_config/pac_hssi_config.py
+++ tools/extra/pac/pac_hssi_config/pac_hssi_config.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2018, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pac/pyfpgaflash/fpgaflash tools/extra/pac/pyfpgaflash/fpgaflash
index 28bf85235e6f..fa0eb473934a 100755
--- tools/extra/pac/pyfpgaflash/fpgaflash
+++ tools/extra/pac/pyfpgaflash/fpgaflash
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/packager/afu_json_mgr.py tools/extra/packager/afu_json_mgr.py
index 0e31ec2646f1..d4bc864ddaf7 100755
--- tools/extra/packager/afu_json_mgr.py
+++ tools/extra/packager/afu_json_mgr.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/packager/jsonschema-2.3.0/json/bin/jsonschema_suite tools/extra/packager/jsonschema-2.3.0/json/bin/jsonschema_suite
index 96108c86ba25..91592830263e 100755
--- tools/extra/packager/jsonschema-2.3.0/json/bin/jsonschema_suite
+++ tools/extra/packager/jsonschema-2.3.0/json/bin/jsonschema_suite
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/python3
from __future__ import print_function
import sys
import textwrap
diff --git tools/extra/packager/packager.py tools/extra/packager/packager.py
index 859932700e3d..f6224dba369d 100755
--- tools/extra/packager/packager.py
+++ tools/extra/packager/packager.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pyfpgadiag/opae/tools/fpgadiag/fpgadiag.py tools/extra/pyfpgadiag/opae/tools/fpgadiag/fpgadiag.py
index cb9971175ef3..9aa03755fb3d 100644
--- tools/extra/pyfpgadiag/opae/tools/fpgadiag/fpgadiag.py
+++ tools/extra/pyfpgadiag/opae/tools/fpgadiag/fpgadiag.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2018, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pyfpgadiag/opae/tools/fpgadiag/fpgamux.py tools/extra/pyfpgadiag/opae/tools/fpgadiag/fpgamux.py
index 2a2a507f223e..5cd267bdab03 100644
--- tools/extra/pyfpgadiag/opae/tools/fpgadiag/fpgamux.py
+++ tools/extra/pyfpgadiag/opae/tools/fpgadiag/fpgamux.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2018, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pyfpgadiag/opae/utils/byteutils.py tools/extra/pyfpgadiag/opae/utils/byteutils.py
index 19e23d77f084..4290bc342424 100644
--- tools/extra/pyfpgadiag/opae/utils/byteutils.py
+++ tools/extra/pyfpgadiag/opae/utils/byteutils.py
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2018, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pypackager/afu_json_mgr.py tools/extra/pypackager/afu_json_mgr.py
index 0e31ec2646f1..d4bc864ddaf7 100755
--- tools/extra/pypackager/afu_json_mgr.py
+++ tools/extra/pypackager/afu_json_mgr.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/extra/pypackager/packager.py tools/extra/pypackager/packager.py
index 859932700e3d..f6224dba369d 100755
--- tools/extra/pypackager/packager.py
+++ tools/extra/pypackager/packager.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/python3
# Copyright(c) 2017, Intel Corporation
#
# Redistribution and use in source and binary forms, with or without
diff --git tools/fpgaflash/fpgaflash tools/fpgaflash/fpgaflash
new file mode 100755
index 000000000000..1340afe3a62d
--- /dev/null
+++ tools/fpgaflash/fpgaflash
@@ -0,0 +1,343 @@
+#! /usr/bin/python3
+# Copyright(c) 2017, Intel Corporation
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import argparse
+import struct
+import fnmatch
+import glob
+import sys
+import tempfile
+import os
+import subprocess
+import fcntl
+import filecmp
+import stat
+import re
+from array import *
+
+
+def check_rpd(ifile):
+ data = ifile.read(0x20)
+ pof_hdr = struct.unpack('IIIIIIII', data)
+ for i in range(0, 3):
+ if pof_hdr[i] != 0xffffffff:
+ print "invalid rpd file"
+ raise Exception
+
+ if pof_hdr[3] != 0x6a6a6a6a:
+ print "invalid rpd file"
+ raise Exception
+
+ return pof_hdr[4]
+
+
+def reverse_bits(x, n):
+ result = 0
+ for i in xrange(n):
+ if (x >> i) & 1:
+ result |= 1 << (n - 1 - i)
+ return result
+
+
+def reverse_bits_in_file(ifile, ofile):
+ bit_rev = array('B')
+ for i in range(0, 256):
+ bit_rev.append(reverse_bits(i, 8))
+
+ while True:
+ ichunk = ifile.read(4096)
+ if not ichunk:
+ break
+
+ ochunk = ''
+ for b in ichunk:
+ ochunk += chr(bit_rev[ord(b)])
+ ofile.write(ochunk)
+
+
+def get_flash_size(dev):
+
+ MEMGETINFO = 0x80204d01
+
+ try:
+ fd = os.open(dev, os.O_SYNC | os.O_RDONLY)
+ except Exception as e:
+ print "failed to open " + dev + ": ", e
+ raise
+
+ ioctl_data = struct.pack('BIIIIIQ', 0, 0, 0, 0, 0, 0, 0)
+
+ try:
+ ret = fcntl.ioctl(fd, MEMGETINFO, ioctl_data)
+ except Exception as e:
+ print "ioctl failed: ", e
+ raise
+
+ ioctl_odata = struct.unpack_from('BIIIIIQ', ret)
+
+ os.close(fd)
+
+ return ioctl_odata[2]
+
+
+def flash_erase(dev, start, nbytes):
+ MEMERASE = 0x40084d02
+
+ try:
+ fd = os.open(dev, os.O_SYNC | os.O_RDWR)
+ except Exception as e:
+ print "failed to open " + dev + ": ", e
+ raise
+
+ ioctl_data = struct.pack('II', start, nbytes)
+
+ try:
+ ret = fcntl.ioctl(fd, MEMERASE, ioctl_data)
+ except Exception as e:
+ print "ioctl failed: ", e
+ raise
+
+ os.close(fd)
+
+
+def flash_write(dev, start, nbytes, ifile):
+
+ try:
+ fd = os.open(dev, os.O_SYNC | os.O_RDWR)
+ except Exception as e:
+ print "failed to open " + dev + ": ", e
+ raise
+
+ os.lseek(fd, start, os.SEEK_SET)
+
+ while nbytes > 0:
+ if (nbytes > 4096):
+ rbytes = 4096
+ else:
+ rbytes = nbytes
+
+ ichunk = ifile.read(rbytes)
+
+ if not ichunk:
+ print "read of flash failed"
+ raise Exception
+
+ os.write(fd, ichunk)
+ nbytes -= rbytes
+
+ os.close(fd)
+
+
+def flash_read(dev, start, nbytes, ofile):
+ try:
+ fd = os.open(dev, os.O_SYNC | os.O_RDONLY)
+ except Exception as e:
+ print "failed to open " + dev + ": ", e
+ raise
+
+ os.lseek(fd, start, os.SEEK_SET)
+
+ while nbytes > 0:
+ if (nbytes > 4096):
+ rbytes = 4096
+ else:
+ rbytes = nbytes
+
+ ichunk = os.read(fd, rbytes)
+
+ if not ichunk:
+ print "read of flash failed"
+ raise Exception
+
+ ofile.write(ichunk)
+ nbytes -= rbytes
+
+ os.close(fd)
+
+
+def parse_args():
+ descr = 'A tool to help update the flash used to configure an '
+ descr += 'Intel FPGA at power up.'
+
+ epi = 'example usage:\n\n'
+ epi += ' fpgaflash user new_image.rpd 0000:04:00.0\n\n'
+
+ fc = argparse.RawDescriptionHelpFormatter
+ parser = argparse.ArgumentParser(description=descr, epilog=epi,
+ formatter_class=fc)
+
+ parser.add_argument('type', help='type of flash programming',
+ choices=['user', 'factory'])
+ parser.add_argument('file', type=argparse.FileType('rb'),
+ help='rpd file to program into flash')
+
+ bdf_help = "bdf of device to program (e.g. 04:00.0 or 0000:04:00.0)"
+ bdf_help += " optional when one device in system"
+
+ parser.add_argument('bdf', nargs='?', help=bdf_help)
+ return parser.parse_args()
+
+
+def get_bdf_mtd_mapping():
+ bdf_map = dict()
+ for fpga in glob.glob('/sys/class/fpga/*'):
+ bdf = os.path.basename(os.readlink(os.path.join(fpga, "device")))
+ if not bdf:
+ continue
+
+ mtds = glob.glob(os.path.join(fpga, 'intel-fpga-fme.*',
+ 'altr-asmip2*', 'mtd', 'mtd*'))
+ for mtd in mtds:
+ if not fnmatch.fnmatchcase(mtd, "*ro"):
+ bdf_map[bdf] = os.path.join('/dev', os.path.basename(mtd))
+ break
+
+ return bdf_map
+
+
+def print_bdf_mtd_mapping(bdf_map):
+ print "\nFPGA cards available for flashing:"
+
+ for key in bdf_map.keys():
+ print " {}".format(key)
+
+ print
+
+
+def normalize_bdf(bdf):
+
+ pat = '[0-9a-fA-F]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]$'
+ if re.match(pat, bdf):
+ return bdf
+
+ if re.match('[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9a-fA-F]$', bdf):
+ return "0000:{}".format(bdf)
+
+
+def update_flash(update_type, ifile, bdf):
+
+ bdf_map = get_bdf_mtd_mapping()
+
+ if len(bdf_map) == 0:
+ print "No FPGA devices found"
+ raise Exception
+
+ mtd_dev = None
+ if not bdf:
+ if len(bdf_map) > 1:
+ print "Must specify a bdf. More than one device found."
+ else:
+ mtd_dev = bdf_map[bdf_map.keys()[0]]
+ else:
+ bdf = normalize_bdf(bdf)
+ if not bdf:
+ print "{} is an invalid bdf".format(bdf)
+ elif bdf not in bdf_map.keys():
+ print "Could not find flash device for {}".format(bdf)
+ else:
+ mtd_dev = bdf_map[bdf]
+
+ if not mtd_dev:
+ print_bdf_mtd_mapping(bdf_map)
+ raise Exception
+
+ try:
+ mode = os.stat(mtd_dev).st_mode
+ except Exception as e:
+ print "Couldn't stat {}".format(mtd_dev)
+ raise
+
+ if not stat.S_ISCHR(mode):
+ print "{} is not a device file.".format(mtd_dev)
+ raise Exception
+
+ flash_size = get_flash_size(mtd_dev)
+
+ print "flash size is {}".format(flash_size)
+
+ start_addr = check_rpd(ifile)
+
+ if update_type == 'factory':
+ start_addr = 0
+
+ ofile = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+
+ ifile.seek(start_addr)
+
+ print "reversing bits"
+ reverse_bits_in_file(ifile, ofile)
+
+ ifile.close()
+ ofile.close()
+
+ print "erasing flash"
+ flash_erase(mtd_dev, start_addr, (flash_size - start_addr))
+
+ nbytes = os.path.getsize(ofile.name)
+ try:
+ rfile = open(ofile.name, 'rb')
+ except Exception as e:
+ print "open({}) failed:".format(ofile.name)
+ raise
+
+ print "writing flash"
+ flash_write(mtd_dev, start_addr, nbytes, rfile)
+ rfile.close()
+
+ vfile = tempfile.NamedTemporaryFile(mode='wb', delete=False)
+
+ print "reading back flash"
+ flash_read(mtd_dev, start_addr, nbytes, vfile)
+
+ vfile.close()
+
+ print "verifying flash"
+
+ retval = filecmp.cmp(ofile.name, vfile.name)
+
+ os.remove(ofile.name)
+ os.remove(vfile.name)
+
+ if retval:
+ print "flash successfully verified"
+ else:
+ print "failed to verify flash"
+ raise Exception
+
+
+if __name__ == "__main__":
+
+ args = parse_args()
+
+ if args.type == 'factory':
+ msg = "Are you sure you want to perform a factory update? [Yes/No]"
+ sys.stdout.write(msg)
+ line = sys.stdin.readline()
+ if line != "Yes\n":
+ sys.exit(1)
+
+ update_flash(args.type, args.file, args.bdf)
diff --git tools/fpgainfo/fpga_common.py tools/fpgainfo/fpga_common.py
new file mode 100644
index 000000000000..b0d4bceee247
--- /dev/null
+++ tools/fpgainfo/fpga_common.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python3
+# Copyright(c) 2017, Intel Corporation
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import os
+
+
+class bitmask(object):
+ def __init__(self, bit_lo, bit_hi=None):
+ value = 0
+ for n in range(bit_lo, (bit_hi or bit_lo) + 1):
+ value |= (1 << n)
+ self.mask = value
+ self.type = int if bit_hi else bool
+ self.bit_lo = bit_lo
+
+ def __call__(self, value):
+ return self.type(self.mask & value) >> self.bit_lo
+
+
+class fpga_command(object):
+ commands = {}
+
+ @classmethod
+ def register_command(cls, subparsers, command_class):
+ assert(
+ command_class.name not in cls.commands
+ ), 'Command entry already exists!!'
+ subparser = subparsers.add_parser(command_class.name)
+ cls.commands[command_class.name] = command_class(subparser)
+
+ def __init__(self, parser):
+ self.parser = parser
+ self.args(self.parser)
+ self.parser.set_defaults(func=self.run)
+
+ def args(self, parser):
+ parser.add_argument('-b', '--bus',
+ help='pcie bus number of resource')
+
+ parser.add_argument('-d', '--device',
+ help='pcie device number of resource')
+
+ parser.add_argument('-f', '--function',
+ help='pcie function number of resource')
+
+ parser.add_argument('--json', action='store_true', default=False,
+ help='Display information as JSON string')
+
+ def run(self, args):
+ raise NotImplementedError
+
+ def fme_feature_is_supported(self, path):
+ return os.path.isfile(path)
diff --git tools/fpgainfo/fpga_fmeinfo.py tools/fpgainfo/fpga_fmeinfo.py
new file mode 100644
index 000000000000..dce5bd302341
--- /dev/null
+++ tools/fpgainfo/fpga_fmeinfo.py
@@ -0,0 +1,58 @@
+#!/usr/bin/python3
+# Copyright(c) 2017, Intel Corporation
+##
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+##
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+##
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+import json
+import fpga_common
+import sysfs
+
+
+class fme_command(fpga_common.fpga_command):
+ name = "fme"
+ properties = ['version',
+ 'ports_num',
+ 'socket_id',
+ 'bitstream_id',
+ 'bitstream_metadata',
+ 'pr.interface_id',
+ 'object_ID']
+
+ def run(self, args):
+ info = sysfs.sysfsinfo()
+ json_data = []
+ for fme in info.fme(**vars(args)):
+ if args.json:
+ json_data.append(fme.to_dict())
+ else:
+ fme.print_info("//****** FME ******//", *self.properties)
+ if args.json:
+ print(json.dumps(json_data, indent=4, sort_keys=False))
+
+
+if __name__ == "__main__":
+ import argparse
+ parser = argparse.ArgumentParser()
+ args = parser.parse_args()
+ args.func(args)
diff --git tools/fpgainfo/fpga_portinfo.py tools/fpgainfo/fpga_portinfo.py
new file mode 100644
index 000000000000..2d63b245edcd
--- /dev/null
+++ tools/fpgainfo/fpga_portinfo.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python3
+# Copyright(c) 2017, Intel Corporation
+##
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+##
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+##
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+import json
+import fpga_common
+import sysfs
+
+
+class port_command(fpga_common.fpga_command):
+ name = "port"
+
+ properties = ['afu_id',
+ 'object_ID']
+
+ def run(self, args):
+ info = sysfs.sysfsinfo()
+ json_data = []
+ for port in info.port(**vars(args)):
+ if args.json:
+ json_data.append(port.to_dict())
+ else:
+ port.print_info("//****** PORT ******//", *self.properties)
+
+ if args.json:
+ print(json.dumps(json_data, indent=4, sort_keys=False))
+
+
+if __name__ == "__main__":
+ import argparse
+ parser = argparse.ArgumentParser()
+ args = parser.parse_args()
+ args.func(args)
diff --git tools/fpgainfo/fpgaerr.py tools/fpgainfo/fpgaerr.py
new file mode 100644
index 000000000000..f1ff29e97186
--- /dev/null
+++ tools/fpgainfo/fpgaerr.py
@@ -0,0 +1,329 @@
+#!/usr/bin/python3
+# Copyright(c) 2017, Intel Corporation
+##
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+##
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+##
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+import json
+from sysfs import sysfsinfo
+from fpga_common import (
+ fpga_command,
+ bitmask)
+
+
+class error_mask(object):
+ _name = None
+ _bitmasks = []
+
+ def __init__(self, value):
+ self.value = value
+
+ def __unicode__(self):
+ s = u'0x{:02x}'.format(self.value)
+ if self.value:
+ for name, bm in self._bitmasks:
+ err = bm(self.value)
+ if err:
+ s += u'\n {:20} : 0x{:02X}'.format(name, err)
+ return s
+
+ def to_dict(self, include_bdf=False):
+ if self._bitmasks:
+ data = (dict([(name, bm(self.value))
+ for (name, bm) in self._bitmasks]))
+ return data
+ else:
+ return self.value
+
+
+class fme_errors(error_mask):
+ _name = "fme"
+ _file = "fme-errors/errors"
+ _bitmasks = [
+ ("CvlCdcParErro0", bitmask(17, 19)),
+ ("Pcie1CdcParErr", bitmask(12, 16)),
+ ("Pcie0CdcParErr", bitmask(7, 11)),
+ ("MBPErr", bitmask(6, 6)),
+ ("AfuAccessModeErr", bitmask(5, 5)),
+ ("IommuParityErr", bitmask(4, 4)),
+ ("KtiCdcParityErr", bitmask(2, 3)),
+ ("FabricFifoUOflow", bitmask(1, 1)),
+ ("FabricErr", bitmask(0, 0)),
+ ]
+
+
+class bbs_errors(error_mask):
+ _name = "bbs"
+ _file = "bbs_errors"
+ _bitmasks = [
+ ("InjectedCatastErr", bitmask(11, 11)),
+ ("ThermCatastErr", bitmask(10, 10)),
+ ("CrcCatastErr", bitmask(9, 9)),
+ ("InjectedFatalErr", bitmask(8, 8)),
+ ("PciePoisonErr", bitmask(7, 7)),
+ ("FabricFatalErr", bitmask(6, 6)),
+ ("IommuFatalErr", bitmask(5, 5)),
+ ("DramFatalErr", bitmask(4, 4)),
+ ("KtiProtoFatalErr", bitmask(3, 3)),
+ ("CciFatalErr", bitmask(2, 2)),
+ ("TagCchFatalErr", bitmask(1, 1)),
+ ("KtiLinkFatalErr", bitmask(0, 0)),
+ ]
+
+
+class port_errors(error_mask):
+ _name = "port"
+ _file = "errors"
+ _bitmasks = [
+ ("VfFlrAccessError", bitmask(51, 51)),
+ ("Ap6Event", bitmask(50, 50)),
+ ("PMRError", bitmask(49, 49)),
+ ("PageFault", bitmask(48, 48)),
+ ("VgaMemRangeError", bitmask(47, 47)),
+ ("LegRangeHighError", bitmask(46, 46)),
+ ("LegRangeLowError", bitmask(45, 45)),
+ ("GenProtRangeError", bitmask(44, 44)),
+ ("L1prMesegError", bitmask(43, 43)),
+ ("L1prSmrr2Error", bitmask(42, 42)),
+ ("L1prSmrrError", bitmask(41, 41)),
+ ("TxReqCounterOverflow", bitmask(40, 40)),
+ ("UnexpMMIOResp", bitmask(34, 34)),
+ ("TxCh2FifoOverflow", bitmask(33, 33)),
+ ("MMIOTimedOut", bitmask(32, 32)),
+ ("TxCh1NonZeroSOP", bitmask(24, 24)),
+ ("TxCh1IncorrectAddr", bitmask(23, 23)),
+ ("TxCh1DataPayloadOverrun", bitmask(22, 22)),
+ ("TxCh1InsufficientData", bitmask(21, 21)),
+ ("TxCh1Len4NotAligned", bitmask(20, 20)),
+ ("TxCh1Len2NotAligned", bitmask(19, 19)),
+ ("TxCh1Len3NotSupported", bitmask(18, 18)),
+ ("TxCh1InvalidReqEncoding", bitmask(17, 17)),
+ ("TxCh1Overflow", bitmask(16, 16)),
+ ("MMIOWrWhileRst", bitmask(10, 10)),
+ ("MMIORdWhileRst", bitmask(9, 9)),
+ ("TxCh0Len4NotAligned", bitmask(4, 4)),
+ ("TxCh0Len2NotAligned", bitmask(3, 3)),
+ ("TxCh0Len3NotSupported", bitmask(2, 2)),
+ ("TxCh0InvalidReqEncoding", bitmask(1, 1)),
+ ("TxCh0Overflow", bitmask(0, 0)),
+ ]
+
+
+class pcie0_errors(error_mask):
+ _name = "pcie0"
+ _file = "pcie0_errors"
+ _bitmasks = [
+ ("FunctTypeErr", bitmask(63, 63)),
+ ("VFNumb", bitmask(62, 62)),
+ ("RxPoisonTlpErr", bitmask(9, 9)),
+ ("ParityErr", bitmask(8, 8)),
+ ("CompTimeOutErr", bitmask(7, 7)),
+ ("CompStatErr", bitmask(6, 6)),
+ ("CompTagErr", bitmask(5, 5)),
+ ("MRLengthErr", bitmask(4, 4)),
+ ("MRAddrErr", bitmask(3, 3)),
+ ("MWLengthErr", bitmask(2, 2)),
+ ("MWAddrErr", bitmask(1, 1)),
+ ("FormatTypeErr", bitmask(0, 0)),
+ ]
+
+
+class pcie1_errors(error_mask):
+ _name = "pcie1"
+ _file = "pcie1_errors"
+ _bitmasks = [
+ ("RxPoisonTlpErr", bitmask(9, 9)),
+ ("ParityErr", bitmask(8, 8)),
+ ("CompTimeOutErr", bitmask(7, 7)),
+ ("CompStatErr", bitmask(6, 6)),
+ ("CompTagErr", bitmask(5, 5)),
+ ("MRLengthErr", bitmask(4, 4)),
+ ("MRAddrErr", bitmask(3, 3)),
+ ("MWLengthErr", bitmask(2, 2)),
+ ("MWAddrErr", bitmask(1, 1)),
+ ("FormatTypeErr", bitmask(0, 0)),
+ ]
+
+
+class gbs_errors(error_mask):
+ _name = "gbs"
+ _file = "gbs_errors"
+ _bitmasks = [
+ ("MBPErr", bitmask(12, 12)),
+ ("PowerThreshAP2", bitmask(11, 11)),
+ ("PowerThreshAP1", bitmask(10, 10)),
+ ("TempThreshAP6", bitmask(9, 9)),
+ ("InjectedWarningErr", bitmask(6, 6)),
+ ("AfuAccessModeErr", bitmask(5, 5)),
+ ("ProcHot", bitmask(4, 4)),
+ ("PortFatalErr", bitmask(3, 3)),
+ ("PcieError", bitmask(2, 2)),
+ ("TempThreshAP2", bitmask(1, 1)),
+ ("TempThreshAP1", bitmask(0, 0)),
+ ]
+
+
+class first_errors(error_mask):
+ _name = "first_error"
+ _file = "first_error"
+ _bitmasks = [
+ ("TxReqCounterOverflow", bitmask(40, 40)),
+ ("TxCh2FifoOverflow", bitmask(33, 33)),
+ ("MMIOTimedOut", bitmask(32, 32)),
+ ("TxCh1IllegalVCsel", bitmask(25, 25)),
+ ("TxCh1NonZeroSOP", bitmask(24, 24)),
+ ("TxCh1IncorrectAddr", bitmask(23, 23)),
+ ("TxCh1DataPayloadOverrun", bitmask(22, 22)),
+ ("TxCh1InsufficientData", bitmask(21, 21)),
+ ("TxCh1Len4NotAligned", bitmask(20, 20)),
+ ("TxCh1Len2NotAligned", bitmask(19, 19)),
+ ("TxCh1Len3NotSupported", bitmask(18, 18)),
+ ("TxCh1InvalidReqEncoding", bitmask(17, 17)),
+ ("TxCh1Overflow", bitmask(16, 16)),
+ ("TxCh0Len4NotAligned", bitmask(4, 4)),
+ ("TxCh0Len2NotAligned", bitmask(3, 3)),
+ ("TxCh0Len3NotSupported", bitmask(2, 2)),
+ ("TxCh0InvalidReqEncoding", bitmask(1, 1)),
+ ("TxCh0Overflow", bitmask(0, 0)),
+ ]
+
+
+class nonfatal_errors(error_mask):
+ _name = "nonfatal"
+ _file = "nonfatal_errors"
+ _bitmasks = [
+ ("MBPErr", bitmask(12, 12)),
+ ("PowerThreshAP2", bitmask(11, 11)),
+ ("PowerThreshAP1", bitmask(10, 10)),
+ ("TempThreshAP6", bitmask(9, 9)),
+ ("InjectedWarningErr", bitmask(6, 6)),
+ ("AfuAccessModeErr", bitmask(5, 5)),
+ ("ProcHot", bitmask(4, 4)),
+ ("PortFatalErr", bitmask(3, 3)),
+ ("PcieError", bitmask(2, 2)),
+ ("TempThreshAP2", bitmask(1, 1)),
+ ("TempThreshAP1", bitmask(0, 0)),
+ ]
+
+
+class fatal_errors(error_mask):
+ _name = "catfatal"
+ _file = "catfatal_errors"
+ _bitmasks = [
+ ("InjectedCatastErr", bitmask(11, 11)),
+ ("ThermCatastErr", bitmask(10, 10)),
+ ("CrcCatastErr", bitmask(9, 9)),
+ ("InjectedFatalErr", bitmask(8, 8)),
+ ("PciePoisonErr", bitmask(7, 7)),
+ ("FabricFatalErr", bitmask(6, 6)),
+ ("IommuFatalErr", bitmask(5, 5)),
+ ("DramFatalErr", bitmask(4, 4)),
+ ("KtiProtoFatalErr", bitmask(3, 3)),
+ ("CciFatalErr", bitmask(2, 2)),
+ ("TagCchFatalErr", bitmask(1, 1)),
+ ("KtiLinkFatalErr", bitmask(0, 0)),
+ ]
+
+
+error_classes = {
+ "fme": {"errors": fme_errors,
+ "pcie0_errors": pcie0_errors,
+ "pcie1_errors": pcie1_errors,
+ "gbs_errors": gbs_errors,
+ "bbs_errors": bbs_errors,
+ "nonfatal_errors": nonfatal_errors,
+ "catfatal_errors": fatal_errors,
+ "first_error": error_mask,
+ "next_error": error_mask},
+ "port": {"errors": port_errors,
+ "first_error": first_errors}
+}
+
+
+def set_error_parsers(err_feature, err_classes):
+ for name, err_class in err_classes.iteritems():
+ if hasattr(err_feature, name):
+ value = getattr(err_feature, name)
+ setattr(err_feature, name, err_class(value.fget(err_feature)))
+
+
+class errors_command(fpga_command):
+ name = 'errors'
+
+ def __init__(self, parser):
+ super(errors_command, self).__init__(parser)
+
+ def args(self, parser):
+ super(errors_command, self).args(parser)
+ parser.add_argument('-c', '--clear', action='store_true',
+ default=False,
+ help='specify whether or not'
+ ' to clear error registers')
+
+ parser.add_argument('which',
+ choices=['fme', 'port', 'all'],
+ default='fme',
+ help='specify what kind of errors to operate on')
+
+ def run(self, args):
+ info = sysfsinfo()
+ resources = []
+ # let's monkey patch the error sysfs resource
+ # by looking at our dicionaries of error parsers and matching
+ # them with the corresponding property and then replacing
+ # that property attribute with an instance of the error parsing
+ # class with the value of the property
+ if args.which == "fme" or args.which == "all":
+ for fme in info.fme(**vars(args)):
+ set_error_parsers(fme.errors, error_classes["fme"])
+ resources.append(fme.errors)
+
+ if args.which == "port" or args.which == "all":
+ for port in info.port(**vars(args)):
+ set_error_parsers(port.errors, error_classes["port"])
+ resources.append(port.errors)
+
+ json_data = []
+ for r in resources:
+ if args.json:
+ json_data.append(r.to_dict(True))
+ else:
+ r.print_info(
+ "//****** {} ******//".format(r.name()))
+
+ if args.json:
+ print(json.dumps(json_data, indent=4, sort_keys=False))
+
+ if args.clear:
+ for r in resources:
+ if not r.clear():
+ print("ERROR: Could not clear errors for resource {}.\n"
+ "Are you running as root?\n".format(r.sysfs_path))
+
+
+if __name__ == "__main__":
+ import argparse
+ parser = argparse.ArgumentParser()
+ errors_command(parser)
+ args = parser.parse_args()
+ args.func(args)
diff --git tools/fpgainfo/fpgainfo tools/fpgainfo/fpgainfo
new file mode 100755
index 000000000000..f207d693bdef
--- /dev/null
+++ tools/fpgainfo/fpgainfo
@@ -0,0 +1,58 @@
+#!/usr/bin/python3
+# Copyright(c) 2017, Intel Corporation
+##
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+##
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+##
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+import argparse
+import fpga_common
+import fpgaerr
+import fpgapwr
+import fpgatemp
+import fpga_fmeinfo
+import fpga_portinfo
+import logging
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser()
+ subparsers = parser.add_subparsers(title='fpga commands')
+
+ fpga_common.fpga_command.register_command(subparsers,
+ fpgaerr.errors_command)
+
+ fpga_common.fpga_command.register_command(subparsers,
+ fpgapwr.power_command)
+
+ fpga_common.fpga_command.register_command(subparsers,
+ fpgatemp.temp_command)
+
+ fpga_common.fpga_command.register_command(subparsers,
+ fpga_fmeinfo.fme_command)
+
+ fpga_common.fpga_command.register_command(subparsers,
+ fpga_portinfo.port_command)
+
+ args = parser.parse_args()
+ logging.basicConfig(filename='fpgainfo.log')
+ args.func(args)
diff --git tools/fpgainfo/fpgatemp.py tools/fpgainfo/fpgatemp.py
new file mode 100644
index 000000000000..8e9dda8e90af
--- /dev/null
+++ tools/fpgainfo/fpgatemp.py
@@ -0,0 +1,59 @@
+#!/usr/bin/python3
+# Copyright(c) 2017, Intel Corporation
+##
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+##
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+##
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+import fpga_common as cmd
+import sysfs
+
+
+def print_celcius(value):
+ return u'{}\u00b0 C'.format(value)
+
+
+class temp_command(cmd.fpga_command):
+ name = "temp"
+ thermal_props = [("temperature", print_celcius),
+ ("threshold1", print_celcius),
+ ("threshold1_policy", int),
+ ("threshold1_reached", bool),
+ ("threshold2", print_celcius),
+ ("threshold2_reached", bool),
+ ("threshold_trip", print_celcius)]
+
+ def run(self, args):
+ info = sysfs.sysfsinfo()
+ for fme in info.fme(**vars(args)):
+ fme.thermal_mgmt.print_info("//****** THERMAL ******//",
+ *[p[0] for p in self.thermal_props],
+ **dict(self.thermal_props))
+
+
+if __name__ == "__main__":
+ import argparse
+ parser = argparse.ArgumentParser()
+ temp_command(parser)
+ args = parser.parse_args()
+ args.func(args)
diff --git tools/fpgainfo/sysfs.py tools/fpgainfo/sysfs.py
new file mode 100644
index 000000000000..2b148e10d74c
--- /dev/null
+++ tools/fpgainfo/sysfs.py
@@ -0,0 +1,442 @@
+#!/usr/bin/python3
+# Copyright(c) 2017, Intel Corporation
+##
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+##
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+##
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+import glob
+import inspect
+import json
+import logging
+import os
+import re
+import uuid
+
+pattern = ('.*/\d+:(?P<bus>\w{2}):'
+ '(?P<device>\d{2})\.(?P<function>\d).*')
+bdf_pattern = re.compile(pattern)
+
+ROOT_PATH = '/sys/class/fpga'
+FPGA_DEVICE = os.path.join(ROOT_PATH, 'intel-fpga-dev.{instance_id}')
+FME_DEVICE = os.path.join(FPGA_DEVICE, 'intel-fpga-fme.{instance_id}')
+PORT_DEVICE = os.path.join(FPGA_DEVICE, 'intel-fpga-port.{instance_id}')
+
+DCP_ID = 0x09c4
+
+
+def read_bdf(path):
+ symlink = os.readlink(path)
+ m = bdf_pattern.match(symlink)
+ data = m.groupdict() if m else {}
+ return dict([(k, int(v, 16)) for (k, v) in data.iteritems()])
+
+
+class sysfs_filter(object):
+ def __init__(self, **kwargs):
+ self._bus = None
+ self._device = None
+ self._function = None
+ for attr in ('bus', 'device', 'function'):
+ value = kwargs.get(attr)
+ if isinstance(value, str) or isinstance(value, unicode):
+ value = int(value, 16)
+ setattr(self, '_{}'.format(attr), value)
+
+ def __call__(self, resource):
+ if self._bus is not None and self._bus != resource.bus:
+ return False
+ if self._device is not None and self._device != resource.device:
+ return False
+ if self._function is not None and self._function != resource.function:
+ return False
+ return True
+
+
+def add_static_property(sysfs_path):
+ return property(lambda s_: s_.parse_sysfs(sysfs_path),
+ lambda s_, v_: s_.write_sysfs(v_, sysfs_path))
+
+
+def add_dynamic_property(obj, property_name, sysfs_path=None):
+ sysfs_path = sysfs_path or property_name
+
+ def getter(self_):
+ return self_.parse_sysfs(sysfs_path)
+
+ def setter(self_, v_):
+ self_.write_sysfs(v_, sysfs_path)
+
+ if obj.sysfs_path_exists(sysfs_path):
+ setattr(obj, property_name,
+ property(getter, setter))
+
+
+class sysfs_resource(object):
+ def __init__(self, path, instance_id, device_id=None, **kwargs):
+ self._path = path
+ self._instance_id = instance_id
+ self._device_id = device_id
+ self._bus = kwargs.get('bus')
+ self._device = kwargs.get('device')
+ self._function = kwargs.get('function')
+
+ def enum_props(self, as_string=False):
+ def pred(xxx_todo_changeme):
+ (k, v) = xxx_todo_changeme
+ if k in dir(sysfs_resource):
+ return False
+ if inspect.ismethod(v):
+ return False
+ if k.startswith('_'):
+ return False
+ return True
+
+ for k, v in filter(pred, inspect.getmembers(self)):
+ k_, v_ = k, v
+ if isinstance(v, property):
+ k_, v_ = k, v.fget(self)
+ elif not as_string and hasattr(v, 'to_dict') and k != '__class__':
+ k_, v_ = k, v.to_dict()
+ if as_string:
+ yield k_, unicode(v_)
+ else:
+ yield k_, v_
+
+ def to_dict(self, include_bdf=False):
+ data = {}
+ for k, v in self.enum_props():
+ data[k] = v
+ if include_bdf:
+ root_data = {}
+ root_data["class_path"] = self.sysfs_path
+ root_data["dev_path"] = os.path.realpath(self.sysfs_path)
+ root_data["bus"] = self.bus
+ root_data["device"] = self.device
+ root_data["function"] = self.function
+ data["pcie_info"] = root_data
+
+ return data
+
+ def to_json(self):
+ return json.dumps(self.to_dict(), indent=4, sort_keys=False)
+
+ def sysfs_path_exists(self, *paths):
+ return os.path.exists(os.path.join(self._path, *paths))
+
+ def read_sysfs(self, *paths):
+ filepath = os.path.join(self._path, *paths)
+ if not os.path.exists(filepath):
+ print("WARNING: {} not found".format(filepath))
+ return None
+ with open(filepath, 'r') as fd:
+ return fd.read().strip()
+
+ def write_sysfs(self, value, *paths):
+ filepath = os.path.join(self._path, *paths)
+ if not os.path.exists(filepath):
+ print("WARNING: {} not found".format(filepath))
+ return None
+ with open(filepath, 'w') as fd:
+ return fd.write(value)
+
+ def parse_sysfs(self, *paths):
+ value = self.read_sysfs(*paths)
+ try:
+ value = int(value)
+ except ValueError:
+ try:
+ value = int(value, 16)
+ except Exception:
+ logging.warn("Could not parse value: {}".format(value))
+ finally:
+ return value
+
+ def print_info(self, label, *props, **kwargs):
+ print(label.upper())
+ print('{:22} : {}'.format('Class Path', self.sysfs_path))
+ print(
+ '{:22} : {}'.format(
+ 'Device Path',
+ os.path.realpath(
+ self.sysfs_path)))
+ print('{:22} : 0x{:02X}'.format('Bus', self.bus))
+ print('{:22} : 0x{:02X}'.format('Device', self.device))
+ print('{:22} : 0x{:02X}'.format('Function', self.function))
+
+ prop_values = [(k, v) for k, v in self.enum_props(as_string=True)]
+ if props:
+ def get_value(key):
+ namespaces = key.split('.')
+ obj = self
+ try:
+ while len(namespaces) > 1:
+ obj = getattr(obj, namespaces.pop(0))
+ p = getattr(obj, namespaces[0])
+ if isinstance(p, property):
+ p = p.fget(self)
+ return p
+ except AttributeError:
+ pass
+
+ prop_values = [(k, get_value(k)) for k in props]
+
+ for k, v in prop_values:
+ if v is not None:
+ value = kwargs.get(k)(v) if k in kwargs else v
+ subbed = re.sub('[_\.]', ' ', k)
+ label = ' '.join([_.capitalize() for _ in subbed.split()])
+ print(u'{:22} : {}'.format(label, value))
+
+ print('\n')
+
+ @property
+ def sysfs_path(self):
+ return self._path
+
+ @property
+ def instance_id(self):
+ return self._instance_id
+
+ @property
+ def bus(self):
+ return self._bus
+
+ @property
+ def device(self):
+ return self._device
+
+ @property
+ def function(self):
+ return self._function
+
+ @property
+ def device_id(self):
+ return self._device_id
+
+
+class pr_feature(sysfs_resource):
+ @property
+ def interface_id(self):
+ return str(uuid.UUID(self.read_sysfs("interface_id")))
+
+
+class sysfs_device(sysfs_resource):
+ @property
+ def device_id(self):
+ return self.parse_sysfs("device")
+
+
+class power_mgmt_feature(sysfs_resource):
+ def __init__(self, path, instance_id, device_id, **kwargs):
+ super(power_mgmt_feature, self).__init__(path, instance_id, device_id,
+ **kwargs)
+ if device_id != DCP_ID:
+ self.consumed = add_static_property("consumed")
+
+
+class thermal_feature(sysfs_resource):
+ def __init__(self, path, instance_id, device_id, **kwargs):
+ super(thermal_feature, self).__init__(path, instance_id, device_id,
+ **kwargs)
+ self.temperature = add_static_property("temperature")
+ if device_id != DCP_ID:
+ self.threshold1 = add_static_property("threshold1")
+ self.threshold1_policy = add_static_property("threshold1_policy")
+ self.threshold1_reached = add_static_property("threshold1_reached")
+ self.threshold2 = add_static_property("threshold2")
+ self.threshold2_reached = add_static_property("threshold2_reached")
+ self.threshold_trip = add_static_property("threshold_trip")
+
+
+class errors_feature(sysfs_resource):
+ revision = add_static_property("revision")
+
+ def __init__(self, path, instance_id, device_id, **kwargs):
+ super(errors_feature, self).__init__(path, instance_id, device_id,
+ **kwargs)
+ self._errors_file = None
+ self._clear_file = None
+ self._name = "errors"
+
+ def name(self):
+ return self._name
+
+ def clear(self):
+ value = self.parse_sysfs(self._errors_file)
+ try:
+ if value:
+ self.write_sysfs(hex(value), self._clear_file)
+ return True
+ except IOError:
+ logging.warn(
+ "Could not clear errors: {}."
+ "Are you running as root?".format(
+ self._clear_file))
+ return False
+
+
+class fme_errors(errors_feature):
+ def __init__(self, path, instance_id, device_id, **kwargs):
+ super(fme_errors, self).__init__(path, instance_id, device_id,
+ **kwargs)
+ self._name = "fme errors"
+ self._errors_file = "fme-errors/errors"
+ self._clear_file = "fme-errors/clear"
+ add_dynamic_property(self, "errors", "fme-errors/errors")
+ add_dynamic_property(self, "first_error", "fme-errors/first_error")
+ add_dynamic_property(self, "next_error", "fme-errors/next_error")
+ add_dynamic_property(self, "pcie0_errors")
+ if device_id != DCP_ID:
+ add_dynamic_property(self, "pcie1_errors")
+ add_dynamic_property(self, "bbs_errors")
+ add_dynamic_property(self, "gbs_errors")
+ add_dynamic_property(self, "catfatal_errors")
+ add_dynamic_property(self, "nonfatal_errors")
+
+
+class port_errors(errors_feature):
+ def __init__(self, path, instance_id, device_id, **kwargs):
+ super(port_errors, self).__init__(path, instance_id, device_id,
+ **kwargs)
+ self._name = "port errors"
+ self._errors_file = "errors"
+ self._clear_file = "clear"
+ add_dynamic_property(self, "errors")
+ add_dynamic_property(self, "first_error")
+
+
+class fme_info(sysfs_resource):
+ def __init__(self, path, instance_id, device_id, **kwargs):
+ super(fme_info, self).__init__(path, instance_id, device_id, **kwargs)
+ self.pr = pr_feature(os.path.join(path, 'pr'), instance_id, device_id,
+ **kwargs)
+ self.power_mgmt = power_mgmt_feature(os.path.join(path, 'power_mgmt'),
+ instance_id,
+ device_id,
+ **kwargs)
+ self.thermal_mgmt = thermal_feature(os.path.join(path, "thermal_mgmt"),
+ instance_id,
+ device_id,
+ **kwargs)
+ self.errors = fme_errors(os.path.join(path, "errors"),
+ instance_id,
+ device_id,
+ **kwargs)
+
+ @property
+ def version(self):
+ return self.read_sysfs("version")
+
+ @property
+ def ports_num(self):
+ return self.read_sysfs("ports_num")
+
+ @property
+ def socket_id(self):
+ return self.read_sysfs("socket_id")
+
+ @property
+ def bitstream_id(self):
+ if self.device_id != DCP_ID:
+ return self.read_sysfs("bitstream_id")
+
+ @property
+ def bitstream_metadata(self):
+ if self.device_id != DCP_ID:
+ return self.read_sysfs("bitstream_metadata")
+
+ @property
+ def object_ID(self):
+ value = self.read_sysfs("dev")
+ valueList = value.split(":")
+ major = valueList[0]
+ minor = valueList[1]
+ objID = ((int(major) & 0xFFF) << 20) | (int(minor) & 0xFFFFF)
+ return hex(objID) + " FPGA_DEVICE"
+
+
+class port_info(sysfs_resource):
+ def __init__(self, path, instance_id, device_id, **kwargs):
+ super(port_info, self).__init__(path, instance_id, device_id, **kwargs)
+ self.errors = port_errors(os.path.join(path, "errors"),
+ instance_id,
+ device_id,
+ **kwargs)
+
+ @property
+ def afu_id(self):
+ return str(uuid.UUID(self.read_sysfs("afu_id")))
+
+ @property
+ def object_ID(self):
+ value = self.read_sysfs("dev")
+ valueList = value.split(":")
+ major = valueList[0]
+ minor = valueList[1]
+ objID = ((int(major) & 0xFFF) << 20) | (int(minor) & 0xFFFFF)
+ return hex(objID) + " FPGA_ACCELERATOR"
+
+
+class sysfsinfo(object):
+ def __init__(self):
+ self._fmelist = []
+ self._portlist = []
+ sysfs_paths = glob.glob(
+ FPGA_DEVICE.format(instance_id='*'))
+ if not sysfs_paths:
+ print "WARNING: No sysfs paths found"
+ for path in sysfs_paths:
+ bdf = read_bdf(os.path.join(path, 'device'))
+ # strip {instance_id} from the template FPGA_DEVICE
+ # socket id is what comes after this in the real path
+ instance_id = path.strip(FPGA_DEVICE.strip('{instance_id}'))
+ device_id = sysfs_device(os.path.join(path, 'device'),
+ instance_id, None,
+ **bdf).device_id
+ sysfs_fme = FME_DEVICE.format(instance_id=instance_id)
+ sysfs_port = PORT_DEVICE.format(instance_id=instance_id)
+ self._fmelist.append(fme_info(sysfs_fme, instance_id,
+ device_id, **bdf))
+ self._portlist.append(port_info(sysfs_port, instance_id,
+ device_id, **bdf))
+
+ def fme(self, **kwargs):
+ return filter(sysfs_filter(**kwargs), self._fmelist)
+
+ def port(self, **kwargs):
+ return filter(sysfs_filter(**kwargs), self._portlist)
+
+
+if __name__ == "__main__":
+ import argparse
+ import pprint
+ parser = argparse.ArgumentParser()
+ parser.add_argument('-b', '--bus')
+ parser.add_argument('-d', '--device')
+ parser.add_argument('-f', '--function')
+
+ args = parser.parse_args()
+ info = sysfsinfo()
+ fmelist = info.fme(**vars(args))
+ portlist = info.port(**vars(args))
+ pprint.pprint([f.to_dict() for f in fmelist])