// Copyright(c) 2017-2018, 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.
#include "fpga_hw.h"
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
#include <iostream>
#include <fstream>
#include "mock/test_utils.h"
#include <glob.h>
#include <iomanip>
#include <sstream>
#define FPGA_BBS_VER_MAJOR(i) (((i) >> 56) & 0xf)
#define FPGA_BBS_VER_MINOR(i) (((i) >> 52) & 0xf)
#define FPGA_BBS_VER_PATCH(i) (((i) >> 48) & 0xf)
namespace opae {
namespace testing {
test_device test_device::unknown() {
return test_device{.fme_guid = "C544CE5C-F630-44E1-8551-59BD87AF432E",
.afu_guid = "C544CE5C-F630-44E1-8551-59BD87AF432E",
.segment = 0x1919,
.bus = 0x0A,
.device = 9,
.function = 5,
.num_vfs = 0,
.socket_id = 9,
.num_slots = 9,
.bbs_id = 9,
.bbs_version = {0xFF, 0xFF, 0xFF},
.state = FPGA_ACCELERATOR_ASSIGNED,
.num_mmio = 0,
.num_interrupts = 0xf,
.fme_object_id = 9,
.port_object_id = 9,
.vendor_id = 0x1234,
.device_id = 0x1234,
.fme_num_errors = 0x1234,
.port_num_errors = 0x1234,
.gbs_guid = "C544CE5C-F630-44E1-8551-59BD87AF432E",
.mdata = ""};
}
const char *skx_mdata =
R"mdata({"version": 1,
"afu-image":
{"clock-frequency-high": 312,
"clock-frequency-low": 156,
"power": 50,
"interface-uuid": "1a422218-6dba-448e-b302-425cbcde1406",
"magic-no": 488605312,
"accelerator-clusters":
[
{
"total-contexts": 1,
"name": "nlb_400",
"accelerator-type-uuid": "d8424dc4-a4a3-c413-f89e-433683f9040b"
}
]
},
"platform-name": "MCP"}"
)mdata";
const char *rc_mdata =
R"mdata({"version": 1,
"afu-image":
{"clock-frequency-high": 312,
"clock-frequency-low": 156,
"interface-uuid": "89a05379-528e-5b84-b8f2-348aea5d02c0",
"magic-no": 488605312,
"accelerator-clusters":
[
{
"total-contexts": 1,
"name": "nlb3",
"accelerator-type-uuid": "d8424dc4-a4a3-c413-f89e-433683f9040b"
}
]
},
"platform-name": "PAC"}"
)mdata";
const char *vc_mdata =
R"mdata({"version": 1,
"afu-image":
{"clock-frequency-high": 312,
"clock-frequency-low": 156,
"interface-uuid": "cf9b1c50-37c9-45e9-8030-f921b17d2b3a",
"magic-no": 488605312,
"accelerator-clusters":
[
{
"total-contexts": 1,
"name": "nlb3",
"accelerator-type-uuid": "9aeffe5f-8457-0612-c000-c9660d824272"
}
]
},
"platform-name": "PAC"}";
)mdata";
static platform_db MOCK_PLATFORMS = {
{"skx-p",
test_platform{.mock_sysfs = "mock_sys_tmp-1socket-nlb0.tar.gz",
.driver = fpga_driver::linux_intel,
.devices = {test_device{
.fme_guid = "1A422218-6DBA-448E-B302-425CBCDE1406",
.afu_guid = "D8424DC4-A4A3-C413-F89E-433683F9040B",
.segment = 0x0,
.bus = 0x5e,
.device = 0,
.function = 0,
.num_vfs = 0,
.socket_id = 0,
.num_slots = 1,
.bbs_id = 0x06400002fc614bb9,
.bbs_version = {6, 4, 0},
.state = FPGA_ACCELERATOR_UNASSIGNED,
.num_mmio = 0x2,
.num_interrupts = 0,
.fme_object_id = 0xf500000,
.port_object_id = 0xf400000,
.vendor_id = 0x8086,
.device_id = 0xbcc0,
.fme_num_errors = 8,
.port_num_errors = 3,
.gbs_guid = "58656f6e-4650-4741-b747-425376303031",
.mdata = skx_mdata}}}},
{"skx-p-1vf",
test_platform{.mock_sysfs = "mock_sys_tmp-1socket-nlb0-vf.tar.gz",
.driver = fpga_driver::linux_intel,
.devices = {test_device{
.fme_guid = "1A422218-6DBA-448E-B302-425CBCDE1406",
.afu_guid = "D8424DC4-A4A3-C413-F89E-433683F9040B",
.segment = 0x0,
.bus = 0x5e,
.device = 0,
.function = 0,
.num_vfs = 1,
.socket_id = 0,
.num_slots = 1,
.bbs_id = 0x06400002fc614bb9,
.bbs_version = {6, 4, 0},
.state = FPGA_ACCELERATOR_UNASSIGNED,
.num_mmio = 0x2,
.num_interrupts = 0,
.fme_object_id = 0xf500000,
.port_object_id = 0xf400000,
.vendor_id = 0x8086,
.device_id = 0xbcc0,
.fme_num_errors = 8,
.port_num_errors = 3,
.gbs_guid = "58656f6e-4650-4741-b747-425376303031",
.mdata = skx_mdata}}}},
{"dcp-rc",
test_platform{.mock_sysfs = "mock_sys_tmp-dcp-rc-nlb3.tar.gz",
.driver = fpga_driver::linux_intel,
.devices = {test_device{
.fme_guid = "F64E598B-EA11-517F-A28E-7BC65D885104",
.afu_guid = "D8424DC4-A4A3-C413-F89E-433683F9040B",
.segment = 0x0,
.bus = 0x05,
.device = 0,
.function = 0,
.num_vfs = 0,
.socket_id = 0,
.num_slots = 1,
.bbs_id = 0x0113000200000177,
.bbs_version = {1, 1, 3},
.state = FPGA_ACCELERATOR_UNASSIGNED,
.num_mmio = 0x2,
.num_interrupts = 0,
.fme_object_id = 0xf500000,
.port_object_id = 0xf400000,
.vendor_id = 0x8086,
.device_id = 0x09c4,
.fme_num_errors = 8,
.port_num_errors = 3,
.gbs_guid = "58656f6e-4650-4741-b747-425376303031",
.mdata = rc_mdata}}}},
{"skx-p-dfl0",
test_platform{.mock_sysfs = "mock_sys_tmp-dfl0-nlb0.tar.gz",
.driver = fpga_driver::linux_dfl0,
.devices = {test_device{
.fme_guid = "1A422218-6DBA-448E-B302-425CBCDE1406",
.afu_guid = "D8424DC4-A4A3-C413-F89E-433683F9040B",
.segment = 0x0,
.bus = 0x5e,
.device = 0,
.function = 0,
.num_vfs = 0,
.socket_id = 0,
.num_slots = 1,
.bbs_id = 0x06400002fc614bb9,
.bbs_version = {6, 4, 0},
.state = FPGA_ACCELERATOR_UNASSIGNED,
.num_mmio = 0x2,
.num_interrupts = 0,
.fme_object_id = 0xf500000,
.port_object_id = 0xf400000,
.vendor_id = 0x8086,
.device_id = 0xbcc0,
.fme_num_errors = 8,
.port_num_errors = 3,
.gbs_guid = "58656f6e-4650-4741-b747-425376303031",
.mdata = skx_mdata}}}},
{"skx-p-dfl0_patchset2",
test_platform{.mock_sysfs = "mock_sys_tmp-dfl0_patchset2-nlb0.tar.gz",
.driver = fpga_driver::linux_dfl0,
.devices = {test_device{
.fme_guid = "1A422218-6DBA-448E-B302-425CBCDE1406",
.afu_guid = "D8424DC4-A4A3-C413-F89E-433683F9040B",
.segment = 0x0,
.bus = 0x5e,
.device = 0,
.function = 0,
.num_vfs = 0,
.socket_id = 0,
.num_slots = 1,
.bbs_id = 0x06400002fc614bb9,
.bbs_version = {6, 4, 0},
.state = FPGA_ACCELERATOR_UNASSIGNED,
.num_mmio = 0x2,
.num_interrupts = 0,
.fme_object_id = 0xf500000,
.port_object_id = 0xf400000,
.vendor_id = 0x8086,
.device_id = 0xbcc0,
.fme_num_errors = 8,
.port_num_errors = 3,
.gbs_guid = "58656f6e-4650-4741-b747-425376303031",
.mdata = skx_mdata}}}},
{"dcp-rc-dfl0_patchset2",
test_platform{.mock_sysfs = "mock_sys_tmp-dcp-rc-dfl0_patchset2-nlb0.tar.gz",
.driver = fpga_driver::linux_dfl0,
.devices = {test_device{
.fme_guid = "F64E598B-EA11-517F-A28E-7BC65D885104",
.afu_guid = "D8424DC4-A4A3-C413-F89E-433683F9040B",
.segment = 0x0,
.bus = 0x05,
.device = 0,
.function = 0,
.num_vfs = 0,
.socket_id = 0,
.num_slots = 1,
.bbs_id = 0x0113000200000177,
.bbs_version = {1, 1, 3},
.state = FPGA_ACCELERATOR_UNASSIGNED,
.num_mmio = 0x2,
.num_interrupts = 0,
.fme_object_id = 0xf500000,
.port_object_id = 0xf400000,
.vendor_id = 0x8086,
.device_id = 0x09c4,
.fme_num_errors = 8,
.port_num_errors = 3,
.gbs_guid = "58656f6e-4650-4741-b747-425376303031",
.mdata = rc_mdata}}}},
{"dcp-vc",
test_platform{.mock_sysfs = "mock_sys_tmp-dcp-vc.tar.gz",
.driver = fpga_driver::linux_intel,
.devices = {test_device{
.fme_guid = "CF9B1C50-37C9-45E9-8030-F921B17D2B3A",
.afu_guid = "9AEFFE5F-8457-0612-C000-C9660D824272",
.segment = 0x0,
.bus = 0x05,
.device = 0,
.function = 0,
.num_vfs = 0,
.socket_id = 0,
.num_slots = 1,
.bbs_id = 0x222000200567bd1,
.bbs_version = {2, 2, 2},
.state = FPGA_ACCELERATOR_UNASSIGNED,
.num_mmio = 0x2,
.num_interrupts = 0,
.fme_object_id = 0xf500000,
.port_object_id = 0xf400000,
.vendor_id = 0x8086,
.device_id = 0x0b30,
.fme_num_errors = 9,
.port_num_errors = 3,
.gbs_guid = "58656f6e-4650-4741-b747-425376303031",
.mdata = vc_mdata}}}},
{"dcp-vc-dfl0",
test_platform{.mock_sysfs = "mock_sys_tmp-dcp-vc-dfl0_patchset2-nlb0.tar.gz",
.driver = fpga_driver::linux_dfl0,
.devices = {test_device{
.fme_guid = "CF9B1C50-37C9-45E9-8030-F921B17D2B3A",
.afu_guid = "9AEFFE5F-8457-0612-C000-C9660D824272",
.segment = 0x0,
.bus = 0x05,
.device = 0,
.function = 0,
.num_vfs = 0,
.socket_id = 0,
.num_slots = 1,
.bbs_id = 0x222000200567bd1,
.bbs_version = {2, 2, 2},
.state = FPGA_ACCELERATOR_UNASSIGNED,
.num_mmio = 0x2,
.num_interrupts = 0,
.fme_object_id = 0xf500000,
.port_object_id = 0xf400000,
.vendor_id = 0x8086,
.device_id = 0x0b30,
.fme_num_errors = 9,
.port_num_errors = 3,
.gbs_guid = "58656f6e-4650-4741-b747-425376303031",
.mdata = vc_mdata}}}}
};
test_platform test_platform::get(const std::string &key) {
return fpga_db::instance()->get(key);
}
bool test_platform::exists(const std::string &key) {
return fpga_db::instance()->exists(key);
}
std::vector<std::string> test_platform::keys(bool sorted) {
return fpga_db::instance()->keys(sorted);
}
std::vector<std::string> test_platform::platforms(
std::initializer_list<std::string> names, fpga_driver drv) {
std::vector<std::string> keys(names);
if (keys.empty()) {
keys = fpga_db::instance()->keys();
}
// from the list of platform names requested, remove the ones not found in
// the platform db
keys.erase(
std::remove_if(keys.begin(), keys.end(), [drv](const std::string &n) {
auto db = fpga_db::instance();
return !db->exists(n) || (drv != fpga_driver::linux_any
&& drv != db->get(n).driver)
|| db->get(n).devices.empty();
}), keys.end());
return keys;
}
std::vector<std::string> test_platform::mock_platforms(
std::initializer_list<std::string> names, fpga_driver drv) {
std::vector<std::string> keys(names);
if (keys.empty()) {
keys = fpga_db::instance()->keys();
}
std::vector<std::string> want;
std::copy_if(keys.begin(), keys.end(), std::back_inserter(want),
[drv](const std::string &k) {
auto db = fpga_db::instance();
return db->exists(k) && (drv == fpga_driver::linux_any ||
db->get(k).driver == drv),
db->get(k).mock_sysfs != nullptr;
});
return want;
}
std::vector<std::string> test_platform::hw_platforms(
std::initializer_list<std::string> names, fpga_driver drv) {
std::vector<std::string> keys(names);
if (keys.empty()) {
keys = fpga_db::instance()->keys();
}
std::vector<std::string> want;
std::copy_if(keys.begin(), keys.end(), std::back_inserter(want),
[drv](const std::string &k) {
auto db = fpga_db::instance();
return db->exists(k) && (drv == fpga_driver::linux_any ||
db->get(k).driver == drv) &&
!db->get(k).devices.empty() &&
db->get(k).mock_sysfs == nullptr;
});
return want;
}
const std::string PCI_DEVICES = "/sys/bus/pci/devices";
typedef std::pair<uint16_t, uint64_t> ven_dev_id;
typedef std::tuple<uint16_t, uint64_t, fpga_driver> platform_cfg;
std::map<ven_dev_id, std::vector<std::string>> known_devices = {
{ { 0x8086, 0xbcc0}, std::vector<std::string>() },
{ { 0x8086, 0xbcc1}, std::vector<std::string>() },
{ { 0x8086, 0x09c4}, std::vector<std::string>() },
{ { 0x8086, 0x09c5}, std::vector<std::string>() },
{ { 0x8086, 0x0b30}, std::vector<std::string>() },
{ { 0x8086, 0x0b31}, std::vector<std::string>() },
};
static std::vector<ven_dev_id> supported_devices() {
std::vector<ven_dev_id> devs;
for (auto kv : known_devices) {
devs.push_back(kv.first);
}
return devs;
}
static std::string read_file(const std::string &path) {
std::ifstream df;
struct stat st;
std::string value_string;
if (stat(path.c_str(), &st)) {
std::cerr << std::string("WARNING: stat:") + path << ":" << strerror(errno) << "\n";
return "";
}
df.open(path);
if (!df.is_open()) {
std::cerr << std::string("WARNING: could not open file ") + path << "\n";
return "";
}
df >> value_string;
return value_string;
}
template<typename T>
static T parse_file_int(const std::string &path) {
std::string value_string = read_file(path);
if (value_string.empty())
return 0;
T value;
try {
value = std::stol(value_string, nullptr, 0);
}
catch (std::invalid_argument& e) {
std::cerr << "WARNING: unable to convert integer from file: " << path << std::endl;
return 0;
}
catch (std::out_of_range& e) {
std::cerr << "WARNING: value too large from file: " << path << std::endl;
return 0;
}
return value;
}
static std::string make_path(int seg, int bus, int dev, int func){
std::stringstream num;
num << std::setw(2) << std::hex << bus;
std::string b (num.str());
num.clear();
num.str(std::string());
num << std::setw(4) << std::setfill('0') << seg;
std::string s (num.str());
num.clear();
num.str(std::string());
num << std::setw(2) << std::setfill('0') << dev;
std::string d (num.str());
std::string device_string = s + ":" + b + ":" + d + "." + std::to_string(func);
return device_string;
}
static std::string glob_first_path(const std::string path) {
glob_t glob_buf;
glob_buf.gl_pathc = 0;
glob_buf.gl_pathv = NULL;
int globres = glob(path.c_str(), 0, NULL, &glob_buf);
std::string found_path;
if (!globres){
if (glob_buf.gl_pathc > 1) {
std::cerr << "Ambiguous object key - using first one" << std::endl;
}
found_path = std::string(glob_buf.gl_pathv[0]);
}
else {
switch (globres) {
case GLOB_NOSPACE:
std::cerr << "FPGA No Memory found." << std::endl;
break;
case GLOB_NOMATCH:
std::cerr << "FPGA Not found." << std::endl;
break;
}
}
if (glob_buf.gl_pathc && glob_buf.gl_pathv) {
globfree(&glob_buf);
}
return found_path;
}
static std::string format_uuid(const std::string &uuid) {
std::string formatted_uuid = uuid;
formatted_uuid.insert(8, "-");
formatted_uuid.insert(13, "-");
formatted_uuid.insert(18, "-");
formatted_uuid.insert(23, "-");
std::transform(formatted_uuid.begin(), formatted_uuid.end(), formatted_uuid.begin(), ::toupper);
return formatted_uuid;
}
template<typename T>
static T read_attribute(const std::string &pci_dir, const std::string &attr) {
std::string attr_path = pci_dir + "/" + attr;
struct stat st;
if (stat(attr_path.c_str(), &st)) {
std::cerr << std::string("WARNING: stat:") + attr_path << ":" << strerror(errno) << "\n";
return 0;
}
return parse_file_int<T>(attr_path);
}
static uint16_t read_socket_id(const std::string devices) {
std::string glob_path = PCI_DEVICES + "/" + devices + "/fpga*/*/*fme.*/socket_id";
std::string socket_path = glob_first_path(glob_path);
return parse_file_int<uint16_t>(socket_path);
}
static uint16_t read_device_id(const std::string &pci_dir) {
std::string device_path = pci_dir + "/device";
return parse_file_int<uint16_t>(device_path);
}
static uint16_t read_vendor_id(const std::string &pci_dir) {
std::string vendor_path = pci_dir + "/vendor";
return parse_file_int<uint16_t>(vendor_path);
}
static uint64_t read_bitstream_id(const std::string &pci_dir) {
std::string bitstream_path = pci_dir + "/fpga*/*/*-fme.*/bitstream_id";
bitstream_path = glob_first_path(bitstream_path);
return parse_file_int<uint64_t>(bitstream_path);
}
static std::string read_afu_id(const std::string &pci_dir) {
std::string afu_path = pci_dir + "/fpga*/*/*-port.*/afu_id";
afu_path = glob_first_path(afu_path);
return format_uuid(read_file(afu_path));
}
static std::string read_pr_interface_id(const std::string &pci_dir) {
std::string pr_interface_path = pci_dir + "/fpga/intel-fpga-dev.*/intel-fpga-fme.*/pr/interface_id";
pr_interface_path = glob_first_path(pr_interface_path);
if (pr_interface_path.empty()) {
pr_interface_path = pci_dir + "/fpga_region/region*/dfl-fme.*/dfl-fme-region.*/fpga_region/region*/compat_id";
pr_interface_path = glob_first_path(pr_interface_path);
}
return format_uuid(read_file(pr_interface_path));
}
int filter_fpga(const struct dirent *ent) {
std::string ename(ent->d_name);
if (ename[0] == '.') {
return 0;
}
std::string pci_path = PCI_DEVICES + "/" + ename;
auto did = read_device_id(pci_path);
auto vid = read_vendor_id(pci_path);
auto devices = supported_devices();
std::vector<ven_dev_id>::const_iterator it = std::find(devices.begin(), devices.end(), ven_dev_id(vid, did));
if (it == devices.end()) {
return 0;
}
known_devices[ven_dev_id(vid, did)].push_back(pci_path);
return 1;
}
std::vector<std::string> find_supported_devices() {
struct dirent **dirs;
int n = scandir(PCI_DEVICES.c_str(), &dirs, filter_fpga, alphasort);
if (n == -1) {
std::string msg = "error scanning pci devices: " + std::string(strerror(errno));
throw std::runtime_error(msg);
}
std::vector<std::string> entries;
while (n--) {
entries.push_back(std::string(dirs[n]->d_name));
free(dirs[n]);
}
free(dirs);
return entries;
}
fpga_db *fpga_db::instance_ = nullptr;
fpga_db::fpga_db()
{
}
fpga_db *fpga_db::instance() {
if (fpga_db::instance_ == nullptr) {
fpga_db::instance_ = new fpga_db();
fpga_db::instance_->discover_hw();
}
return fpga_db::instance_;
}
static std::map<platform_cfg, std::string> platform_names = {
{ platform_cfg(0x8086, 0xbcc0, fpga_driver::linux_intel), "skx-p" },
{ platform_cfg(0x8086, 0xbcc1, fpga_driver::linux_intel), "skx-p-v" },
{ platform_cfg(0x8086, 0x09c4, fpga_driver::linux_intel), "dcp-rc" },
{ platform_cfg(0x8086, 0x09c5, fpga_driver::linux_intel), "dcp-rc-v" },
{ platform_cfg(0x8086, 0xbcc0, fpga_driver::linux_dfl0), "skx-p-dfl0_patchset2" },
{ platform_cfg(0x8086, 0x09c4, fpga_driver::linux_dfl0), "dcp-rc-dfl0_patchset2" },
{ platform_cfg(0x8086, 0xbcc0, fpga_driver::linux_dfl0), "skx-p-dfl0" },
{ platform_cfg(0x8086, 0x0b30, fpga_driver::linux_intel), "dcp-vc" },
{ platform_cfg(0x8086, 0x0b31, fpga_driver::linux_intel), "dcp-vc-v" },
{ platform_cfg(0x8086, 0x0b30, fpga_driver::linux_dfl0), "dcp-vc-dfl0" },
};
const char *PCI_DEV_PATTERN = "([0-9a-fA-F]{4}):([0-9a-fA-F]{2}):([0-9]{2})\\.([0-9])";
test_device make_device(uint16_t ven_id, uint16_t dev_id, const std::string &platform, const std::string &pci_path) {
test_device dev = MOCK_PLATFORMS[platform].devices[0];
auto r = regex<>::create(PCI_DEV_PATTERN);
auto m = r->match(pci_path);
if (m) {
dev.segment = std::stoi(m->group(1), nullptr, 16);
dev.bus = std::stoi(m->group(2), nullptr, 16);
dev.device = std::stoi(m->group(3), nullptr, 10);
dev.function = std::stoi(m->group(4), nullptr, 10);
dev.num_vfs = read_attribute<uint8_t>(pci_path, "sriov_numvfs");
dev.vendor_id = ven_id;
dev.device_id = dev_id;
std::string device_string = make_path(dev.segment, dev.bus, dev.device, dev.function);
dev.socket_id = read_socket_id(device_string);
uint64_t bitstream_id = read_bitstream_id(pci_path);
dev.bbs_id = bitstream_id;
dev.bbs_version = {(uint8_t)FPGA_BBS_VER_MAJOR(bitstream_id),
(uint8_t)FPGA_BBS_VER_MINOR(bitstream_id),
(uint16_t)FPGA_BBS_VER_PATCH(bitstream_id)};
strcpy(dev.fme_guid, read_pr_interface_id(pci_path).c_str());
strcpy(dev.afu_guid, read_afu_id(pci_path).c_str());
} else {
std::cerr << "error matching pci dev pattern (" << pci_path << ")\n";
}
return dev;
}
/**
* @brief read the 'driver' symlink to determine if the driver is dfl or intel
*
* @param path a sysfs path representing a device (under
* /sys/bus/pci/<s:b:d:f>)
*
* @return fpga_driver enumerating indicating what kind of driver is bound to
* the device
*/
fpga_driver get_driver(const std::string &path)
{
char buffer[PATH_MAX] = { 0 };
std::string sysfs_drvpath = path + "/driver";
ssize_t lnk_len = readlink(sysfs_drvpath.c_str(), buffer, PATH_MAX);
if (!lnk_len) {
auto msg = std::string("error readling link: ") + sysfs_drvpath;
throw std::runtime_error(msg);
}
std::string bname = basename(buffer);
if (bname == "intel-fpga-pci") {
return fpga_driver::linux_intel;
}
if (bname == "dfl-pci") {
return fpga_driver::linux_dfl0;
}
return fpga_driver::none;
}
std::pair<std::string, test_platform> make_platform(uint16_t ven_id, uint16_t dev_id, const std::vector<std::string> &pci_paths) {
test_platform platform;
// test_platform data structure only supports one driver (for now)
// TODO: assert that all devices represented by pci_patsh are all bound to
// the same driver - for now, just use the first path
platform.driver = get_driver(pci_paths[0]);
// this is discovered hw platform, set mock_sysfs to null
platform.mock_sysfs = nullptr;
std::string name = platform_names[platform_cfg(ven_id, dev_id, platform.driver)];
for (auto p : pci_paths) {
platform.devices.push_back(make_device(ven_id, dev_id, name, p));
}
return std::make_pair(name, platform);
}
void fpga_db::discover_hw() {
platform_db db;
#ifdef OPAE_ENABLE_MOCK
std::cout << "Mock is enabled." << std::endl;
platforms_ = MOCK_PLATFORMS;
#else
auto sys_pci_devs = find_supported_devices();
for (auto kv : known_devices) {
if (!kv.second.empty()) {
ven_dev_id id = kv.first;
platforms_.insert(make_platform(id.first, id.second, kv.second));
}
}
#endif // OPAE_ENABLE_MOCK
}
std::vector<std::string> fpga_db::keys(bool sorted) {
std::vector<std::string> keys(platforms_.size());
std::transform(
platforms_.begin(), platforms_.end(), keys.begin(),
[](const std::pair<std::string, test_platform> &it) { return it.first; });
if (sorted) {
std::sort(keys.begin(), keys.end());
}
return keys;
}
test_platform fpga_db::get(const std::string &key) {
return platforms_[key];
}
bool fpga_db::exists(const std::string &key) {
return platforms_.find(key) != platforms_.end();
}
} // end of namespace testing
} // end of namespace opae