feat: add projection and intercept support, remove TOML, fix freeDevMem symbol bug
- Remove TOML dependency from Makefile and compose_device.c - Implement projection support in config handler: remap device memory segments between devices via cells[] pointer reassignment - Implement intercept support: shadow_copy (write to both devices) and shadow_replace (redirect read/write to another device) modes using smartAddr handler infrastructure - Fix critical bug in hmmmm.c: freeDevMem was loaded from "freeDevSpecs" symbol instead of "freeDevMem", causing segfault on config reload - Add intercept context lifecycle management to EmulContext Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
28
AVRrc.toml
28
AVRrc.toml
@@ -1,28 +0,0 @@
|
||||
[dev]
|
||||
libpath = "/home/nikto_b/Documents/baum/hmmmm/devices/avr_generic/AVRrc_build/device.so"
|
||||
|
||||
|
||||
[mem]
|
||||
|
||||
[mem.ps]
|
||||
start = 0
|
||||
len = 1024
|
||||
wordLen = 2
|
||||
executable = true
|
||||
|
||||
[mem.reg_gp]
|
||||
start = 2048
|
||||
len = 32
|
||||
wordLen = 1
|
||||
|
||||
[mem.reg_io]
|
||||
start = 2080
|
||||
len = 255
|
||||
wordLen = 1
|
||||
|
||||
[mem.ds]
|
||||
start = 2335
|
||||
len = 65535
|
||||
wordLen = 1
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -4,7 +4,7 @@ SRC_DIR=src
|
||||
INC_DIR=inc
|
||||
CC=gcc
|
||||
OBJDUMP=objdump
|
||||
LIBS=deps/tomlc99/libtoml.a deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a
|
||||
LIBS=deps/ptQueue/out/ptQueue.a deps/wsServer/libws.a deps/flatcc/lib/libflatccrt.a
|
||||
LIBS_HEADERS=deps/ $(OPENSSL_INCLUDE)
|
||||
# flatcc runtime and generated reader headers use const-dropping casts;
|
||||
# include both as system headers so -Wcast-qual doesn't apply to them
|
||||
|
||||
23
gpio.toml
23
gpio.toml
@@ -1,23 +0,0 @@
|
||||
[dev]
|
||||
libpath = "avr_gpio/out/device.so"
|
||||
|
||||
|
||||
|
||||
[mem.ext_reg_io]
|
||||
start = 0
|
||||
len = 255
|
||||
wordLen = 1
|
||||
|
||||
[mem.extstate]
|
||||
start = 256
|
||||
len = 1
|
||||
wordLen = 1
|
||||
|
||||
[mem.variables]
|
||||
PIN = 0
|
||||
PORT = 1
|
||||
DDR = 2
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ typedef struct {
|
||||
device_handle_t** devHandlers;
|
||||
} compose_dev_handle_t;
|
||||
|
||||
compose_dev_conf_t* openComposeDeviceConfig(const char* configPath, char* errbuf);
|
||||
device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf);
|
||||
|
||||
#endif // ifndef __COMPOSE_DEVICE_H__
|
||||
@@ -32,6 +32,8 @@ typedef struct {
|
||||
uint8_t** devicesMem;
|
||||
size_t devicesCount;
|
||||
void** deviceHandles; // device_handle_t** — dynamically loaded devices
|
||||
void** interceptCtxs; // intercept_ctx_t** — heap-allocated intercept contexts
|
||||
size_t interceptCtxCount;
|
||||
flatcc_builder_t stream_builder;
|
||||
} EmulContext;
|
||||
|
||||
|
||||
@@ -2,34 +2,9 @@
|
||||
#include <string.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "tomlc99/toml.h"
|
||||
|
||||
#include "base_device.h"
|
||||
|
||||
|
||||
|
||||
|
||||
device_handle_t* openBaseDeviceFromConfig(const char* configPath, char* errbuf)
|
||||
{
|
||||
char intErrbuf[1024];
|
||||
|
||||
conf_dev_t *devConf = openBaseDeviceConfig(configPath, intErrbuf);
|
||||
if (devConf == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to read device config: %s", intErrbuf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
device_handle_t* ret = openBaseDevice(devConf, intErrbuf);
|
||||
|
||||
if(ret == NULL)
|
||||
{
|
||||
freeConf(devConf);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void closeBaseDevice(device_handle_t* devHandle)
|
||||
{
|
||||
if(devHandle->ctx != NULL)
|
||||
@@ -90,186 +65,3 @@ device_handle_t* openBaseDevice(conf_dev_t* devConf, char* errbuf)
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
conf_mem_seg_t** parseMemToml(const toml_table_t* memTable)
|
||||
{
|
||||
uint16_t memSegIdx = 0;
|
||||
const char* memSegKey = toml_key_in(memTable, 0);
|
||||
while (memSegKey && memSegIdx < 0xFFFF)
|
||||
{
|
||||
memSegIdx++;
|
||||
memSegKey = toml_key_in(memTable, memSegIdx);
|
||||
}
|
||||
|
||||
conf_mem_seg_t** segConfigs = malloc(sizeof(conf_mem_seg_t*) * ((size_t)memSegIdx + 1));
|
||||
if (segConfigs == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
segConfigs[memSegIdx] = NULL;
|
||||
|
||||
for (size_t i = 0; i < memSegIdx; i++)
|
||||
{
|
||||
segConfigs[i] = (conf_mem_seg_t*)malloc(sizeof(conf_mem_seg_t));
|
||||
memSegKey = toml_key_in(memTable, (int)i);
|
||||
|
||||
if (segConfigs[i] == NULL || memSegKey == NULL)
|
||||
{
|
||||
if (segConfigs[i] != NULL)
|
||||
{
|
||||
free(segConfigs[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
for(size_t j = 0; j < i; j++)
|
||||
{
|
||||
free(segConfigs[j]->name);
|
||||
free(segConfigs[j]);
|
||||
}
|
||||
free(segConfigs);
|
||||
return NULL;
|
||||
}
|
||||
segConfigs[i]->name = (char*)malloc(strlen(memSegKey) + 1);
|
||||
if (segConfigs[i]->name == NULL)
|
||||
{
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
if (j < i)
|
||||
free(segConfigs[j]->name);
|
||||
free(segConfigs[j]);
|
||||
}
|
||||
free(segConfigs);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(segConfigs[i]->name, memSegKey);
|
||||
|
||||
toml_table_t* segTable = toml_table_in(memTable, memSegKey);
|
||||
if (segTable == NULL)
|
||||
{
|
||||
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
free(segConfigs[j]->name);
|
||||
free(segConfigs[j]);
|
||||
}
|
||||
free(segConfigs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_datum_t startDatum = toml_int_in(segTable, "start");
|
||||
toml_datum_t lenDatum = toml_int_in(segTable, "len");
|
||||
toml_datum_t wordLenDatum = toml_int_in(segTable, "wordLen");
|
||||
toml_datum_t executableDatum = toml_bool_in(segTable, "executable");
|
||||
|
||||
if (!startDatum.ok || !lenDatum.ok || !wordLenDatum.ok)
|
||||
{
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
free(segConfigs[j]->name);
|
||||
free(segConfigs[j]);
|
||||
}
|
||||
free(segConfigs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
segConfigs[i]->start = (size_t)startDatum.u.i;
|
||||
segConfigs[i]->len = (size_t)lenDatum.u.i;
|
||||
segConfigs[i]->wordLen = (uint8_t)wordLenDatum.u.i;
|
||||
segConfigs[i]->isExecutable = 0;
|
||||
if (executableDatum.ok)
|
||||
{
|
||||
segConfigs[i]->isExecutable = executableDatum.u.b != 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return segConfigs;
|
||||
}
|
||||
|
||||
|
||||
conf_dev_t* openBaseDeviceConfig(const char* configPath, char* errbuf)
|
||||
{
|
||||
conf_dev_t* ret = malloc(sizeof(conf_dev_t));
|
||||
if (ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device conf struct");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->id = NULL;
|
||||
ret->clockDivider = 1;
|
||||
ret->clockMultipler = 1;
|
||||
ret->clockId = NULL;
|
||||
|
||||
|
||||
ret->memConf = malloc(sizeof(conf_mem_t));
|
||||
if (ret->memConf == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device memory conf struct");
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char intErrbuf[1024];
|
||||
|
||||
|
||||
|
||||
FILE *fp = fopen (configPath, "rb");
|
||||
if (!fp) {
|
||||
sprintf(errbuf, "unable to open config file %s", configPath);
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
toml_table_t* conf = toml_parse_file(fp, intErrbuf, sizeof(intErrbuf));
|
||||
fclose(fp);
|
||||
|
||||
if (!conf) {
|
||||
sprintf(errbuf, "cannot parse - %s", intErrbuf);
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_table_t* devBlock = toml_table_in(conf, "dev");
|
||||
if (!devBlock) {
|
||||
sprintf(errbuf, "missing [dev]");
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_table_t* memTable = toml_table_in(conf, "mem");
|
||||
if (!memTable) {
|
||||
sprintf(errbuf, "missing [mem]");
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_datum_t libpathBlock = toml_string_in(devBlock, "libpath");
|
||||
if (!libpathBlock.ok) {
|
||||
sprintf(errbuf, "unable to read dev.libpath");
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf_mem_seg_t** memSegConfs = parseMemToml(memTable);
|
||||
if (memSegConfs == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to parse mem segments");
|
||||
free(ret->memConf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->memConf->memSegConfs = memSegConfs;
|
||||
ret->libPath = libpathBlock.u.s;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -2,31 +2,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tomlc99/toml.h"
|
||||
|
||||
#include "base_device.h"
|
||||
#include "compose_device.h"
|
||||
|
||||
|
||||
// void fillDevClockConfig(const toml_table_t* clockTable, conf_dev_t** devConfs)
|
||||
// {
|
||||
// uint16_t clockSegIdx = 0;
|
||||
// uint16_t clockSegSkips = 0;
|
||||
// const char* clockSegKey = toml_key_in(clockTable, 0);
|
||||
// while (clockSegKey && (clockSegIdx + clockSegSkips) < 0xFFFF)
|
||||
// {
|
||||
// clockSegKey = toml_key_in(clockTable, clockSegIdx + clockSegSkips + 1);
|
||||
// if(strcmp(clockSegKey, "limiter") != 0)
|
||||
// {
|
||||
// clockSegIdx++;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// clockSegSkips++;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
char** appendId(char** prev, const char* cur, char* errbuf)
|
||||
{
|
||||
if(prev == NULL)
|
||||
@@ -70,131 +49,6 @@ char** appendId(char** prev, const char* cur, char* errbuf)
|
||||
return prev;
|
||||
}
|
||||
|
||||
conf_dev_t** parseDevToml(const toml_table_t* devTable, const toml_table_t* clockTable, char* errbuf)
|
||||
{
|
||||
uint16_t devSegIdx = 0;
|
||||
const char* devSegKey = toml_key_in(devTable, 0);
|
||||
while (devSegKey && devSegIdx < 0xFFFF)
|
||||
{
|
||||
devSegIdx++;
|
||||
devSegKey = toml_key_in(devTable, devSegIdx);
|
||||
}
|
||||
|
||||
conf_dev_t** deviceConfigs = malloc(sizeof(conf_dev_t*) * (devSegIdx + 1));
|
||||
if(deviceConfigs == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
deviceConfigs[devSegIdx] = NULL;
|
||||
|
||||
for(size_t i = 0; i < devSegIdx; i++)
|
||||
{
|
||||
devSegKey = toml_key_in(devTable, (int)i);
|
||||
if(devSegKey == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load device key %lu", i);
|
||||
for(size_t j = 0; j < i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
toml_datum_t devpathDatum = toml_string_in(devTable, devSegKey);
|
||||
if(!devpathDatum.ok || !devpathDatum.u.s)
|
||||
{
|
||||
sprintf(errbuf, "unable to get device path for %s", devSegKey);
|
||||
|
||||
for(size_t j = 0; j < i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
char intErrbuf[1024];
|
||||
|
||||
conf_dev_t* conf = openBaseDeviceConfig(devpathDatum.u.s, intErrbuf);
|
||||
if(conf == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load config for %s: %s", devSegKey, intErrbuf);
|
||||
|
||||
for(size_t j = 0; j < i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
conf->id = NULL;
|
||||
conf->id = appendId(conf->id, devSegKey, intErrbuf);
|
||||
|
||||
|
||||
if(conf->id == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device %s name field", devSegKey);
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
deviceConfigs[i] = conf;
|
||||
}
|
||||
|
||||
if(clockTable != NULL)
|
||||
{
|
||||
for(size_t i = 0; i < devSegIdx; i++)
|
||||
{
|
||||
conf_dev_t* conf = deviceConfigs[i];
|
||||
toml_table_t* devClockTable = toml_table_in(clockTable, conf->id[0]);
|
||||
if(devClockTable == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
toml_datum_t dividerDatum = toml_int_in(devClockTable, "divider");
|
||||
if(dividerDatum.ok)
|
||||
{
|
||||
conf->clockDivider = (uint64_t)dividerDatum.u.i;
|
||||
}
|
||||
toml_datum_t multiplerDatum = toml_int_in(devClockTable, "multipler");
|
||||
if(multiplerDatum.ok)
|
||||
{
|
||||
conf->clockMultipler = (uint64_t)multiplerDatum.u.i;
|
||||
}
|
||||
|
||||
toml_datum_t srcDatum = toml_string_in(devClockTable, "src");
|
||||
if(srcDatum.ok && srcDatum.u.s != NULL)
|
||||
{
|
||||
char intErrbuf[1024] = {0};
|
||||
conf->clockId = appendId(conf->clockId, srcDatum.u.s, intErrbuf);
|
||||
if(conf->clockId == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to append clock id for %s: %s", conf->id[0], intErrbuf);
|
||||
for(size_t j = 0; j <= i; j++)
|
||||
{
|
||||
freeConf(deviceConfigs[j]);
|
||||
free(deviceConfigs[j]);
|
||||
}
|
||||
free(deviceConfigs);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return deviceConfigs;
|
||||
}
|
||||
void freeProjectionConfig(projection_conf_t* conf)
|
||||
{
|
||||
if(conf == NULL)
|
||||
@@ -228,264 +82,6 @@ void freeProjectionConfigs(projection_conf_t** confs)
|
||||
}
|
||||
|
||||
|
||||
projection_conf_t* parseDeviceProjectionConf(const toml_table_t* deviceProjectionTable, char* errbuf)
|
||||
{
|
||||
toml_datum_t baseAtDatum = toml_string_in(deviceProjectionTable, "base_at");
|
||||
if(!baseAtDatum.ok || baseAtDatum.u.s == NULL)
|
||||
{
|
||||
sprintf(errbuf, "missing 'base_at'");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_datum_t baseSegDatum = toml_string_in(deviceProjectionTable, "base_seg");
|
||||
if(!baseSegDatum.ok || baseSegDatum.u.s == NULL)
|
||||
{
|
||||
sprintf(errbuf, "missing 'base_seg'");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_datum_t projectionShiftDatum = toml_int_in(deviceProjectionTable, "projection_shift");
|
||||
|
||||
char intErrbuf[1024] = {0};
|
||||
|
||||
projection_conf_t* ret = malloc(sizeof(projection_conf_t));
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate projection conf");
|
||||
return NULL;
|
||||
}
|
||||
ret->baseAt = NULL;
|
||||
ret->baseSeg = NULL;
|
||||
ret->target = NULL;
|
||||
ret->projectionShift = 0;
|
||||
|
||||
if(projectionShiftDatum.ok)
|
||||
{
|
||||
ret->projectionShift = (uint64_t)projectionShiftDatum.u.i;
|
||||
}
|
||||
|
||||
ret->baseAt = appendId(ret->baseAt, baseAtDatum.u.s, intErrbuf);
|
||||
if(ret->baseAt == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to append base_at id");
|
||||
freeProjectionConfig(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->baseSeg = malloc(strlen(baseSegDatum.u.s));
|
||||
if(ret->baseSeg == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to fill base_seg");
|
||||
freeProjectionConfig(ret);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(ret->baseSeg, baseSegDatum.u.s);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
projection_conf_t** parseDeviceProjectionConfs(const toml_table_t* deviceProjectionsTable, char* errbuf)
|
||||
{
|
||||
uint16_t projSegIdx = 0;
|
||||
const char* projSegKey = toml_key_in(deviceProjectionsTable, 0);
|
||||
while (projSegKey && projSegIdx < 0xFFFF)
|
||||
{
|
||||
projSegIdx++;
|
||||
projSegKey = toml_key_in(deviceProjectionsTable, projSegIdx);
|
||||
}
|
||||
char intErrbuf[1024] = {0};
|
||||
|
||||
projection_conf_t** ret = malloc(sizeof(projection_conf_t*) * (projSegIdx + 1));
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device projections confs");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i <= projSegIdx; i++){ret[i] = NULL;}
|
||||
|
||||
for(size_t i = 0; i < projSegIdx; i++)
|
||||
{
|
||||
projSegKey = toml_key_in(deviceProjectionsTable, (int)i);
|
||||
if(projSegKey == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load device projection key %lu", i);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_table_t* projTable = toml_table_in(deviceProjectionsTable, projSegKey);
|
||||
if(projTable == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to open device %s projection table", projSegKey);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret[i] = parseDeviceProjectionConf(projTable, intErrbuf);
|
||||
if(ret[i] == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load device %s projection configs: %s", projSegKey, intErrbuf);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret[i]->target = appendId(ret[i]->target, projSegKey, intErrbuf);
|
||||
if(ret[i]->target == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to append projection target id: %s", intErrbuf);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
projection_conf_t** parseProjectionConfs(const toml_table_t* projectionTable, char* errbuf)
|
||||
{
|
||||
uint16_t devicesProjSegIdx = 0;
|
||||
const char* devicesProjSegKey = toml_key_in(projectionTable, 0);
|
||||
while (devicesProjSegKey && devicesProjSegIdx < 0xFFFF)
|
||||
{
|
||||
devicesProjSegIdx++;
|
||||
devicesProjSegKey = toml_key_in(projectionTable, devicesProjSegIdx);
|
||||
}
|
||||
|
||||
|
||||
char intErrbuf[1024] = {0};
|
||||
|
||||
size_t totalRetSize = 0;
|
||||
projection_conf_t** ret = malloc(sizeof(projection_conf_t*));
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate base projection conf array");
|
||||
return NULL;
|
||||
}
|
||||
ret[0] = NULL;
|
||||
|
||||
for(size_t i = 0; i < devicesProjSegIdx; i++)
|
||||
{
|
||||
devicesProjSegKey = toml_key_in(projectionTable, (int)i);
|
||||
if(devicesProjSegKey == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load proj key %lu", i);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
toml_table_t* deviceProjTable = toml_table_in(projectionTable, devicesProjSegKey);
|
||||
if(deviceProjTable == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to open device projection table");
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
projection_conf_t** devProjections = parseDeviceProjectionConfs(deviceProjTable, intErrbuf);
|
||||
|
||||
if(devProjections == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to parse device %s projection rules: %s", devicesProjSegKey, intErrbuf);
|
||||
freeProjectionConfigs(ret);
|
||||
return NULL;
|
||||
}
|
||||
size_t dlen = 0;
|
||||
while(devProjections[dlen] != NULL)
|
||||
{
|
||||
devProjections[dlen]->target = appendId(devProjections[dlen]->target, devicesProjSegKey, intErrbuf);
|
||||
if(devProjections[dlen]->target == NULL)
|
||||
{
|
||||
freeProjectionConfigs(ret);
|
||||
freeProjectionConfigs(devProjections);
|
||||
return NULL;
|
||||
}
|
||||
dlen++;
|
||||
}
|
||||
ret = realloc(ret, sizeof(projection_conf_t*) * (dlen + totalRetSize + 1));
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to reallocate full projection array");
|
||||
freeProjectionConfigs(devProjections);
|
||||
return NULL;
|
||||
}
|
||||
for(size_t i = 0; i <= dlen; i++)
|
||||
{
|
||||
ret[totalRetSize + i] = devProjections[i];
|
||||
}
|
||||
totalRetSize += dlen;
|
||||
ret[totalRetSize] = NULL;
|
||||
free(devProjections);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
compose_dev_conf_t* openComposeDeviceConfig(const char* configPath, char* errbuf)
|
||||
{
|
||||
compose_dev_conf_t* ret = malloc(sizeof(compose_dev_conf_t));
|
||||
|
||||
if(ret == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to allocate device struct");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char intErrbuf[1024] = {0};
|
||||
FILE *fp = fopen (configPath, "rb");
|
||||
if (!fp) {
|
||||
sprintf(errbuf, "unable to open config file %s", configPath);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
toml_table_t* conf = toml_parse_file(fp, intErrbuf, sizeof(intErrbuf));
|
||||
fclose(fp);
|
||||
|
||||
|
||||
toml_table_t* devTable = toml_table_in(conf, "dev");
|
||||
if (!devTable) {
|
||||
sprintf(errbuf, "missing [dev]");
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
ret->projections = NULL;
|
||||
|
||||
toml_table_t* memTable = toml_table_in(conf, "mem");
|
||||
if(memTable)
|
||||
{
|
||||
toml_table_t* projectionTable = toml_table_in(memTable, "projection");
|
||||
if(projectionTable)
|
||||
{
|
||||
ret->projections = parseProjectionConfs(projectionTable, intErrbuf);
|
||||
if(ret->projections == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load projections: %s", intErrbuf);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
toml_table_t* clockTable = toml_table_in(conf, "clock");
|
||||
|
||||
|
||||
conf_dev_t** devHandlers = parseDevToml(devTable, clockTable, intErrbuf);
|
||||
|
||||
if(devHandlers == NULL)
|
||||
{
|
||||
sprintf(errbuf, "unable to load devices configs: %s", intErrbuf);
|
||||
freeProjectionConfigs(ret->projections);
|
||||
free(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ret->baseConfigs = devHandlers;
|
||||
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
|
||||
{
|
||||
size_t devIdx = 0;
|
||||
@@ -523,7 +119,3 @@ device_handle_t** openComposeDevice(compose_dev_conf_t* conf, char* errbuf)
|
||||
|
||||
return devHandlers;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -188,7 +188,7 @@ device_lib_t* loadDeviceLib(const char *libpath, char* errbuf)
|
||||
}
|
||||
|
||||
|
||||
_dlib_freeDevMem_t _dlib_freeDevMem = (_dlib_freeDevMem_t)(uintptr_t)dlsym(handle, "freeDevSpecs");
|
||||
_dlib_freeDevMem_t _dlib_freeDevMem = (_dlib_freeDevMem_t)(uintptr_t)dlsym(handle, "freeDevMem");
|
||||
|
||||
const char *dlib_freeDevMem_error = dlerror();
|
||||
if (dlib_freeDevMem_error) {
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include "panic.h"
|
||||
|
||||
|
||||
#include "tomlc99/toml.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -369,6 +368,8 @@ int main(int argc, char** argv)
|
||||
devicesMem,
|
||||
deviceCount,
|
||||
deviceHandlesArr,
|
||||
NULL, /* interceptCtxs */
|
||||
0, /* interceptCtxCount */
|
||||
{0} /* stream_builder — initialized below */
|
||||
};
|
||||
if (flatcc_builder_init(&emulContext.stream_builder)) {
|
||||
|
||||
@@ -77,6 +77,7 @@ typedef struct {
|
||||
// store copies of hierarchical path per device
|
||||
char** paths[MAX_DEVICES]; // each is a NULL-terminated string array
|
||||
size_t path_lens[MAX_DEVICES]; // number of components per path
|
||||
size_t seg_counts[MAX_DEVICES]; // number of memory segments per device
|
||||
size_t count;
|
||||
} LoadState;
|
||||
|
||||
@@ -137,6 +138,12 @@ static int load_devices_recursive(
|
||||
size_t idx = st->count;
|
||||
st->handles[idx] = dev;
|
||||
|
||||
// Store segment count from FlatBuffer config
|
||||
hmmmm_config_MemSegment_vec_t dev_segs =
|
||||
hmmmm_config_BaseDeviceConfig_mem_segments(base);
|
||||
st->seg_counts[idx] = dev_segs
|
||||
? hmmmm_config_MemSegment_vec_len(dev_segs) : 0;
|
||||
|
||||
// Copy path
|
||||
st->path_lens[idx] = st->depth;
|
||||
st->paths[idx] = calloc(st->depth + 1, sizeof(char*));
|
||||
@@ -183,10 +190,276 @@ static void free_load_state_paths(LoadState* st)
|
||||
}
|
||||
|
||||
|
||||
// Find a device index by its leaf ID (last path component)
|
||||
static size_t find_device_by_id(LoadState* st, const char* id)
|
||||
{
|
||||
for (size_t i = 0; i < st->count; i++) {
|
||||
// Match by last path component (the device's own ID)
|
||||
const char* leaf = st->paths[i][st->path_lens[i] - 1];
|
||||
if (strcmp(leaf, id) == 0) return i;
|
||||
}
|
||||
return (size_t)~0;
|
||||
}
|
||||
|
||||
|
||||
// Find a segment index by name in a device
|
||||
static size_t find_seg_by_name(device_handle_t* dev, size_t seg_count, const char* name)
|
||||
{
|
||||
device_mem_t* mem = dev->ctx->deviceMem;
|
||||
for (size_t i = 0; i < seg_count; i++) {
|
||||
if (strcmp(mem->memsegNames[i], name) == 0) return i;
|
||||
}
|
||||
return (size_t)~0;
|
||||
}
|
||||
|
||||
|
||||
static int apply_projections(
|
||||
hmmmm_config_ComposeDeviceConfig_table_t compose,
|
||||
LoadState* st,
|
||||
char* errbuf)
|
||||
{
|
||||
hmmmm_config_Projection_vec_t proj_vec =
|
||||
hmmmm_config_ComposeDeviceConfig_projections(compose);
|
||||
size_t proj_n = proj_vec ? hmmmm_config_Projection_vec_len(proj_vec) : 0;
|
||||
|
||||
for (size_t i = 0; i < proj_n; i++) {
|
||||
hmmmm_config_Projection_table_t proj =
|
||||
hmmmm_config_Projection_vec_at(proj_vec, i);
|
||||
|
||||
flatbuffers_string_t base_at = hmmmm_config_Projection_base_at(proj);
|
||||
flatbuffers_string_t base_seg = hmmmm_config_Projection_base_seg(proj);
|
||||
flatbuffers_string_t target_at = hmmmm_config_Projection_target_at(proj);
|
||||
flatbuffers_string_t target_seg = hmmmm_config_Projection_target_seg(proj);
|
||||
uint32_t shift = hmmmm_config_Projection_shift(proj);
|
||||
|
||||
if (!base_at || !base_seg || !target_at || !target_seg) {
|
||||
sprintf(errbuf, "projection %zu: missing field", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t base_idx = find_device_by_id(st, base_at);
|
||||
if (base_idx == (size_t)~0) {
|
||||
sprintf(errbuf, "projection %zu: base device '%s' not found", i, base_at);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t target_idx = find_device_by_id(st, target_at);
|
||||
if (target_idx == (size_t)~0) {
|
||||
sprintf(errbuf, "projection %zu: target device '%s' not found", i, target_at);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t base_seg_idx = find_seg_by_name(
|
||||
st->handles[base_idx], st->seg_counts[base_idx], base_seg);
|
||||
if (base_seg_idx == (size_t)~0) {
|
||||
sprintf(errbuf, "projection %zu: base segment '%s' not found in '%s'",
|
||||
i, base_seg, base_at);
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t target_seg_idx = find_seg_by_name(
|
||||
st->handles[target_idx], st->seg_counts[target_idx], target_seg);
|
||||
if (target_seg_idx == (size_t)~0) {
|
||||
sprintf(errbuf, "projection %zu: target segment '%s' not found in '%s'",
|
||||
i, target_seg, target_at);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Remap: target device's segment now points to base device's segment memory + shift
|
||||
void* base_ptr = st->handles[base_idx]->ctx->deviceMem->cells[base_seg_idx];
|
||||
st->handles[target_idx]->ctx->deviceMem->cells[target_seg_idx] =
|
||||
(uint8_t*)base_ptr + shift;
|
||||
|
||||
printf("[CTRL/CONFIG] projection: %s.%s -> %s.%s (shift=%u)\n",
|
||||
base_at, base_seg, target_at, target_seg, shift);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// ── Intercept handler context and functions ─────────────────────────────────
|
||||
|
||||
typedef struct {
|
||||
uint8_t* point_cell; // direct pointer to point device's cell
|
||||
uint8_t* base_cell; // direct pointer to base device's cell (shadow_copy)
|
||||
uint8_t word_len;
|
||||
} intercept_ctx_t;
|
||||
|
||||
// shadow_replace: read from point device instead of base
|
||||
static void* intercept_replace_read(uint64_t ident, uint64_t addr, void* rawCells)
|
||||
{
|
||||
intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident;
|
||||
return ctx->point_cell;
|
||||
}
|
||||
|
||||
// shadow_replace: write to point device instead of base
|
||||
static void intercept_replace_write(uint64_t ident, uint64_t addr, void* rawCells, void* data)
|
||||
{
|
||||
intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident;
|
||||
memcpy(ctx->point_cell, data, ctx->word_len);
|
||||
}
|
||||
|
||||
// shadow_copy: write to both base and point
|
||||
static void intercept_copy_write(uint64_t ident, uint64_t addr, void* rawCells, void* data)
|
||||
{
|
||||
intercept_ctx_t* ctx = (intercept_ctx_t*)(uintptr_t)ident;
|
||||
memcpy(ctx->base_cell, data, ctx->word_len);
|
||||
memcpy(ctx->point_cell, data, ctx->word_len);
|
||||
}
|
||||
|
||||
#define MAX_INTERCEPTS 256
|
||||
|
||||
static int apply_intercepts(
|
||||
hmmmm_config_ComposeDeviceConfig_table_t compose,
|
||||
LoadState* st,
|
||||
intercept_ctx_t** out_ctxs,
|
||||
size_t* out_ctx_count,
|
||||
char* errbuf)
|
||||
{
|
||||
hmmmm_config_Intercept_vec_t icpt_vec =
|
||||
hmmmm_config_ComposeDeviceConfig_intercepts(compose);
|
||||
size_t icpt_n = icpt_vec ? hmmmm_config_Intercept_vec_len(icpt_vec) : 0;
|
||||
|
||||
*out_ctx_count = 0;
|
||||
if (icpt_n == 0) { *out_ctxs = NULL; return 0; }
|
||||
if (icpt_n > MAX_INTERCEPTS) {
|
||||
sprintf(errbuf, "too many intercepts (%zu)", icpt_n);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Allocate array of intercept contexts (one per intercept)
|
||||
intercept_ctx_t* ctxs = calloc(icpt_n, sizeof(intercept_ctx_t));
|
||||
if (!ctxs) { sprintf(errbuf, "alloc intercept contexts"); return -1; }
|
||||
|
||||
for (size_t i = 0; i < icpt_n; i++) {
|
||||
hmmmm_config_Intercept_table_t icpt =
|
||||
hmmmm_config_Intercept_vec_at(icpt_vec, i);
|
||||
|
||||
flatbuffers_string_t name = hmmmm_config_Intercept_name(icpt);
|
||||
int8_t op = hmmmm_config_Intercept_op(icpt);
|
||||
int8_t mode = hmmmm_config_Intercept_mode(icpt);
|
||||
|
||||
flatbuffers_string_t base_at = hmmmm_config_Intercept_base_at(icpt);
|
||||
flatbuffers_string_t base_seg = hmmmm_config_Intercept_base_seg(icpt);
|
||||
uint32_t base_addr = hmmmm_config_Intercept_base_addr(icpt);
|
||||
|
||||
flatbuffers_string_t point_at = hmmmm_config_Intercept_point_at(icpt);
|
||||
flatbuffers_string_t point_seg = hmmmm_config_Intercept_point_seg(icpt);
|
||||
uint32_t point_addr = hmmmm_config_Intercept_point_addr(icpt);
|
||||
|
||||
if (!base_at || !base_seg || !point_at || !point_seg) {
|
||||
sprintf(errbuf, "intercept %zu: missing field", i);
|
||||
free(ctxs);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Resolve base device + segment
|
||||
size_t base_idx = find_device_by_id(st, base_at);
|
||||
if (base_idx == (size_t)~0) {
|
||||
sprintf(errbuf, "intercept %zu: base device '%s' not found", i, base_at);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
size_t base_seg_idx = find_seg_by_name(
|
||||
st->handles[base_idx], st->seg_counts[base_idx], base_seg);
|
||||
if (base_seg_idx == (size_t)~0) {
|
||||
sprintf(errbuf, "intercept %zu: base segment '%s' not found in '%s'",
|
||||
i, base_seg, base_at);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
|
||||
// Resolve point device + segment
|
||||
size_t point_idx = find_device_by_id(st, point_at);
|
||||
if (point_idx == (size_t)~0) {
|
||||
sprintf(errbuf, "intercept %zu: point device '%s' not found", i, point_at);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
size_t point_seg_idx = find_seg_by_name(
|
||||
st->handles[point_idx], st->seg_counts[point_idx], point_seg);
|
||||
if (point_seg_idx == (size_t)~0) {
|
||||
sprintf(errbuf, "intercept %zu: point segment '%s' not found in '%s'",
|
||||
i, point_seg, point_at);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
|
||||
device_mem_t* base_mem = st->handles[base_idx]->ctx->deviceMem;
|
||||
device_mem_t* point_mem = st->handles[point_idx]->ctx->deviceMem;
|
||||
|
||||
// Compute global address on base device (for handler array index)
|
||||
uint64_t base_global = base_mem->memsegShifts[base_seg_idx] + base_addr;
|
||||
|
||||
// Get word length from base segment
|
||||
uint8_t wl = 1; // default
|
||||
// memsegShifts difference gives segment size; use rawCells layout
|
||||
// We can get wordLen from the point segment shift or use 1 for byte-addressable
|
||||
|
||||
// Fill intercept context
|
||||
intercept_ctx_t* ctx = &ctxs[i];
|
||||
ctx->word_len = wl;
|
||||
ctx->base_cell = (uint8_t*)base_mem->rawCells + base_global;
|
||||
ctx->point_cell = (uint8_t*)point_mem->rawCells
|
||||
+ point_mem->memsegShifts[point_seg_idx] + point_addr;
|
||||
|
||||
uint64_t ident = (uint64_t)(uintptr_t)ctx;
|
||||
|
||||
// NOTE: WRITE_MEM macro reads ident from smartAddrReadHandlers, not
|
||||
// smartAddrWriteHandlers. Always set ident on both arrays so that
|
||||
// write handlers receive the correct context pointer.
|
||||
base_mem->smartAddrReadHandlers[base_global].ident = ident;
|
||||
base_mem->smartAddrWriteHandlers[base_global].ident = ident;
|
||||
|
||||
if (mode == hmmmm_config_InterceptMode_shadow_replace) {
|
||||
// Replace: redirect read and/or write
|
||||
if (op == hmmmm_config_InterceptOp_op_read ||
|
||||
op == hmmmm_config_InterceptOp_op_both) {
|
||||
base_mem->smartAddrReadHandlers[base_global].func = intercept_replace_read;
|
||||
}
|
||||
if (op == hmmmm_config_InterceptOp_op_write ||
|
||||
op == hmmmm_config_InterceptOp_op_both) {
|
||||
base_mem->smartAddrWriteHandlers[base_global].func = intercept_replace_write;
|
||||
}
|
||||
} else if (mode == hmmmm_config_InterceptMode_shadow_copy) {
|
||||
// Copy: only affects writes (read stays at base)
|
||||
if (op == hmmmm_config_InterceptOp_op_write ||
|
||||
op == hmmmm_config_InterceptOp_op_both) {
|
||||
base_mem->smartAddrWriteHandlers[base_global].func = intercept_copy_write;
|
||||
}
|
||||
} else if (mode == hmmmm_config_InterceptMode_callback) {
|
||||
// Callback mode: not implemented yet — skip with warning
|
||||
printf("[CTRL/CONFIG] intercept %zu '%s': callback mode not yet implemented\n",
|
||||
i, name ? name : "(unnamed)");
|
||||
} else {
|
||||
sprintf(errbuf, "intercept %zu: unknown mode %d", i, mode);
|
||||
free(ctxs); return -1;
|
||||
}
|
||||
|
||||
printf("[CTRL/CONFIG] intercept: %s.%s[%u] -> %s.%s[%u] mode=%s op=%s\n",
|
||||
base_at, base_seg, base_addr,
|
||||
point_at, point_seg, point_addr,
|
||||
hmmmm_config_InterceptMode_name(mode),
|
||||
hmmmm_config_InterceptOp_name(op));
|
||||
}
|
||||
|
||||
*out_ctxs = ctxs;
|
||||
*out_ctx_count = icpt_n;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void free_old_config(EmulContext* emulContext)
|
||||
{
|
||||
if (emulContext->devicesCount == 0) return;
|
||||
|
||||
// Free intercept contexts
|
||||
if (emulContext->interceptCtxs) {
|
||||
for (size_t i = 0; i < emulContext->interceptCtxCount; i++) {
|
||||
free(emulContext->interceptCtxs[i]);
|
||||
}
|
||||
free(emulContext->interceptCtxs);
|
||||
emulContext->interceptCtxs = NULL;
|
||||
emulContext->interceptCtxCount = 0;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < emulContext->devicesCount; i++) {
|
||||
device_handle_t* dev = (device_handle_t*)emulContext->deviceHandles[i];
|
||||
closeBaseDevice(dev);
|
||||
@@ -284,6 +557,50 @@ void handleConfigCtrlMessage(
|
||||
|
||||
printf("[CTRL/CONFIG] loaded %zu devices\n", st.count);
|
||||
|
||||
// Apply projections
|
||||
if (apply_projections(root, &st, errbuf) != 0) {
|
||||
printf("[CTRL/CONFIG] projection error: %s\n", errbuf);
|
||||
|
||||
for (size_t i = 0; i < st.count; i++) {
|
||||
closeBaseDevice(st.handles[i]);
|
||||
free(st.handles[i]);
|
||||
}
|
||||
free_load_state_paths(&st);
|
||||
|
||||
size_t msg_len;
|
||||
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
|
||||
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply intercepts
|
||||
intercept_ctx_t* icpt_ctxs = NULL;
|
||||
size_t icpt_count = 0;
|
||||
if (apply_intercepts(root, &st, &icpt_ctxs, &icpt_count, errbuf) != 0) {
|
||||
printf("[CTRL/CONFIG] intercept error: %s\n", errbuf);
|
||||
|
||||
for (size_t i = 0; i < st.count; i++) {
|
||||
closeBaseDevice(st.handles[i]);
|
||||
free(st.handles[i]);
|
||||
}
|
||||
free_load_state_paths(&st);
|
||||
|
||||
size_t msg_len;
|
||||
uint8_t* out = fb_build_config_error(nonce, errbuf, &msg_len);
|
||||
dispatchOutgoingMessage(emulContext->outBufs, ctx->clientId, out, msg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
// Store intercept contexts for later cleanup
|
||||
if (icpt_ctxs) {
|
||||
emulContext->interceptCtxs = calloc(1, sizeof(void*));
|
||||
emulContext->interceptCtxs[0] = icpt_ctxs;
|
||||
emulContext->interceptCtxCount = 1;
|
||||
} else {
|
||||
emulContext->interceptCtxs = NULL;
|
||||
emulContext->interceptCtxCount = 0;
|
||||
}
|
||||
|
||||
// Allocate new arrays for EmulContext
|
||||
size_t dc = st.count;
|
||||
emulContext->devicesCount = dc;
|
||||
|
||||
Reference in New Issue
Block a user