Working example

This commit is contained in:
DataHoarder 2021-10-24 21:32:49 +02:00
parent d3cd980e4b
commit 4a0eb4f2b0
19 changed files with 494 additions and 135 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
/cmake-build-debug
/build
.idea

View file

@ -1,7 +1,9 @@
cmake_minimum_required(VERSION 3.13)
project(src)
project(fsm)
set(CMAKE_CXX_STANDARD 14)
add_executable(fsmd src/fsmd.cpp src/device/PCIEDevice.cpp src/device/PCIEDevice.h src/FM10K.cpp src/FM10K.h)
include_directories(${fsm_SOURCE_DIR}/src)
add_executable(fsmd src/fsmd.cpp src/device/PCIEDevice.cpp src/device/PCIEDevice.h src/fm10k/FM10K.cpp src/fm10k/FM10K.h src/fm10k/Functions.h src/fm10k/Functions.cpp)
add_executable(fsm src/fsm.cpp)

14
build.sh Executable file
View file

@ -0,0 +1,14 @@
#!/bin/bash
set -ex
pushd "${0%/*}"
rm -rvf build
mkdir build
pushd build
export CC=clang
export CXX=clang++
export CFLAGS="-ggdb -O0 -march=x86-64 -mtune=generic"
export CXXFLAGS="-ggdb -O0 -march=x86-64 -mtune=generic"
cmake ..
make -j$(nproc)

View file

@ -1,25 +0,0 @@
#pragma once
#include <cstdint>
#include <algorithm>
#include <memory>
#include "device/Device.h"
#include "registers/MGMT.h"
class FM10K {
public:
static const uint64_t BAR4_SIZE = 0x0000000004000000;
static const uint64_t BAR4_OFFSET = 0x0;
explicit FM10K(std::unique_ptr<Device> device) : m_device(std::move(device)) {
}
private:
std::unique_ptr<Device> m_device;
};

View file

@ -5,9 +5,11 @@ public:
virtual ~Device()= default;
virtual bool valid() const = 0;
virtual uint32_t get32(uint64_t address) const = 0;
virtual void set32(uint64_t address, uint32_t value) const = 0;
virtual uint32_t get32(uint32_t address) const = 0;
virtual void set32(uint32_t address, uint32_t value) const = 0;
virtual uint64_t get64(uint64_t address) const = 0;
virtual void set64(uint64_t address, uint64_t value) const = 0;
virtual volatile uint32_t* map32(uint32_t address) const = 0;
virtual uint64_t get64(uint32_t address) const = 0;
virtual void set64(uint32_t address, uint64_t value) const = 0;
};

View file

