FPGA driver support

This patch is not a plugin implementation

Cyborg agent can call driver as follow:
from cyborg.accelerator.drivers.fpga import base
fdriver = base.FPGADriver
 # There maybe more than one vendor fpga card on a host.
venders = fdriver.discover_vendors()
for v in venders:
    dr = fdriver.create(v)
    dr.discover()

intel = base.FPGADriver.create("intel")
intel.program("/fpga/device/path", "/local/path/image")

When call IntelFPGADriver.discover(), we can get the result as follow:
    [{'assignable': False,
      'devices': '0000:5e:00.0',
      'function': 'pf',
      'parent_devices': '',
      'path': '/sys/class/fpga/intel-fpga-dev.0',
      'pr_num': '1',
      'product_id': '0xbcc0',
      'regions': [{'assignable': True,
        'devices': '0000:5e:00.1',
        'function': 'vf',
        'parent_devices': '0000:5e:00.0',
        'path': '/sys/class/fpga/intel-fpga-dev.2',
        'product_id': '0xbcc1',
        'vendor_id': '0x8086'}],
      'vendor_id': '0x8086'},
     {'assignable': True,
      'devices': '0000:be:00.0',
      'function': 'pf',
      'parent_devices': '',
      'path': '/sys/class/fpga/intel-fpga-dev.1',
      'pr_num': '0',
      'product_id': '0xbcc0',
      'vendor_id': '0x8086'}]

Cyborg agent can use it to form it's DB.

Co-Authored-By: Dolpher Du <Dolpher.Du@intel.com>

Change-Id: I132be5ecdb90b385b68b0cdf306d1402ef4bfc04
This commit is contained in:
Shaohe Feng 2018-01-04 09:14:08 +00:00
parent b22761ab0d
commit 5b72422930
13 changed files with 841 additions and 0 deletions

View File

@ -0,0 +1,41 @@
# Copyright 2018 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
import glob
from oslo_log import log as logging
__import__('pkg_resources').declare_namespace(__name__)
__import__(".".join([__package__, 'base']))
LOG = logging.getLogger(__name__)
def load_fpga_vendor_driver():
files = glob.glob(os.path.join(os.path.dirname(__file__), "*/driver*"))
modules = set(map(lambda s: ".".join(s.rsplit(".")[0].rsplit("/", 2)[-2:]),
files))
for m in modules:
try:
__import__(".".join([__package__, m]))
LOG.debug("Successfully loaded FPGA vendor driver: %s." % m)
except ImportError as e:
LOG.error("Failed to load FPGA vendor driver: %s. Details: %s"
% (m, e))
load_fpga_vendor_driver()

View File

@ -0,0 +1,52 @@
# Copyright 2018 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Cyborg FPGA driver implementation.
"""
from cyborg.accelerator.drivers.fpga import utils
VENDOR_MAPS = {"0x8086": "intel"}
class FPGADriver(object):
"""Base class for FPGA drivers.
This is just a virtual FPGA drivers interface.
Vedor should implement their specific drivers.
"""
@classmethod
def create(cls, vendor, *args, **kwargs):
for sclass in cls.__subclasses__():
vendor = VENDOR_MAPS.get(vendor, vendor)
if vendor == sclass.VENDOR:
return sclass(*args, **kwargs)
raise LookupError("Not find the FPGA driver for vendor %s" % vendor)
def __init__(self, *args, **kwargs):
pass
def discover(self):
raise NotImplementedError()
def program(self, device_path, image):
raise NotImplementedError()
@classmethod
def discover_vendors(cls):
return utils.discover_vendors()

View File

@ -0,0 +1,56 @@
# Copyright 2018 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Cyborg Intel FPGA driver implementation.
"""
import subprocess
from cyborg.accelerator.drivers.fpga.base import FPGADriver
from cyborg.accelerator.drivers.fpga.intel import sysinfo
class IntelFPGADriver(FPGADriver):
"""Base class for FPGA drivers.
This is just a virtual FPGA drivers interface.
Vedor should implement their specific drivers.
"""
VENDOR = "intel"
def __init__(self, *args, **kwargs):
pass
def discover(self):
return sysinfo.fpga_tree()
def program(self, device_path, image):
bdf = ""
path = sysinfo.find_pf_by_vf(device_path) if sysinfo.is_vf(
device_path) else device_path
if sysinfo.is_bdf(device_path):
bdf = sysinfo.get_pf_bdf(device_path)
else:
bdf = sysinfo.get_bdf_by_path(path)
bdfs = sysinfo.split_bdf(bdf)
cmd = ["sudo", "fpgaconf"]
for i in zip(["-b", "-d", "-f"], bdfs):
cmd.extend(i)
cmd.append(image)
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
# FIXME Should log p.communicate(), p.stderr
p.wait()
return p.returncode

