2018-08-21 13:24:17 +02:00
|
|
|
/** Linux PCI helpers
|
|
|
|
*
|
2022-12-14 17:39:07 +01:00
|
|
|
* @author Steffen Vogel <post@steffenvogel.de>
|
2022-03-15 09:05:42 -04:00
|
|
|
* @copyright 2014-2022, Institute for Automation of Complex Power Systems, EONERC
|
2022-05-19 17:40:10 +02:00
|
|
|
* @license Apache License 2.0
|
2018-08-21 13:24:17 +02:00
|
|
|
*********************************************************************************/
|
|
|
|
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <libgen.h>
|
2019-06-23 16:26:44 +02:00
|
|
|
#include <cstring>
|
2018-08-21 13:24:17 +02:00
|
|
|
#include <unistd.h>
|
2022-08-30 12:17:39 -04:00
|
|
|
#include <sys/stat.h>
|
2018-08-21 13:24:17 +02:00
|
|
|
#include <linux/limits.h>
|
2022-12-19 15:25:55 +01:00
|
|
|
#include <linux/pci_regs.h>
|
|
|
|
#include <fcntl.h>
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2019-04-23 12:57:51 +02:00
|
|
|
#include <villas/utils.hpp>
|
2019-10-27 20:23:47 +01:00
|
|
|
#include <villas/exceptions.hpp>
|
2021-08-11 12:40:19 -04:00
|
|
|
#include <villas/config.hpp>
|
2020-06-14 21:52:29 +02:00
|
|
|
#include <villas/kernel/pci.hpp>
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
using namespace villas::kernel::pci;
|
2019-05-30 12:43:37 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
DeviceList::DeviceList()
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
struct dirent *e;
|
|
|
|
DIR *dp;
|
|
|
|
FILE *f;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
snprintf(path, sizeof(path), "%s/bus/pci/devices", SYSFS_PATH);
|
|
|
|
|
|
|
|
dp = opendir(path);
|
2020-06-14 21:52:29 +02:00
|
|
|
if (!dp)
|
|
|
|
throw SystemError("Failed to detect PCI devices");
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
while ((e = readdir(dp)))
|
|
|
|
{
|
2022-12-02 17:16:44 +01:00
|
|
|
// Ignore special entries
|
2018-08-21 13:24:17 +02:00
|
|
|
if ((strcmp(e->d_name, ".") == 0) ||
|
2022-12-19 15:25:55 +01:00
|
|
|
(strcmp(e->d_name, "..") == 0))
|
2018-08-21 13:24:17 +02:00
|
|
|
continue;
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
Id id;
|
|
|
|
Slot slot;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
struct
|
|
|
|
{
|
|
|
|
const char *s;
|
|
|
|
unsigned int *p;
|
|
|
|
} map[] = {
|
|
|
|
{"vendor", &id.vendor},
|
|
|
|
{"device", &id.device}};
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2022-12-02 17:16:44 +01:00
|
|
|
// Read vendor & device id
|
2022-12-19 15:25:55 +01:00
|
|
|
for (int i = 0; i < 2; i++)
|
|
|
|
{
|
2018-08-21 13:24:17 +02:00
|
|
|
snprintf(path, sizeof(path), "%s/bus/pci/devices/%s/%s", SYSFS_PATH, e->d_name, map[i].s);
|
|
|
|
|
|
|
|
f = fopen(path, "r");
|
|
|
|
if (!f)
|
2020-06-14 21:52:29 +02:00
|
|
|
throw SystemError("Failed to open '{}'", path);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
ret = fscanf(f, "%x", map[i].p);
|
|
|
|
if (ret != 1)
|
2021-02-16 14:15:38 +01:00
|
|
|
throw RuntimeError("Failed to parse {} ID from: {}", map[i].s, path);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
|
2022-12-02 17:16:44 +01:00
|
|
|
// Get slot id
|
2020-06-14 21:52:29 +02:00
|
|
|
ret = sscanf(e->d_name, "%4x:%2x:%2x.%u", &slot.domain, &slot.bus, &slot.device, &slot.function);
|
2018-08-21 13:24:17 +02:00
|
|
|
if (ret != 4)
|
2021-02-16 14:15:38 +01:00
|
|
|
throw RuntimeError("Failed to parse PCI slot number: {}", e->d_name);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-15 21:05:51 +02:00
|
|
|
emplace_back(std::make_shared<Device>(id, slot));
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dp);
|
2020-06-14 21:52:29 +02:00
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-15 21:05:51 +02:00
|
|
|
DeviceList::value_type
|
2020-06-14 21:52:29 +02:00
|
|
|
DeviceList::lookupDevice(const Slot &s)
|
|
|
|
{
|
2022-12-19 15:25:55 +01:00
|
|
|
return *std::find_if(begin(), end(), [s](const DeviceList::value_type &d)
|
|
|
|
{ return d->slot == s; });
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2020-06-15 21:05:51 +02:00
|
|
|
DeviceList::value_type
|
2020-06-14 21:52:29 +02:00
|
|
|
DeviceList::lookupDevice(const Id &i)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
2022-12-19 15:25:55 +01:00
|
|
|
return *std::find_if(begin(), end(), [i](const DeviceList::value_type &d)
|
|
|
|
{ return d->id == i; });
|
2020-06-15 21:05:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
DeviceList::value_type
|
|
|
|
DeviceList::lookupDevice(const Device &d)
|
|
|
|
{
|
2022-12-19 15:25:55 +01:00
|
|
|
auto dev = std::find_if(begin(), end(), [d](const DeviceList::value_type &e)
|
|
|
|
{ return *e == d; });
|
2022-08-30 12:17:39 -04:00
|
|
|
|
|
|
|
return dev == end() ? value_type() : *dev;
|
2020-06-14 21:52:29 +02:00
|
|
|
}
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
Id::Id(const std::string &str) : vendor(0),
|
|
|
|
device(0),
|
|
|
|
class_code(0)
|
2020-06-14 21:52:29 +02:00
|
|
|
{
|
|
|
|
char *s, *c, *e;
|
|
|
|
char *tmp = strdup(str.c_str());
|
|
|
|
|
|
|
|
if (!*tmp)
|
|
|
|
return;
|
|
|
|
|
|
|
|
s = strchr(tmp, ':');
|
2022-12-19 15:25:55 +01:00
|
|
|
if (!s)
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
free(tmp);
|
2022-12-19 15:25:55 +01:00
|
|
|
throw RuntimeError("Failed to parse PCI id: ':' expected", str);
|
2020-06-14 21:52:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
*s++ = 0;
|
2022-12-19 15:25:55 +01:00
|
|
|
if (tmp[0] && strcmp(tmp, "*"))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
long int x = strtol(tmp, &e, 16);
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if ((e && *e) || (x < 0 || x > 0xffff))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
free(tmp);
|
|
|
|
throw RuntimeError("Failed to parse PCI id: {}: Invalid vendor id", str);
|
|
|
|
}
|
|
|
|
|
|
|
|
vendor = x;
|
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
c = strchr(s, ':');
|
|
|
|
if (c)
|
|
|
|
*c++ = 0;
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if (s[0] && strcmp(s, "*"))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
long int x = strtol(s, &e, 16);
|
2022-12-19 15:25:55 +01:00
|
|
|
if ((e && *e) || (x < 0 || x > 0xffff))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
free(tmp);
|
|
|
|
throw RuntimeError("Failed to parse PCI id: {}: Invalid device id", str);
|
|
|
|
}
|
|
|
|
|
|
|
|
device = x;
|
|
|
|
}
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if (c && c[0] && strcmp(s, "*"))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
long int x = strtol(c, &e, 16);
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if ((e && *e) || (x < 0 || x > 0xffff))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
free(tmp);
|
|
|
|
throw RuntimeError("Failed to parse PCI id: {}: Invalid class code", str);
|
|
|
|
}
|
|
|
|
|
|
|
|
class_code = x;
|
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
bool Id::operator==(const Id &i)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
2022-12-19 15:25:55 +01:00
|
|
|
if ((i.device != 0 && i.device != device) ||
|
|
|
|
(i.vendor != 0 && i.vendor != vendor))
|
2020-06-14 21:52:29 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if ((i.class_code != 0) || (i.class_code != class_code))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
Slot::Slot(const std::string &str) : domain(0),
|
|
|
|
bus(0),
|
|
|
|
device(0),
|
|
|
|
function(0)
|
2020-06-14 21:52:29 +02:00
|
|
|
{
|
|
|
|
char *tmp = strdup(str.c_str());
|
|
|
|
char *colon = strrchr(tmp, ':');
|
|
|
|
char *dot = strchr((colon ? colon + 1 : tmp), '.');
|
|
|
|
char *mid = tmp;
|
|
|
|
char *e, *buss, *colon2;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if (colon)
|
|
|
|
{
|
2018-08-21 13:24:17 +02:00
|
|
|
*colon++ = 0;
|
|
|
|
mid = colon;
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
colon2 = strchr(tmp, ':');
|
2022-12-19 15:25:55 +01:00
|
|
|
if (colon2)
|
|
|
|
{
|
2018-08-21 13:24:17 +02:00
|
|
|
*colon2++ = 0;
|
2020-06-14 21:52:29 +02:00
|
|
|
buss = colon2;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if (tmp[0] && strcmp(tmp, "*"))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
long int x = strtol(tmp, &e, 16);
|
2022-12-19 15:25:55 +01:00
|
|
|
if ((e && *e) || (x < 0 || x > 0x7fffffff))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
free(tmp);
|
|
|
|
throw RuntimeError("Failed to parse PCI slot: {}: invalid domain", str);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
domain = x;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2020-06-14 21:52:29 +02:00
|
|
|
buss = tmp;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if (buss[0] && strcmp(buss, "*"))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
long int x = strtol(buss, &e, 16);
|
2022-12-19 15:25:55 +01:00
|
|
|
if ((e && *e) || (x < 0 || x > 0xff))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
free(tmp);
|
|
|
|
throw RuntimeError("Failed to parse PCI slot: {}: invalid bus", str);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
bus = x;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dot)
|
|
|
|
*dot++ = 0;
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if (mid[0] && strcmp(mid, "*"))
|
|
|
|
{
|
2018-08-21 13:24:17 +02:00
|
|
|
long int x = strtol(mid, &e, 16);
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if ((e && *e) || (x < 0 || x > 0x1f))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
free(tmp);
|
|
|
|
throw RuntimeError("Failed to parse PCI slot: {}: invalid slot", str);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
device = x;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if (dot && dot[0] && strcmp(dot, "*"))
|
|
|
|
{
|
2018-08-21 13:24:17 +02:00
|
|
|
long int x = strtol(dot, &e, 16);
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if ((e && *e) || (x < 0 || x > 7))
|
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
free(tmp);
|
|
|
|
throw RuntimeError("Failed to parse PCI slot: {}: invalid function", str);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
function = x;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
free(tmp);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
bool Slot::operator==(const Slot &s)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
2022-12-19 15:25:55 +01:00
|
|
|
if ((s.domain != 0 && s.domain != domain) ||
|
|
|
|
(s.bus != 0 && s.bus != bus) ||
|
|
|
|
(s.device != 0 && s.device != device) ||
|
|
|
|
(s.function != 0 && s.function != function))
|
2020-06-14 21:52:29 +02:00
|
|
|
return false;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
return true;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
bool Device::operator==(const Device &f)
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
2020-06-14 21:52:29 +02:00
|
|
|
return id == f.id && slot == f.slot;
|
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
std::list<Region>
|
|
|
|
Device::getRegions() const
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
2022-12-19 15:25:55 +01:00
|
|
|
FILE *f;
|
2018-08-21 13:24:17 +02:00
|
|
|
char sysfs[1024];
|
|
|
|
|
|
|
|
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/resource",
|
2022-12-19 15:25:55 +01:00
|
|
|
SYSFS_PATH, slot.domain, slot.bus, slot.device, slot.function);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
f = fopen(sysfs, "r");
|
|
|
|
if (!f)
|
2020-06-14 21:52:29 +02:00
|
|
|
throw SystemError("Failed to open resource mapping {}", sysfs);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
std::list<Region> regions;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
ssize_t bytesRead;
|
2022-12-19 15:25:55 +01:00
|
|
|
char *line = nullptr;
|
2018-08-21 13:24:17 +02:00
|
|
|
size_t len = 0;
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
int reg_num = 0;
|
2019-03-26 06:50:56 +01:00
|
|
|
|
2022-12-02 17:16:44 +01:00
|
|
|
// Cap to 8 regions, just because we don't know how many may exist.
|
2022-12-19 15:25:55 +01:00
|
|
|
while (reg_num < 8 && (bytesRead = getline(&line, &len, f)) != -1)
|
|
|
|
{
|
2018-08-21 13:24:17 +02:00
|
|
|
unsigned long long tokens[3];
|
2022-12-19 15:25:55 +01:00
|
|
|
char *s = line;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
|
|
{
|
|
|
|
char *end;
|
2018-08-21 13:24:17 +02:00
|
|
|
tokens[i] = strtoull(s, &end, 16);
|
2022-12-19 15:25:55 +01:00
|
|
|
if (s == end)
|
|
|
|
{
|
|
|
|
log->debug("Error parsing line {} of {}", reg_num + 1, sysfs);
|
2022-12-02 17:16:44 +01:00
|
|
|
tokens[0] = tokens[1] = 0; // Mark invalid
|
2018-08-21 13:24:17 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
s = end;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(line);
|
|
|
|
|
2022-12-02 17:16:44 +01:00
|
|
|
// Required for getline() to allocate a new buffer on the next iteration.
|
2019-04-08 10:42:45 +02:00
|
|
|
line = nullptr;
|
2018-08-21 13:24:17 +02:00
|
|
|
len = 0;
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
if (tokens[0] != tokens[1])
|
|
|
|
{ // This is a valid region
|
2020-06-14 21:52:29 +02:00
|
|
|
Region region;
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
region.num = reg_num;
|
|
|
|
region.start = tokens[0];
|
2022-12-19 15:25:55 +01:00
|
|
|
region.end = tokens[1];
|
2020-06-14 21:52:29 +02:00
|
|
|
region.flags = tokens[2];
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
regions.push_back(region);
|
|
|
|
}
|
2019-10-27 20:23:47 +01:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
reg_num++;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
fclose(f);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
return regions;
|
|
|
|
}
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
std::string
|
|
|
|
Device::getDriver() const
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
char sysfs[1024], syml[1024];
|
|
|
|
memset(syml, 0, sizeof(syml));
|
|
|
|
|
|
|
|
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/driver", SYSFS_PATH,
|
2022-12-19 15:25:55 +01:00
|
|
|
slot.domain, slot.bus, slot.device, slot.function);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2022-08-30 12:17:39 -04:00
|
|
|
struct stat st;
|
|
|
|
ret = stat(sysfs, &st);
|
|
|
|
if (ret)
|
|
|
|
return "";
|
|
|
|
|
2018-08-21 13:24:17 +02:00
|
|
|
ret = readlink(sysfs, syml, sizeof(syml));
|
|
|
|
if (ret < 0)
|
2020-06-14 21:52:29 +02:00
|
|
|
throw SystemError("Failed to follow link: {}", sysfs);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
return basename(syml);
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
bool Device::attachDriver(const std::string &driver) const
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
char fn[1024];
|
|
|
|
|
2022-12-02 17:16:44 +01:00
|
|
|
// Add new ID to driver
|
2020-06-14 21:52:29 +02:00
|
|
|
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/new_id", SYSFS_PATH, driver.c_str());
|
2018-08-21 13:24:17 +02:00
|
|
|
f = fopen(fn, "w");
|
|
|
|
if (!f)
|
2020-06-14 21:52:29 +02:00
|
|
|
throw SystemError("Failed to add PCI id to {} driver ({})", driver, fn);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
log->info("Adding ID to {} module: {:04x} {:04x}", driver, id.vendor, id.device);
|
2020-06-14 21:52:29 +02:00
|
|
|
fprintf(f, "%04x %04x", id.vendor, id.device);
|
2018-08-21 13:24:17 +02:00
|
|
|
fclose(f);
|
|
|
|
|
2022-12-02 17:16:44 +01:00
|
|
|
// Bind to driver
|
2020-06-14 21:52:29 +02:00
|
|
|
snprintf(fn, sizeof(fn), "%s/bus/pci/drivers/%s/bind", SYSFS_PATH, driver.c_str());
|
2018-08-21 13:24:17 +02:00
|
|
|
f = fopen(fn, "w");
|
|
|
|
if (!f)
|
2020-06-14 21:52:29 +02:00
|
|
|
throw SystemError("Failed to bind PCI device to {} driver ({})", driver, fn);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
log->info("Bind device to {} driver", driver);
|
2020-06-14 21:52:29 +02:00
|
|
|
fprintf(f, "%04x:%02x:%02x.%x\n", slot.domain, slot.bus, slot.device, slot.function);
|
2018-08-21 13:24:17 +02:00
|
|
|
fclose(f);
|
|
|
|
|
2020-06-14 21:52:29 +02:00
|
|
|
return true;
|
2018-08-21 13:24:17 +02:00
|
|
|
}
|
|
|
|
|
2022-12-19 15:25:55 +01:00
|
|
|
|
|
|
|
bool Device::readHostBar(uint32_t &bar) const
|
|
|
|
{
|
|
|
|
unsigned long long start, end, size, flags;
|
2022-12-20 14:29:36 +01:00
|
|
|
char *path = NULL;
|
|
|
|
if (asprintf(&path, "%s/bus/pci/devices/%04x:%02x:%02x.%x/resource", SYSFS_PATH,
|
|
|
|
slot.domain, slot.bus, slot.device, slot.function) == -1)
|
|
|
|
{
|
|
|
|
log->error("could not allocate memory for path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
FILE *file = fopen(path, "r");
|
2022-12-19 15:25:55 +01:00
|
|
|
if (!file)
|
|
|
|
{
|
|
|
|
log->error("error opening resource file");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fscanf(file, "%llx %llx %llx", &start, &end, &flags) != 3)
|
|
|
|
{
|
|
|
|
log->error("error reading resource file");
|
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (end > start)
|
|
|
|
{
|
|
|
|
size = end - start + 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
log->error("error reading bar size");
|
|
|
|
fclose(file);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
log->debug("start: {:#x}, end: {:#x}, size: {:#x}, flags: {:#x}", start, end, size, flags);
|
|
|
|
fclose(file);
|
|
|
|
bar = start;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Device::rewriteBar()
|
|
|
|
{
|
|
|
|
uint32_t hostbar, configbar;
|
|
|
|
if (!readHostBar(hostbar))
|
|
|
|
{
|
|
|
|
log->error("error reading host bar");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!readBar(configbar))
|
|
|
|
{
|
|
|
|
log->error("error reading config bar");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
log->debug("hostbar: {:#x}, configbar: {:#x}", hostbar, configbar);
|
|
|
|
if (hostbar == configbar)
|
|
|
|
{
|
|
|
|
log->debug("bar is already correct");
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
log->debug("bar is incorrect, rewriting");
|
|
|
|
return writeBar(hostbar);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Device::readBar(uint32_t &bar) const
|
|
|
|
{
|
|
|
|
off_t pos = PCI_BASE_ADDRESS_0;
|
|
|
|
uint32_t read_buf;
|
|
|
|
char *path = NULL;
|
|
|
|
if (asprintf(&path, "%s/bus/pci/devices/%04x:%02x:%02x.%x/config", SYSFS_PATH,
|
|
|
|
slot.domain, slot.bus, slot.device, slot.function) == -1)
|
|
|
|
{
|
|
|
|
log->error("could not allocate memory for path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
int fd = open(path, O_RDWR);
|
|
|
|
if (fd < 0)
|
|
|
|
{
|
|
|
|
log->error("could not open config space");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pread(fd, &read_buf, sizeof(read_buf), pos) != sizeof(read_buf))
|
|
|
|
{
|
|
|
|
log->error("error reading from {:#x}", pos);
|
|
|
|
close(fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bar = read_buf;
|
|
|
|
close(fd);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Device::writeBar(uint32_t new_bar)
|
|
|
|
{
|
|
|
|
uint32_t write_buf = new_bar;
|
|
|
|
off_t pos = PCI_BASE_ADDRESS_0;
|
|
|
|
char *path = NULL;
|
|
|
|
if (asprintf(&path, "%s/bus/pci/devices/%04x:%02x:%02x.%x/config", SYSFS_PATH,
|
|
|
|
slot.domain, slot.bus, slot.device, slot.function) == -1)
|
|
|
|
{
|
|
|
|
log->error("could not allocate memory for path");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
int fd = open(path, O_RDWR);
|
|
|
|
if (fd < 0)
|
|
|
|
{
|
|
|
|
log->error("could not open config space");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pwrite(fd, &write_buf, sizeof(write_buf), pos) != sizeof(write_buf))
|
|
|
|
{
|
|
|
|
log->error("error writing to {:#x}", pos);
|
|
|
|
close(fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
close(fd);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Device::getIOMMUGroup() const
|
2018-08-21 13:24:17 +02:00
|
|
|
{
|
|
|
|
int ret;
|
2022-11-08 11:37:29 -05:00
|
|
|
char *group;
|
2022-12-02 17:16:44 +01:00
|
|
|
// readlink() does not add a null terminator!
|
2022-11-08 11:37:29 -05:00
|
|
|
char link[1024] = {0};
|
|
|
|
char sysfs[1024];
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/iommu_group", SYSFS_PATH,
|
2022-12-19 15:25:55 +01:00
|
|
|
slot.domain, slot.bus, slot.device, slot.function);
|
2018-08-21 13:24:17 +02:00
|
|
|
|
|
|
|
ret = readlink(sysfs, link, sizeof(link));
|
|
|
|
if (ret < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
group = basename(link);
|
|
|
|
|
|
|
|
return atoi(group);
|
|
|
|
}
|