@ -3,8 +3,48 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <fstream>
#include <sstream>
#include "PCIEDevice.h"
#include "../FM10K.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;
@ -20,73 +60,42 @@ std::vector<PCIEDevice::DeviceEntry> PCIEDevice::DeviceEntry::find() {
struct dirent *entry;
std::string fileName;
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) {
fileName = "/sys/bus/pci/devices/";
fileName += entry->d_name;
fileName += "/vendor";
FILE *vendorBytesPointer = fopen(fileName.c_str(), "rb");
if (vendorBytesPointer != nullptr) {
char *vendorBytesBuffer = nullptr;
size_t vendorBytesLen;
size_t vendorBytesRead = getdelim(&vendorBytesBuffer, &vendorBytesLen, '\0', vendorBytesPointer);
if (vendorBytesRead != -1) {
int vendor = (int) strtol(vendorBytesBuffer, nullptr, 0);
uint16_t vendor = get_file_integer(basePath + "/vendor");
uint16_t _class = get_file_integer(basePath + "/device");
if (vendor == 0x8086) { //Intel Corporation
if (
vendor == 0x8086 //Intel Corporation
&&
(_class == 0x15a4 || _class == 0x15d0 || _class == 0x15d5) //FM10000 devices
) {
if (access((basePath + "/resource4").c_str(), F_OK) == 0) {
fileName = "/sys/bus/pci/devices/";
fileName += entry->d_name;
fileName += "/device";
FILE *classBytesPointer = fopen(fileName.c_str(), "rb");
if (classBytesPointer != nullptr) {
char *classBytesBuffer = nullptr;
size_t classBytesLen;
size_t classBytesRead = getdelim(&classBytesBuffer, &classBytesLen, '\0',
classBytesPointer);
if (classBytesRead != -1) {
int _class = (int) strtol(classBytesBuffer, nullptr, 0);
if (_class == 0x15a4 || _class == 0x15d0 || _class == 0x15d5) { //FM10000 devices
fileName = "/sys/bus/pci/devices/";
fileName += entry->d_name;
fileName += "/resource4";
if (access(fileName.c_str(), F_OK) == 0) {
entries.emplace_back(fileName, vendor, _class);
}
}
}
if (classBytesBuffer != nullptr) {
free(classBytesBuffer);
}
fclose(classBytesPointer);
}
}
auto vpd = get_file_contents(basePath + "/vpd");
entries.emplace_back(basePath + "/resource4", vendor, _class, vpd);
}
if (vendorBytesBuffer != nullptr) {
free(vendorBytesBuffer);
}
fclose(vendorBytesPointer);
}
}
}
closedir(folder);
return entries;
}
PCIEDevice::PCIEDevice(PCIEDevice::DeviceEntry device) : m_device (std::move(device)){
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 = mmap(nullptr, FM10K::BAR4_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, m_bar4Resource, 0);
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);
@ -110,18 +119,109 @@ PCIEDevice::~PCIEDevice() {
}
}
uint32_t PCIEDevice::get32(uint64_t address) const {
return *(((volatile uintptr_t *) m_bar4mmap) + address);
uint32_t PCIEDevice::get32(uint32_t address) const {
return (m_bar4mmap && address < FM10K::BAR4_SIZE) ? *getaddress(address) : 0;
}
void PCIEDevice::set32(uint64_t address, uint32_t value) const {
*(volatile uintptr_t *) (((uintptr_t *) m_bar4mmap) + address) = value;
void PCIEDevice::set32(uint32_t address, uint32_t value) const {
if(m_bar4mmap && address < FM10K::BAR4_SIZE){
*getaddress(address) = value;
}
}
uint64_t PCIEDevice::get64(uint64_t address) const {
return *(((volatile uintptr_t *) m_bar4mmap) + address);
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(uint64_t address, uint64_t value) const {
*(uintptr_t volatile *) (((uintptr_t *) m_bar4mmap) + address) = value;
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;
}

View file

@ -2,24 +2,66 @@
#include <string>
#include <vector>
#include <memory>
#include <unordered_map>
#include "Device.h"
class PCIEDevice: public Device {
public:
class VitalProductData{
public:
class ResourceType {
public:
enum class TypeName : uint8_t{
StringTag = 0x02,
VPD_R = 0x10,
VPD_W = 0x11,
End = 0x0f,
};
explicit ResourceType(const std::vector<uint8_t>& s, size_t& offset);
TypeName getName() const{
return m_name;
}
const auto& getData() const{
return m_data;
}
protected:
TypeName m_name;
std::vector<uint8_t> m_data;
};
explicit VitalProductData(const std::vector<uint8_t>& s);
private:
void fillFromEntry(const std::vector<uint8_t>& v, size_t& offset);
std::unordered_map<std::string, std::vector<uint8_t>> m_vpd;
};
class DeviceEntry{
public:
DeviceEntry(std::string path, uint16_t vendor, uint16_t _class) : m_path(std::move(path)), m_class(_class), m_vendor(vendor){
DeviceEntry(std::string path, uint16_t vendor, uint16_t _class, const std::vector<uint8_t>& vpd) : m_path(std::move(path)), m_class(_class), m_vendor(vendor), m_vpd(vpd){
}
DeviceEntry(const DeviceEntry& a) =default;
static std::vector<DeviceEntry> find();
const std::string& getPath() const{
const auto& getPath() const{
return m_path;
}
const auto& getVPD() const{
return m_vpd;
}
uint16_t getVendor() const{
return m_vendor;
}
@ -31,15 +73,17 @@ public:
std::string m_path;
uint16_t m_vendor;
uint16_t m_class;
VitalProductData m_vpd;
};
explicit PCIEDevice(PCIEDevice::DeviceEntry device);
explicit PCIEDevice(const PCIEDevice::DeviceEntry& device);
uint32_t get32(uint64_t address) const override;
void set32(uint64_t address, uint32_t value) const override;
uint32_t get32(uint32_t address) const override;
volatile uint32_t* map32(uint32_t address) const override;
void set32(uint32_t address, uint32_t value) const override;
uint64_t get64(uint64_t address) const override;
void set64(uint64_t address, uint64_t value) const override;
uint64_t get64(uint32_t address) const override;
void set64(uint32_t address, uint64_t value) const override;
const PCIEDevice::DeviceEntry& getDeviceEntry() const{
return m_device;
@ -49,11 +93,15 @@ public:
return m_bar4mmap != nullptr && m_bar4Resource > 0;
}
~PCIEDevice();
~PCIEDevice() override;
private:
volatile uint32_t* getaddress(uint32_t address) const {
return m_bar4mmap + address;
}
int m_bar4Resource = 0;
void* m_bar4mmap = nullptr;
uint32_t* m_bar4mmap = nullptr;
PCIEDevice::DeviceEntry m_device;
};

12
src/fm10k/Constants.h Normal file
View file

@ -0,0 +1,12 @@
#pragma once
#include "Functions.h"
namespace FM10K {
const uint64_t BAR4_SIZE = 0x0000000004000000;
const uint64_t BAR4_OFFSET = 0x0;
//These should not be set (on hw) to more than 1100 or less than 800
constexpr uint32_t DEFAULT_VDDS_VOLTAGE = 850; //Spec says 0.9 Volts, and also 0.85
constexpr uint32_t DEFAULT_VDDF_VOLTAGE = 950; //Spec says 0.9 Volts, and also 0.95
}

57
src/fm10k/FM10K.h Normal file
View file

@ -0,0 +1,57 @@
#pragma once
#include <cstdint>
#include <cfloat>
#include <algorithm>
#include <memory>
#include "device/Device.h"
#include "registers/MGMT.h"
#include "Constants.h"
#define FM10K_GET_REGISTER(i, t) i.getType<t>(t ## __Address)
#define FM10K_MAP_REGISTER(i, t) i.mapType<t>(t ## __Address)
#define FM10K_SET_REGISTER(i, t, v) i.setType<t>(t ## __Address, v)
#define FM10K_SET_REGISTER_FIELD(i, t, f, v) \
auto __f = FM10K_MAP_REGISTER(i, t); \
__f->fields. f = v
namespace FM10K{
class FM10K {
public:
explicit FM10K(std::unique_ptr<Device> device) : m_device(std::move(device)) {
}
template<typename T> volatile T* mapType(uint32_t address) const {
return reinterpret_cast<volatile T*>(m_device->map32(address));
}
template<typename T> T getType(uint32_t address) const {
T t;
t.value = m_device->get32(address);
return std::move(t);
}
template<typename T> void setType(uint32_t address, const T& t) const {
t.value = m_device->set32(address, t.value);
return std::move(t);
}
template<typename T> void setTypeMask(uint32_t address, uint32_t mask, const T& t) const {
t.value = m_device->set32(address, t.value & mask);
return std::move(t);
}
private:
std::unique_ptr<Device> m_device;
};
}

2
src/fm10k/Functions.cpp Normal file
View file

@ -0,0 +1,2 @@
#include "Functions.h"

20
src/fm10k/Functions.h Normal file
View file

@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
#include <cfloat>
namespace FM10K{
constexpr uint32_t VR12_VID_to_Millivolts(uint8_t v){
return v == 0 ? 0 : 5 * (v - 1) + 250;
}
constexpr uint32_t Volts_to_Millivolts(double v){
return static_cast<uint32_t>(v * 1000.0);
}
constexpr uint8_t Millivolts_to_VR12_VID(uint32_t v){
return ((v - 250) / 5) + 1;
}
}

View file

@ -0,0 +1,50 @@
#pragma once
#include "Register.h"
namespace FM10K{
namespace registers{
namespace MGMT{
enum class DEVICE_CFG_FeatureCode: uint8_t {
FULL = 0b00,
HALF = 0b01,
BASIC = 0b10,
RESERVED = 0b11,
};
typedef PACKED(struct {
uint8_t PCIeMode: 4; //Bitfield array [0..3]
uint8_t Eth100GDisabled: 1;
DEVICE_CFG_FeatureCode FeatureCode: 2;
uint16_t PCIeEnable: 9; //Bitfield array [0..8]
uint8_t SystimeClockSource: 1;
uint16_t : 15;
}, uint32_t) DEVICE_CFG;
const uint32_t DEVICE_CFG__Address = 0x4;
typedef PACKED(struct {
uint16_t PartNumber : 16;
uint16_t : 16;
}, uint32_t) VITAL_PRODUCT_DATA;
const uint32_t VITAL_PRODUCT_DATA__Address = 0x304;
typedef PACKED(struct {
uint16_t ChipVersion : 7;
uint32_t : 25;
}, uint32_t) CHIP_VERSION;
const uint32_t CHIP_VERSION__Address = 0x452;
typedef PACKED(struct {
uint16_t : 11;
uint8_t SKU : 5;
uint8_t VDDS_VRM : 8;
uint8_t VDDF_VRM : 8;
}, uint32_t) FUSE_DATA_0;
const uint32_t FUSE_DATA_0__Address = 0xC0E;
}
}
}

View file

@ -0,0 +1,11 @@
#pragma once
#include "pack.h"
#include "MGMT.h"
namespace FM10K{
namespace registers{
}
}

View file

@ -1,6 +1,17 @@
#include <iostream>
#include <memory>
#include <sstream>
#include <iomanip>
#include "device/PCIEDevice.h"
#include "fm10k/FM10K.h"
#include "fm10k/registers/Register.h"
template <typename T> std::string tohex(T v){
std::stringstream o;
o << std::hex << std::setw(sizeof(v) * 2) << std::setfill('0') << v;
return o.str();
}
int main() {
auto entries = PCIEDevice::DeviceEntry::find();
@ -8,13 +19,24 @@ int main() {
std::cout << "Found " << entries.size() << " FM10K management device(s)." << std::endl;
for(auto& entry : entries){
auto dev = std::make_unique<PCIEDevice>(entry);
std::cout << "dev.path: " << dev->getDeviceEntry().getPath() << std::endl;
std::cout << "dev.vendor: " << dev->getDeviceEntry().getVendor() << std::endl;
std::cout << "dev.class: " << dev->getDeviceEntry().getClass() << std::endl;
std::cout << "DEVICE_CFG: " << dev->get32(0x4) << std::endl;
std::cout << "BSM_ARGS: " << dev->get32(0xC00) << std::endl;
std::cout << "FUSE_DATA(64): " << dev->get64(0xC0E) << std::endl;
auto dev = std::make_unique<PCIEDevice>(entry);
std::cout << "dev.path: " << dev->getDeviceEntry().getPath() << std::endl;
std::cout << "dev.vendor: " << tohex(dev->getDeviceEntry().getVendor()) << std::endl;
std::cout << "dev.class: " << tohex(dev->getDeviceEntry().getClass()) << std::endl;
FM10K::FM10K fm10k(std::move(dev));
auto fuseData_0 = FM10K_MAP_REGISTER(fm10k, FM10K::registers::MGMT::FUSE_DATA_0);
std::cout << "FUSE_DATA_0: " << tohex(fuseData_0->value) << std::endl;
std::cout << "FUSE_DATA_0.SKU: " << std::to_string(fuseData_0->fields.SKU) << std::endl;
std::cout << "FUSE_DATA_0.VDDF_VRM: " << std::to_string(fuseData_0->fields.VDDS_VRM) << std::endl;
std::cout << "FUSE_DATA_0.VDDF_VRM: " << std::to_string(fuseData_0->fields.VDDF_VRM) << std::endl;
auto deviceCfg = FM10K_MAP_REGISTER(fm10k, FM10K::registers::MGMT::DEVICE_CFG);
std::cout << "DEVICE_CFG: " << tohex(deviceCfg->value) << std::endl;
auto vpd = FM10K_MAP_REGISTER(fm10k, FM10K::registers::MGMT::VITAL_PRODUCT_DATA);
std::cout << "VITAL_PRODUCT_DATA.PartNumber: " << tohex(vpd->fields.PartNumber) << std::endl;
}
return 0;
}

46
src/pack.h Normal file
View file

@ -0,0 +1,46 @@
#pragma once
#include <cstdint>
#if defined(__GNUC__) && !defined(__clang__)
#error "GCC is currently not supported due to volatile union packed struct issue. Use clang instead"
#endif
#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 4)) || defined(__clang__) || defined(__TINYC__)
#if defined(__TINYC__)
#define PACKED(x, s) \
_Pragma("pack(1)") \
union { \
s value; \
x __attribute__((packed)) fields; \
} \
_Pragma("pack(1)")
#define PACKEDM(x, s, n) \
_Pragma("pack(1)") \
union { \
s value[n]; \
x __attribute__((packed)) fields; \
} \
_Pragma("pack(1)")
#else
#define PACKED(x, s) \
union { \
s value; \
x __attribute__((packed, aligned(sizeof(s)))) fields; \
} __attribute__((packed, aligned(sizeof(s))))
#define PACKEDM(x, s, n) \
union { \
s value[n]; \
x __attribute__((packed, aligned(sizeof(s)))) fields; \
} __attribute__((packed, aligned(sizeof(s))))
#endif
#else
#error "Packed bit-fields of type char were not properly bit-packed on many targets prior to GCC 4.4, upgrade to GCC >= 4.4."
#endif

View file

@ -1,8 +0,0 @@
#pragma once
#include "pack.h"
namespace MGMT{
}

View file

@ -1,23 +0,0 @@
#pragma once
#include "stdint.h"
#if __GNUC__ > 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ >= 4)) || __clang__ || __TINYC__
#if defined(__TINYC__)
#pragma pack(1)
#endif
#define PACKED(x, s) union { \
s value; \
x __attribute__((packed)) fields; \
}
#define PACKEDM(x, s, n) union { \
s value[n]; \
x __attribute__((packed)) fields; \
}
#else
#error "Packed bit-fields of type char were not properly bit-packed on many targets prior to GCC 4.4, upgrade to GCC >= 4.4."
#endif

28
test/test-gcc-struct.cpp Normal file
View file

@ -0,0 +1,28 @@
#include <cstdint>
#include <cstdio>
#define PACKED(x, s) \
union { \
s value; \
x __attribute__((packed, aligned(sizeof(s)))) fields; \
} __attribute__((packed, aligned(sizeof(s))))
typedef PACKED(struct {
uint16_t PartNumber : 16;
uint16_t _reserved: 16;
}, uint32_t) VITAL_PRODUCT_DATA;
static volatile uint32_t v = 0x0000ae21;
static volatile uint32_t* vpd = &v;
template<typename T> volatile T* mapType() {
return reinterpret_cast<volatile T*>(vpd);
}
int main(){
auto tt = mapType<VITAL_PRODUCT_DATA>();
uint16_t t = tt->fields.PartNumber;
printf("%d\n", tt->value);
printf("%d\n", t);
}