View File

@ -0,0 +1,162 @@
# Copyright 2018 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Cyborg Intel FPGA driver implementation.
"""
# from cyborg.accelerator.drivers.fpga.base import FPGADriver
import glob
import os
import re
SYS_FPGA = "/sys/class/fpga"
DEVICE = "device"
PF = "physfn"
VF = "virtfn*"
BDF_PATTERN = re.compile(
"^[a-fA-F\d]{4}:[a-fA-F\d]{2}:[a-fA-F\d]{2}\.[a-fA-F\d]$")
DEVICE_FILE_MAP = {"vendor": "vendor_id",
"device": "product_id",
"sriov_numvfs": "pr_num"}
DEVICE_FILE_HANDLER = {}
DEVICE_EXPOSED = ["vendor", "device", "sriov_numvfs"]
def all_fpgas():
# glob.glob1("/sys/class/fpga", "*")
return glob.glob(os.path.join(SYS_FPGA, "*"))
def all_vf_fpgas():
return [dev.rsplit("/", 2)[0] for dev in
glob.glob(os.path.join(SYS_FPGA, "*/device/physfn"))]
def all_pure_pf_fpgas():
return [dev.rsplit("/", 2)[0] for dev in
glob.glob(os.path.join(SYS_FPGA, "*/device/virtfn0"))]
def target_symbolic_map():
maps = {}
for f in glob.glob(os.path.join(SYS_FPGA, "*/device")):
maps[os.path.realpath(f)] = os.path.dirname(f)
return maps
def bdf_path_map():
maps = {}
for f in glob.glob(os.path.join(SYS_FPGA, "*/device")):
maps[os.path.basename(os.path.realpath(f))] = os.path.dirname(f)
return maps
def all_vfs_in_pf_fpgas(pf_path):
maps = target_symbolic_map()
vfs = glob.glob(os.path.join(pf_path, "device/virtfn*"))
return [maps[os.path.realpath(vf)] for vf in vfs]
def all_pf_fpgas():
return [dev.rsplit("/", 2)[0] for dev in
glob.glob(os.path.join(SYS_FPGA, "*/device/sriov_totalvfs"))]
def is_vf(path):
return True if glob.glob(os.path.join(path, "device/physfn")) else False
def find_pf_by_vf(path):
maps = target_symbolic_map()
p = os.path.realpath(os.path.join(path, "device/physfn"))
return maps[p]
def is_bdf(bdf):
return True if BDF_PATTERN.match(bdf) else False
def get_bdf_by_path(path):
return os.path.basename(os.readlink(os.path.join(path, "device")))
def split_bdf(bdf):
return ["0x" + v for v in bdf.replace(".", ":").rsplit(":")[1:]]
def get_pf_bdf(bdf):
path = bdf_path_map().get(bdf)
if path:
path = find_pf_by_vf(path) if is_vf(path) else path
return get_bdf_by_path(path)
return bdf
def fpga_device(path):
infos = {}
def read_line(filename):
with open(filename) as f:
return f.readline().strip()
# NOTE "In 3.x, os.path.walk is removed in favor of os.walk."
for (dirpath, dirnames, filenames) in os.walk(path):
for filename in filenames:
if filename in DEVICE_EXPOSED:
key = DEVICE_FILE_MAP.get(filename) or filename
if key in DEVICE_FILE_HANDLER and callable(
DEVICE_FILE_HANDLER(key)):
infos[key] = DEVICE_FILE_HANDLER(key)(
os.path.join(dirpath, filename))
else:
infos[key] = read_line(os.path.join(dirpath, filename))
return infos
def fpga_tree():
def gen_fpga_infos(path, vf=True):
name = os.path.basename(path)
dpath = os.path.realpath(os.path.join(path, DEVICE))
bdf = os.path.basename(dpath)
func = "vf" if vf else "pf"
pf_bdf = os.path.basename(
os.path.realpath(os.path.join(dpath, PF))) if vf else ""
fpga = {"path": path, "function": func,
"devices": bdf, "assignable": True,
"parent_devices": pf_bdf,
"name": name}
d_info = fpga_device(dpath)
fpga.update(d_info)
return fpga
devs = []
pure_pfs = all_pure_pf_fpgas()
for pf in all_pf_fpgas():
fpga = gen_fpga_infos(pf, False)
if pf in pure_pfs:
fpga["assignable"] = False
fpga["regions"] = []
vfs = all_vfs_in_pf_fpgas(pf)
for vf in vfs:
vf_fpga = gen_fpga_infos(vf, True)
fpga["regions"].append(vf_fpga)
devs.append(fpga)
return devs

View File

@ -0,0 +1,36 @@
# Copyright 2018 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Utils for FPGA driver.
"""
import glob
import re
VENDORS = ["intel"] # can extend, such as ["intel", "xilinx"]
SYS_FPGA_PATH = "/sys/class/fpga"
VENDORS_PATTERN = re.compile("|".join(["(%s)" % v for v in VENDORS]))
def discover_vendors():
vendors = set()
for p in glob.glob1(SYS_FPGA_PATH, "*"):
m = VENDORS_PATTERN.match(p)
if m:
vendors.add(m.group())
return list(vendors)

