228 lines
6.2 KiB
C++
228 lines
6.2 KiB
C++
#include <cstring>
|
|
#include <dirent.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include "PCIEDevice.h"
|
|
#include "fm10k/FM10K.h"
|
|
|
|
std::vector<uint8_t> get_file_contents(const std::string& path){
|
|
std::ifstream file;
|
|
file.open(path, std::ios::in | std::ios::binary);
|
|
if(file.fail()){
|
|
file.close();
|
|
return {};
|
|
}
|
|
|
|
std::vector<uint8_t> data;
|
|
|
|
std::for_each(std::istreambuf_iterator<char>(file),
|
|
std::istreambuf_iterator<char>(),
|
|
[&data](const char c){
|
|
data.push_back(c);
|
|
});
|
|
|
|
file.close();
|
|
return std::move(data);
|
|
}
|
|
|
|
uint64_t get_file_integer(const std::string& path){
|
|
auto s = get_file_contents(path);
|
|
std::string t;
|
|
t.reserve(s.empty() ? 0 : s.size() - 1);
|
|
|
|
for(auto& c : s){
|
|
if(c == '\n'){
|
|
break;
|
|
}
|
|
t += c;
|
|
}
|
|
|
|
if(t.empty()){
|
|
return -1;
|
|
}
|
|
return std::strtoul(t.c_str(), nullptr, 0);
|
|
}
|
|
|
|
std::vector<PCIEDevice::DeviceEntry> PCIEDevice::DeviceEntry::find() {
|
|
std::vector<PCIEDevice::DeviceEntry> entries;
|
|
|
|
//TODO: BSD?
|
|
|
|
DIR *folder;
|
|
|
|
folder = opendir("/sys/bus/pci/devices");
|
|
if (folder == nullptr) {
|
|
return entries;
|
|
}
|
|
|
|
struct dirent *entry;
|
|
|
|
while ((entry = readdir(folder))) {
|
|
std::string basePath("/sys/bus/pci/devices/");
|
|
basePath += entry->d_name;
|
|
|
|
if (entry->d_type == DT_DIR || entry->d_type == DT_LNK) {
|
|
|
|
uint16_t vendor = get_file_integer(basePath + "/vendor");
|
|
uint16_t _class = get_file_integer(basePath + "/device");
|
|
|
|
if (
|
|
vendor == 0x8086 //Intel Corporation
|
|
&&
|
|
(_class == 0x15a4 || _class == 0x15d0 || _class == 0x15d5) //FM10000 devices
|
|
) {
|
|
if (access((basePath + "/resource4").c_str(), F_OK) == 0) {
|
|
|
|
auto vpd = get_file_contents(basePath + "/vpd");
|
|
entries.emplace_back(basePath + "/resource4", vendor, _class, vpd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
closedir(folder);
|
|
|
|
return entries;
|
|
}
|
|
|
|
PCIEDevice::PCIEDevice(const PCIEDevice::DeviceEntry& device) : m_device (device){
|
|
m_bar4Resource = open(m_device.getPath().c_str(), O_RDWR);
|
|
if(m_bar4Resource <= 0){
|
|
//TODO printf("Unable to open BAR4 resource %s\n", m_device.getPath().c_str());
|
|
return;
|
|
}
|
|
|
|
m_bar4mmap = static_cast<uint32_t *>(mmap(nullptr, FM10K::BAR4_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, m_bar4Resource, 0));
|
|
if(m_bar4mmap == MAP_FAILED){
|
|
//TODO printf("Unable to map BAR4 resource %s\n", m_device.getPath().c_str());
|
|
close(m_bar4Resource);
|
|
m_bar4Resource = 0;
|
|
m_bar4mmap = nullptr;
|
|
return;
|
|
}
|
|
}
|
|
|
|
PCIEDevice::~PCIEDevice() {
|
|
if(m_bar4mmap != nullptr){
|
|
if(munmap(m_bar4mmap, FM10K::BAR4_SIZE) != 0){
|
|
//Error
|
|
}
|
|
m_bar4mmap = nullptr;
|
|
}
|
|
|
|
if(m_bar4Resource > 0){
|
|
close(m_bar4Resource);
|
|
m_bar4Resource = 0;
|
|
}
|
|
}
|
|
|
|
uint32_t PCIEDevice::get32(uint32_t address) const {
|
|
return (m_bar4mmap && address < FM10K::BAR4_SIZE) ? *getaddress(address) : 0;
|
|
}
|
|
|
|
void PCIEDevice::set32(uint32_t address, uint32_t value) const {
|
|
if(m_bar4mmap && address < FM10K::BAR4_SIZE){
|
|
*getaddress(address) = value;
|
|
}
|
|
}
|
|
|
|
uint64_t PCIEDevice::get64(uint32_t address) const {
|
|
return (m_bar4mmap && (address + 1) < FM10K::BAR4_SIZE) ? *reinterpret_cast<volatile uint64_t*>(getaddress(address)) : 0;
|
|
}
|
|
|
|
void PCIEDevice::set64(uint32_t address, uint64_t value) const {
|
|
if(m_bar4mmap && (address + 1) < FM10K::BAR4_SIZE){
|
|
*reinterpret_cast<volatile uint64_t*>(getaddress(address)) = value;
|
|
}
|
|
}
|
|
|
|
volatile uint32_t* PCIEDevice::map32(uint32_t address) const {
|
|
return (m_bar4mmap && address < FM10K::BAR4_SIZE) ? getaddress(address) : nullptr;
|
|
}
|
|
|
|
PCIEDevice::VitalProductData::VitalProductData(const std::vector<uint8_t>& s) {
|
|
size_t offset = 0;
|
|
|
|
while(offset < s.size()){
|
|
ResourceType t(s, offset);
|
|
|
|
|
|
switch (t.getName()) {
|
|
|
|
case ResourceType::TypeName::StringTag:
|
|
if(m_vpd.empty()){
|
|
//Product Name
|
|
m_vpd.emplace("Product Name", t.getData());
|
|
}
|
|
break;
|
|
case ResourceType::TypeName::VPD_R:
|
|
{
|
|
size_t o = 0;
|
|
while (o < t.getData().size()){
|
|
fillFromEntry(t.getData(), o);
|
|
}
|
|
}
|
|
break;
|
|
case ResourceType::TypeName::VPD_W:
|
|
{
|
|
size_t o = 0;
|
|
while (o < t.getData().size()){
|
|
fillFromEntry(t.getData(), o);
|
|
}
|
|
}
|
|
break;
|
|
case ResourceType::TypeName::End:
|
|
return;
|
|
}
|
|
};
|
|
}
|
|
|
|
void PCIEDevice::VitalProductData::fillFromEntry(const std::vector<uint8_t> &v, size_t& offset) {
|
|
std::string keyword;
|
|
keyword.reserve(2);
|
|
|
|
keyword += static_cast<char>(v[offset++]);
|
|
keyword += static_cast<char>(v[offset++]);
|
|
uint8_t length = v[offset++];
|
|
|
|
std::vector<uint8_t> data;
|
|
std::copy(v.begin() + offset, v.begin() + offset + length, std::back_inserter(data));
|
|
offset += length;
|
|
|
|
m_vpd.emplace(std::move(keyword), std::move(data));
|
|
}
|
|
|
|
PCIEDevice::VitalProductData::ResourceType::ResourceType(const std::vector<uint8_t>& s, size_t& offset) {
|
|
uint8_t h = s[offset++];
|
|
|
|
uint16_t length = 0;
|
|
|
|
if(((h>>7)&1) == 0){ //Small Data Type
|
|
PACKED(struct {
|
|
uint8_t length : 3;
|
|
uint8_t name : 4;
|
|
uint8_t type : 1;
|
|
}, uint8_t) header{};
|
|
header.value = h;
|
|
|
|
m_name = static_cast<TypeName>(header.fields.name);
|
|
length = header.fields.length;
|
|
}else{ //Large Data Type
|
|
PACKED(struct {
|
|
uint8_t name : 7;
|
|
uint8_t type : 1;
|
|
}, uint8_t) header{};
|
|
header.value = h;
|
|
|
|
m_name = static_cast<TypeName>(header.fields.name);
|
|
length = s[offset] | s[offset + 1] << 8;
|
|
offset += 2;
|
|
}
|
|
|
|
std::copy(s.begin() + offset, s.begin() + offset + length, std::back_inserter(m_data));
|
|
offset += length;
|
|
}
|