1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/node/ synced 2025-03-16 00:00:02 +01:00
VILLASnode/common/lib/kernel/pci.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

395 lines
8.2 KiB
C++
Raw Normal View History

/** Linux PCI helpers
*
2022-03-15 09:13:39 -04:00
* @author Steffen Vogel <svogel2@eonerc.rwth-aachen.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
*********************************************************************************/
#include <dirent.h>
#include <libgen.h>
#include <cstring>
#include <unistd.h>
2022-08-30 12:17:39 -04:00
#include <sys/stat.h>
#include <linux/limits.h>
2019-10-27 20:23:47 +01:00
#include <villas/log.hpp>
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>
2020-06-14 21:52:29 +02:00
using namespace villas::kernel::pci;
2020-06-14 21:52:29 +02:00
DeviceList::DeviceList()
{
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");
while ((e = readdir(dp))) {
// Ignore special entries
if ((strcmp(e->d_name, ".") == 0) ||
(strcmp(e->d_name, "..") == 0) )
continue;
2020-06-14 21:52:29 +02:00
Id id;
Slot slot;
struct { const char *s; unsigned int *p; } map[] = {
2020-06-14 21:52:29 +02:00
{ "vendor", &id.vendor },
{ "device", &id.device }
};
// Read vendor & device id
for (int i = 0; i < 2; i++) {
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);
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);
fclose(f);
}
// 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);
if (ret != 4)
2021-02-16 14:15:38 +01:00
throw RuntimeError("Failed to parse PCI slot number: {}", e->d_name);
2020-06-15 21:05:51 +02:00
emplace_back(std::make_shared<Device>(id, slot));
}
closedir(dp);
2020-06-14 21:52:29 +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)
{
2020-06-15 21:05:51 +02:00
return *std::find_if(begin(), end(), [s](const DeviceList::value_type &d) {
return d->slot == s;
});
}
2020-06-15 21:05:51 +02:00
DeviceList::value_type
2020-06-14 21:52:29 +02:00
DeviceList::lookupDevice(const Id &i)
{
2020-06-15 21:05:51 +02:00
return *std::find_if(begin(), end(), [i](const DeviceList::value_type &d) {
return d->id == i;
});
}
DeviceList::value_type
DeviceList::lookupDevice(const Device &d)
{
2022-08-30 12:17:39 -04:00
auto dev = std::find_if(begin(), end(), [d](const DeviceList::value_type &e) {
2020-06-15 21:05:51 +02:00
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
}
Id::Id(const std::string &str) :
vendor(0),
device(0),
class_code(0)
{
char *s, *c, *e;
char *tmp = strdup(str.c_str());
if (!*tmp)
return;
s = strchr(tmp, ':');
if (!s) {
free(tmp);
throw RuntimeError("Failed to parse PCI id: ':' expected", str);
}
*s++ = 0;
if (tmp[0] && strcmp(tmp, "*")) {
long int x = strtol(tmp, &e, 16);
if ((e && *e) || (x < 0 || x > 0xffff)) {
free(tmp);
throw RuntimeError("Failed to parse PCI id: {}: Invalid vendor id", str);
}
vendor = x;
}
2020-06-14 21:52:29 +02:00
c = strchr(s, ':');
if (c)
*c++ = 0;
if (s[0] && strcmp(s, "*")) {
long int x = strtol(s, &e, 16);
if ((e && *e) || (x < 0 || x > 0xffff)) {
free(tmp);
throw RuntimeError("Failed to parse PCI id: {}: Invalid device id", str);
}
device = x;
}
if (c && c[0] && strcmp(s, "*")) {
long int x = strtol(c, &e, 16);
if ((e && *e) || (x < 0 || x > 0xffff)) {
free(tmp);
throw RuntimeError("Failed to parse PCI id: {}: Invalid class code", str);
}
class_code = x;
}
}
2020-06-14 21:52:29 +02:00
bool
Id::operator==(const Id &i)
{
2020-06-14 21:52:29 +02:00
if ((i.device != 0 && i.device != device) ||
(i.vendor != 0 && i.vendor != vendor))
return false;
if ((i.class_code != 0) || (i.class_code != class_code))
return false;
return true;
}
Slot::Slot(const std::string &str) :
domain(0),
bus(0),
device(0),
function(0)
{
char *tmp = strdup(str.c_str());
char *colon = strrchr(tmp, ':');
char *dot = strchr((colon ? colon + 1 : tmp), '.');
char *mid = tmp;
char *e, *buss, *colon2;
if (colon) {
*colon++ = 0;
mid = colon;
2020-06-14 21:52:29 +02:00
colon2 = strchr(tmp, ':');
if (colon2) {
*colon2++ = 0;
2020-06-14 21:52:29 +02:00
buss = colon2;
2020-06-14 21:52:29 +02:00
if (tmp[0] && strcmp(tmp, "*")) {
long int x = strtol(tmp, &e, 16);
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);
}
2020-06-14 21:52:29 +02:00
domain = x;
}
}
else
2020-06-14 21:52:29 +02:00
buss = tmp;
2020-06-14 21:52:29 +02:00
if (buss[0] && strcmp(buss, "*")) {
long int x = strtol(buss, &e, 16);
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);
}
2020-06-14 21:52:29 +02:00
bus = x;
}
}
if (dot)
*dot++ = 0;
if (mid[0] && strcmp(mid, "*")) {
long int x = strtol(mid, &e, 16);
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);
}
2020-06-14 21:52:29 +02:00
device = x;
}
if (dot && dot[0] && strcmp(dot, "*")) {
long int x = strtol(dot, &e, 16);
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);
}
2020-06-14 21:52:29 +02:00
function = x;
}
2020-06-14 21:52:29 +02:00
free(tmp);
}
2020-06-14 21:52:29 +02:00
bool
Slot::operator==(const Slot &s)
{
2020-06-14 21:52:29 +02: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))
return false;
2020-06-14 21:52:29 +02:00
return true;
}
2020-06-14 21:52:29 +02:00
bool
Device::operator==(const Device &f)
{
2020-06-14 21:52:29 +02:00
return id == f.id && slot == f.slot;
}
2020-06-14 21:52:29 +02:00
std::list<Region>
Device::getRegions() const
{
FILE* f;
char sysfs[1024];
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/resource",
2020-06-14 21:52:29 +02:00
SYSFS_PATH, slot.domain, slot.bus, slot.device, slot.function);
f = fopen(sysfs, "r");
if (!f)
2020-06-14 21:52:29 +02:00
throw SystemError("Failed to open resource mapping {}", sysfs);
2020-06-14 21:52:29 +02:00
std::list<Region> regions;
ssize_t bytesRead;
2019-04-08 10:42:45 +02:00
char* line = nullptr;
size_t len = 0;
2020-06-14 21:52:29 +02:00
int reg_num = 0;
// Cap to 8 regions, just because we don't know how many may exist.
2020-06-14 21:52:29 +02:00
while (reg_num < 8 && (bytesRead = getline(&line, &len, f)) != -1) {
unsigned long long tokens[3];
char* s = line;
2019-04-07 15:12:32 +02:00
for (int i = 0; i < 3; i++) {
char* end;
tokens[i] = strtoull(s, &end, 16);
2020-06-11 18:33:34 +02:00
if (s == end) {
2020-06-14 21:52:29 +02:00
printf("Error parsing line %d of %s\n", reg_num + 1, sysfs);
tokens[0] = tokens[1] = 0; // Mark invalid
break;
}
s = end;
}
free(line);
// Required for getline() to allocate a new buffer on the next iteration.
2019-04-08 10:42:45 +02:00
line = nullptr;
len = 0;
if (tokens[0] != tokens[1]) { // This is a valid region
2020-06-14 21:52:29 +02:00
Region region;
2020-06-14 21:52:29 +02:00
region.num = reg_num;
region.start = tokens[0];
region.end = tokens[1];
region.flags = tokens[2];
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++;
}
2020-06-14 21:52:29 +02:00
fclose(f);
2020-06-14 21:52:29 +02:00
return regions;
}
2020-06-14 21:52:29 +02:00
std::string
Device::getDriver() const
{
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,
2020-06-14 21:52:29 +02:00
slot.domain, slot.bus, slot.device, slot.function);
2022-08-30 12:17:39 -04:00
struct stat st;
ret = stat(sysfs, &st);
if (ret)
return "";
ret = readlink(sysfs, syml, sizeof(syml));
if (ret < 0)
2020-06-14 21:52:29 +02:00
throw SystemError("Failed to follow link: {}", sysfs);
2020-06-14 21:52:29 +02:00
return basename(syml);
}
2020-06-14 21:52:29 +02:00
bool
Device::attachDriver(const std::string &driver) const
{
FILE *f;
char fn[1024];
// 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());
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);
2021-02-16 14:15:38 +01:00
auto logger = logging.get("kernel:pci");
logger->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);
fclose(f);
// 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());
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);
2021-02-16 14:15:38 +01:00
logger->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);
fclose(f);
2020-06-14 21:52:29 +02:00
return true;
}
2020-06-14 21:52:29 +02:00
int
Device::getIOMMUGroup() const
{
int ret;
2022-11-08 11:37:29 -05:00
char *group;
// readlink() does not add a null terminator!
2022-11-08 11:37:29 -05:00
char link[1024] = {0};
char sysfs[1024];
snprintf(sysfs, sizeof(sysfs), "%s/bus/pci/devices/%04x:%02x:%02x.%x/iommu_group", SYSFS_PATH,
2020-06-14 21:52:29 +02:00
slot.domain, slot.bus, slot.device, slot.function);
ret = readlink(sysfs, link, sizeof(link));
if (ret < 0)
return -1;
group = basename(link);
return atoi(group);
}