View File

@ -0,0 +1,105 @@
# Copyright 2018 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
import os
import subprocess
import fixtures
from cyborg.accelerator.drivers.fpga.base import FPGADriver
from cyborg.accelerator.drivers.fpga.intel import sysinfo
from cyborg.tests import base
from cyborg.tests.unit.accelerator.drivers.fpga.intel import prepare_test_data
class TestFPGADriver(base.TestCase):
def setUp(self):
super(TestFPGADriver, self).setUp()
self.syspath = sysinfo.SYS_FPGA
sysinfo.SYS_FPGA = "/sys/class/fpga"
tmp_sys_dir = self.useFixture(fixtures.TempDir())
prepare_test_data.create_fake_sysfs(tmp_sys_dir.path)
sysinfo.SYS_FPGA = os.path.join(
tmp_sys_dir.path, sysinfo.SYS_FPGA.split("/", 1)[-1])
def tearDown(self):
super(TestFPGADriver, self).tearDown()
sysinfo.SYS_FPGA = self.syspath
def test_create(self):
FPGADriver.create("intel")
self.assertRaises(LookupError, FPGADriver.create, "xilinx")
def test_discover(self):
d = FPGADriver()
self.assertRaises(NotImplementedError, d.discover)
def test_program(self):
d = FPGADriver()
self.assertRaises(NotImplementedError, d.program, "path", "image")
def test_intel_discover(self):
expect = [{'function': 'pf', 'assignable': False, 'pr_num': '1',
'vendor_id': '0x8086', 'devices': '0000:5e:00.0',
'regions': [{
'function': 'vf', 'assignable': True,
'product_id': '0xbcc1',
'name': 'intel-fpga-dev.2',
'parent_devices': '0000:5e:00.0',
'path': '%s/intel-fpga-dev.2' % sysinfo.SYS_FPGA,
'vendor_id': '0x8086',
'devices': '0000:5e:00.1'}],
'name': 'intel-fpga-dev.0',
'parent_devices': '',
'path': '%s/intel-fpga-dev.0' % sysinfo.SYS_FPGA,
'product_id': '0xbcc0'},
{'function': 'pf', 'assignable': True, 'pr_num': '0',
'vendor_id': '0x8086', 'devices': '0000:be:00.0',
'name': 'intel-fpga-dev.1',
'parent_devices': '',
'path': '%s/intel-fpga-dev.1' % sysinfo.SYS_FPGA,
'product_id': '0xbcc0'}]
expect.sort()
intel = FPGADriver.create("intel")
fpgas = intel.discover()
fpgas.sort()
self.assertEqual(2, len(fpgas))
self.assertEqual(fpgas, expect)
@mock.patch.object(subprocess, 'Popen', autospec=True)
def test_intel_program(self, mock_popen):
class p(object):
returncode = 0
def wait(self):
pass
b = "0x5e"
d = "0x00"
f = "0x0"
expect_cmd = ['sudo', 'fpgaconf', '-b', b,
'-d', d, '-f', f, '/path/image']
mock_popen.return_value = p()
intel = FPGADriver.create("intel")
# program VF
intel.program("0000:5e:00.1", "/path/image")
mock_popen.assert_called_with(expect_cmd, stdout=subprocess.PIPE)
# program PF
intel.program("0000:5e:00.0", "/path/image")
mock_popen.assert_called_with(expect_cmd, stdout=subprocess.PIPE)

View File

@ -0,0 +1,93 @@
# Copyright 2018 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import mock
import os
import subprocess
import fixtures
from cyborg.accelerator.drivers.fpga.intel import sysinfo
from cyborg.accelerator.drivers.fpga.intel.driver import IntelFPGADriver
from cyborg.tests import base
from cyborg.tests.unit.accelerator.drivers.fpga.intel import prepare_test_data
class TestIntelFPGADriver(base.TestCase):
def setUp(self):
super(TestIntelFPGADriver, self).setUp()
self.syspath = sysinfo.SYS_FPGA
sysinfo.SYS_FPGA = "/sys/class/fpga"
tmp_sys_dir = self.useFixture(fixtures.TempDir())
prepare_test_data.create_fake_sysfs(tmp_sys_dir.path)
sysinfo.SYS_FPGA = os.path.join(
tmp_sys_dir.path, sysinfo.SYS_FPGA.split("/", 1)[-1])
def tearDown(self):
super(TestIntelFPGADriver, self).tearDown()
sysinfo.SYS_FPGA = self.syspath
def test_discover(self):
expect = [{'function': 'pf', 'assignable': False, 'pr_num': '1',
'vendor_id': '0x8086', 'devices': '0000:5e:00.0',
'regions': [{
'function': 'vf', 'assignable': True,
'product_id': '0xbcc1',
'name': 'intel-fpga-dev.2',
'parent_devices': '0000:5e:00.0',
'path': '%s/intel-fpga-dev.2' % sysinfo.SYS_FPGA,
'vendor_id': '0x8086',
'devices': '0000:5e:00.1'}],
'name': 'intel-fpga-dev.0',
'parent_devices': '',
'path': '%s/intel-fpga-dev.0' % sysinfo.SYS_FPGA,
'product_id': '0xbcc0'},
{'function': 'pf', 'assignable': True, 'pr_num': '0',
'vendor_id': '0x8086', 'devices': '0000:be:00.0',
'parent_devices': '',
'name': 'intel-fpga-dev.1',
'path': '%s/intel-fpga-dev.1' % sysinfo.SYS_FPGA,
'product_id': '0xbcc0'}]
expect.sort()
intel = IntelFPGADriver()
fpgas = intel.discover()
fpgas.sort()
self.assertEqual(2, len(fpgas))
self.assertEqual(fpgas, expect)
@mock.patch.object(subprocess, 'Popen', autospec=True)
def test_intel_program(self, mock_popen):
class p(object):
returncode = 0
def wait(self):
pass
b = "0x5e"
d = "0x00"
f = "0x0"
expect_cmd = ['sudo', 'fpgaconf', '-b', b,
'-d', d, '-f', f, '/path/image']
mock_popen.return_value = p()
intel = IntelFPGADriver()
# program VF
intel.program("0000:5e:00.1", "/path/image")
mock_popen.assert_called_with(expect_cmd, stdout=subprocess.PIPE)
# program PF
intel.program("0000:5e:00.0", "/path/image")
mock_popen.assert_called_with(expect_cmd, stdout=subprocess.PIPE)

View File

@ -0,0 +1,296 @@
#!/usr/bin/python
# Copyright 2018 Intel, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import argparse
import copy
import glob
import os
import shutil
PF0_ADDR = "0000:5e:00.0"
PF1_ADDR = "0000:be:00.0"
VF0_ADDR = "0000:5e:00.1"
FPGA_TREE = {
"dev.0": {"bdf": PF0_ADDR,
"regions": {"dev.2": {"bdf": VF0_ADDR}}},
"dev.1": {"bdf": PF1_ADDR}}
SYS_DEVICES = "sys/devices"
SYS_CLASS_FPGA = "sys/class/fpga"
DEV_PREFIX = "intel-fpga"
PGFA_DEVICE_COMMON_SUB_DIR = ["power"]
PGFA_DEVICE_COMMON_CONTENT = {
"broken_parity_status": "0",
"class": "0x120000",
"config": "",
"consistent_dma_mask_bits": "64",
"d3cold_allowed": "1",
"device": "0xbcc0",
"dma_mask_bits": "64",
"driver_override": "(null)",
"enable": "1",
"irq": "16",
"local_cpulist": "0-111",
"local_cpus": "00000000,00000000,00000000,00000000,00000000,"
"00000000,00000000,00000000,00000000,00000000,"
"0000ffff,ffffffff,ffffffff,ffffffff",
"modalias": "pci:v00008086d0000BCC0sv00000000sd00000000bc12sc00i00",
"msi_bus": "",
"numa_node": "-1",
"resource": [
"0x00000000c6000000 0x00000000c607ffff 0x000000000014220c",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x00000000c6080000 0x00000000c60fffff 0x000000000014220c",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x00000000c6100000 0x00000000c617ffff 0x000000000014220c",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000"],
"resource0": "",
"resource0_wc": "",
"subsystem_device": "0x0000",
"subsystem_vendor": "0x0000",
"uevent": [
"DRIVER=intel-fpga-pci",
"PCI_CLASS=120000",
"PCI_ID=8086:BCC0",
"PCI_SUBSYS_ID=0000:0000",
"PCI_SLOT_NAME=0000:5e:00.0",
"MODALIAS=pci:v00008086d0000BCC0sv00000000sd00000000bc12sc00i00"],
"vendor": "0x8086"}
PGFA_DEVICES_SPECIAL_COMMON_CONTENT = {
"dev.0": {
"resource2": "",
"resource2_wc": "",
"sriov_numvfs": "1",
"sriov_totalvfs": "1",
},
"dev.1": {
"resource": [
"0x00000000fbc00000 0x00000000fbc7ffff 0x000000000014220c",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x00000000fbc80000 0x00000000fbcfffff 0x000000000014220c",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x00000000fbd00000 0x00000000fbd7ffff 0x000000000014220c",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000"],
"resource2": "",
"resource2_wc": "",
"sriov_numvfs": "0",
"sriov_totalvfs": "1",
"uevent": [
"DRIVER=intel-fpga-pci",
"PCI_CLASS=120000",
"PCI_ID=8086:BCC0",
"PCI_SUBSYS_ID=0000:0000",
"PCI_SLOT_NAME=0000:be:00.0",
"MODALIAS=pci:v00008086d0000BCC0sv00000000sd00000000bc12sc00i00"],
},
"dev.2": {
"d3cold_allowed": "0",
"device": "0xbcc1",
"modalias": "pci:v00008086d0000BCC0sv00000000sd00000000bc12sc00i00",
"irq": "0",
"resource": [
"0x00000000c6100000 0x00000000c617ffff 0x000000000014220c",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000",
"0x0000000000000000 0x0000000000000000 0x0000000000000000"],
"uevent": [
"DRIVER=intel-fpga-pci",
"PCI_CLASS=120000",
"PCI_ID=8086:BCC1",
"PCI_SUBSYS_ID=0000:0000",
"PCI_SLOT_NAME=0000:5e:00.1",
"MODALIAS=pci:v00008086d0000BCC1sv00000000sd00000000bc12sc00i00"],
}
}
PGFA_DEVICE_COMMON_SOFT_LINK = {
"driver": "../../../bus/pci/drivers/intel-fpga-pci",
"iommu": "../../virtual/iommu/dmar8",
"iommu_group": "../../../kernel/iommu_groups/75",
"subsystem": "../../../bus/pci"
}
PGFA_DEVICES_SPECIAL_SOFT_LINK = {
"dev.0": {
"firmware_node": "../../LNXSYSTM:00/device:00/PNP0A08:18/device:1d4",
},
"dev.1": {
"firmware_node": "../../LNXSYSTM:00/device:00/PNP0A08:19/device:1d5",
"iommu": "../../virtual/iommu/dmar4",
"iommu_group": "../../../kernel/iommu_groups/76",
},
"dev.2": {
"iommu": "../../virtual/iommu/dmar9",
"iommu_group": "../../../kernel/iommu_groups/81",
}
}
PGFA_DEVICES_SPECIAL_SOFT_LINK = {
"dev.0": {
"firmware_node": "../../LNXSYSTM:00/device:00/PNP0A08:18/device:1d4",
},
"dev.1": {
"firmware_node": "../../LNXSYSTM:00/device:00/PNP0A08:19/device:1d5",
"iommu": "../../virtual/iommu/dmar4",
"iommu_group": "../../../kernel/iommu_groups/76",
},
"dev.2": {
"iommu": "../../virtual/iommu/dmar9",
"iommu_group": "../../../kernel/iommu_groups/81",
}
}
PGFA_DEVICE_PF_SOFT_LINK = {
"virtfn": lambda k, v: (k + str(int(v.rsplit(".", 1)[-1]) - 1),
"/".join(["..", v]))
}
PGFA_DEVICE_VF_SOFT_LINK = {
"physfn": lambda k, v: (k, "/".join(["..", v]))
}
def gen_fpga_content(path, dev):
content = copy.copy(PGFA_DEVICE_COMMON_CONTENT)
content.update(PGFA_DEVICES_SPECIAL_COMMON_CONTENT[dev])
for k, v in content.items():
p = os.path.join(path, k)
if not v:
os.mknod(p)
elif type(v) is str:
with open(p, 'a') as f:
f.write(v + "\n")
elif type(v) is list:
with open(p, 'a') as f:
f.writelines([l + "\n" for l in v])
def gen_fpga_sub_dir(path):
for d in PGFA_DEVICE_COMMON_SUB_DIR:
p = os.path.join(path, d)
os.makedirs(p)
def gen_fpga_pf_soft_link(path, bdf):
for k, v in PGFA_DEVICE_PF_SOFT_LINK.items():
if callable(v):
k, v = v(k, bdf)
os.symlink(v, os.path.join(path, k))
def gen_fpga_common_soft_link(path, bdf):
for k, v in PGFA_DEVICE_COMMON_SOFT_LINK.items():
os.symlink(v, os.path.join(path, k))
def gen_fpga_vf_soft_link(path, bdf):
for k, v in PGFA_DEVICE_VF_SOFT_LINK.items():
if callable(v):
k, v = v(k, bdf)
os.symlink(v, os.path.join(path, k))
def create_devices_path_and_files(tree, device_path, class_fpga_path,
vf=False, pfinfo={}):
for k, v in tree.items():
bdf = v["bdf"]
pci_path = "pci" + bdf.rsplit(":", 1)[0]
bdf_path = os.path.join(device_path, pci_path, bdf)
ln = "-".join([DEV_PREFIX, k])
dev_path = os.path.join(bdf_path, "fpga", ln)
os.makedirs(dev_path)
gen_fpga_content(bdf_path, k)
gen_fpga_sub_dir(bdf_path)
if vf:
gen_fpga_pf_soft_link(pfinfo["path"], bdf)
gen_fpga_vf_soft_link(bdf_path, pfinfo["bdf"])
pfinfo = {"path": bdf_path, "bdf": bdf}
if "regions" in v:
create_devices_path_and_files(
v["regions"], device_path, class_fpga_path, True, pfinfo)
source = dev_path.split("sys")[-1]
os.symlink("../.." + source, os.path.join(class_fpga_path, ln))
os.symlink("../../../" + bdf, os.path.join(dev_path, "device"))
def create_devices_soft_link(class_fpga_path):
devs = glob.glob1(class_fpga_path, "*")
for dev in devs:
path = os.path.realpath("%s/%s/device" % (class_fpga_path, dev))
softlinks = copy.copy(PGFA_DEVICE_COMMON_SOFT_LINK)
softlinks.update(
PGFA_DEVICES_SPECIAL_SOFT_LINK[dev.rsplit("-", 1)[-1]])
for k, v in softlinks.items():
source = os.path.normpath(os.path.join(path, v))
if not os.path.exists(source):
os.makedirs(source)
os.symlink(v, os.path.join(path, k))
def create_fake_sysfs(prefix=""):
sys_device = os.path.join(prefix, SYS_DEVICES)
sys_class_fpga = os.path.join(prefix, SYS_CLASS_FPGA)
basedir = os.path.dirname(sys_device)
if os.path.exists(basedir):
shutil.rmtree(basedir, ignore_errors=False, onerror=None)
os.makedirs(sys_class_fpga)
create_devices_path_and_files(FPGA_TREE, sys_device, sys_class_fpga)
create_devices_soft_link(sys_class_fpga)
def main():
create_fake_sysfs()
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Generate a fake sysfs for intel FPGA.")
group = parser.add_mutually_exclusive_group()
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
parser.add_argument("-p", "--prefix", type=str,
default="/tmp", dest="p",
help='Set the prefix path of the fake sysfs. '
'default "/tmp"')
args = parser.parse_args()
create_fake_sysfs(args.p)